close Warning: Failed to sync with repository "ogBrowser-Git": (1366, "Incorrect string value: '\\xF0\\x9F\\x93\\xA6 I...' for column 'message' at row 1"); repository information may be out of date. Look in the Trac log for more information including mitigation strategies.

source: ogAgent-Git/src/opengnsys/modules/server/ogAdmClient/__init__.py

main 8.4.2
Last change on this file was 9e750ce, checked in by Natalia Serrano <natalia.serrano@…>, 7 days ago

refs #2862 parches ugr

  • Property mode set to 100644
File size: 46.5 KB
Line 
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2014 Virtual Cable S.L.
5# Copyright (c) 2024-2025 Qindel Formación y Servicios S.L.
6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without modification,
9# are permitted provided that the following conditions are met:
10#
11#    * Redistributions of source code must retain the above copyright notice,
12#      this list of conditions and the following disclaimer.
13#    * Redistributions in binary form must reproduce the above copyright notice,
14#      this list of conditions and the following disclaimer in the documentation
15#      and/or other materials provided with the distribution.
16#    * Neither the name of Virtual Cable S.L. nor the names of its contributors
17#      may be used to endorse or promote products derived from this software
18#      without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30"""
31@author: Ramón M. Gómez, ramongomez at us dot es
32@author: Natalia Serrano, nserrano at qindel dot com
33"""
34
35import base64
36import os
37import signal
38import string
39import random
40import subprocess
41from pathlib import Path
42from urllib.parse import unquote
43
44from opengnsys import VERSION
45from opengnsys.log import logger
46from opengnsys.workers import ogLiveWorker
47
48# Check authorization header decorator
49def check_secret (fnc):
50    """
51    Decorator to check for received secret key and raise exception if it isn't valid.
52    """
53    def wrapper (*args, **kwargs):
54        try:
55            this, path, get_params, post_params, server = args
56
57            if not server:      ## this happens on startup, eg. onActivation->autoexecCliente->ejecutaArchivo->popup->check_secret
58                return fnc (*args, **kwargs)
59
60            if this.random == server.headers['Authorization']:
61                return fnc (*args, **kwargs)
62            else:
63                raise Exception ('Unauthorized operation')
64        except Exception as e:
65            logger.error (str (e))
66            raise Exception (e)
67
68    return wrapper
69
70# Check if operation is permitted
71def execution_level(level):
72    def check_permitted(fnc):
73        def wrapper(*args, **kwargs):
74            levels = ['status', 'halt', 'full']
75            this = args[0]
76            try:
77                if levels.index(level) <= levels.index(this.exec_level):
78                    return fnc(*args, **kwargs)
79                else:
80                    raise Exception('Unauthorized operation')
81            except Exception as e:
82                logger.debug (str(e))
83                raise Exception(e)
84
85        return wrapper
86
87    return check_permitted
88
89class ogAdmClientWorker (ogLiveWorker):
90    name          = 'ogAdmClient'  # Module name
91    REST          = None  # REST object
92
93    def InventariandoSoftware (self, dsk, par, nfn):
94        sft_src = f'/tmp/CSft-{self.IPlocal}-{par}'
95        try:
96            self.interfaceAdmin (nfn, [dsk, par, sft_src])
97            herror = 0
98        except:
99            herror = 1
100
101        if herror:
102            logger.warning ('Error al ejecutar el comando')
103            b64 = ''
104            self.muestraMensaje (20)
105        else:
106            if not os.path.exists (sft_src):
107                raise Exception (f'interfaceAdmin({nfn}) returned success but did not create file ({sft_src})')
108            sft_src_contents = Path (sft_src).read_bytes()
109
110            b64 = base64.b64encode (sft_src_contents).decode ('utf-8')
111            self.muestraMensaje (19)
112
113        cmd = {
114            'nfn': 'RESPUESTA_InventarioSoftware',
115            'dsk': dsk,         ## not in the original C code, around ogAdmClient.c:1944
116            'par': par,
117            'contents': b64,
118        }
119        return self.respuestaEjecucionComando (cmd, herror, 0)
120
121    def ejecutaArchivo (self,fn):
122        logger.debug ('fn ({})'.format (fn))
123
124        ## in the "file" there's not just some bash, but a sequence of parameters such as "nfn=Function\rparam1=foo\rparam2=bar"
125        buffer = subprocess.run (['cat', fn], capture_output=True).stdout.strip().decode ('utf-8')
126        logger.debug ('buffer ({})'.format (buffer.replace ('\r', '\\r')))    ## change \r so as not to mess with the log
127        if buffer:
128            for l in buffer.split ('@'):
129                if not len (l): continue
130                logger.debug ('line ({})'.format (l.replace ('\r', '\\r')))    ## change \r so as not to mess with the log
131                ## at this point, an option would be fire up a curl to localhost, but we can also parse the params and locally call the desired function:
132                post_params = {}
133                for param in l.split ("\r"):
134                    k, v = param.split ('=')
135                    post_params[k] = v
136                logger.debug ('post_params "{}"'.format (post_params))
137
138                func_name = post_params.pop ('nfn', None)
139                if func_name is None:
140                    logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
141                    break
142                func = getattr (self, 'process_' + func_name)
143                ## func is already a ref to self.func, so we don't have to call self.func(...) or func(self, ...)
144
145                logger.debug ('calling function "{}" with post_params "{}"'.format (func_name, post_params))
146                output = func ([], {}, post_params, None)
147                logger.debug ('output "{}"'.format (output))
148                if not output:
149                    logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
150                    break
151
152    def inclusionCliente (self):
153        cfg = self.LeeConfiguracion()
154        if not cfg:
155            logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
156            logger.warning ('Ha ocurrido algún problema en el proceso de inclusión del cliente')
157            logger.error ('LeeConfiguracion() failed')
158            return False
159        res = self.enviaMensajeServidor ('InclusionCliente', { 'cfg': self.cfg2obj (cfg), 'secret': self.random, 'agent_version': VERSION })
160        logger.debug ('res ({})'.format (res))
161
162        ## RESPUESTA_InclusionCliente
163        if (type (res) is not dict or 0 == res['res']) :
164            logger.error ('Ha ocurrido algún problema en el proceso de inclusión del cliente')
165            return False
166
167        if (not res['ido'] or not res['npc']):
168            logger.error ('Se han recibido parámetros con valores no válidos')
169            return False
170
171        self.idordenador     = res['ido']   ## Identificador del ordenador
172        self.nombreordenador = res['npc']   ## Nombre del ordenador
173        self.cache           = res['che']   ## Tamaño de la caché reservada al cliente
174        self.idproautoexec   = res['exe']   ## Procedimento de inicio (Autoexec)
175        self.idcentro        = res['idc']   ## Identificador de la Unidad Organizativa
176        self.idaula          = res['ida']   ## Identificador del aula
177
178        return True
179
180    def cuestionCache (self):
181        return True         ## ogAdmClient.c:425
182
183    def autoexecCliente (self):
184        res = self.enviaMensajeServidor ('AutoexecCliente', { 'exe': self.idproautoexec })
185        logger.debug ('res ({})'.format (res))
186
187        if (type (res) is not dict):
188            logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
189            logger.error ('Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración')
190            return False
191
192        ## RESPUESTA_AutoexecCliente
193        if (type (res) is not dict or 0 == res['res']) :
194            logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
195            return False
196
197        logger.info (res)
198        res = self.enviaMensajeServidor ('enviaArchivo', { 'nfl': res['nfl'] })
199        if (type (res) is not dict):
200            logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
201            logger.error ('Ha ocurrido algún problema al recibir un archivo por la red')
202            return False
203        logger.debug (f'res ({res})')
204
205        fileautoexec = '/tmp/_autoexec_{}'.format (self.IPlocal)
206        logger.debug ('fileautoexec ({})'.format (fileautoexec))
207        with open (fileautoexec, 'w') as fd:
208            fd.write (base64.b64decode (res['contents']).decode ('utf-8'))
209
210        self.ejecutaArchivo (fileautoexec)
211
212        return True
213
214    def comandosPendientes (self):
215        while (True):
216            res = self.enviaMensajeServidor ('ComandosPendientes')   ## receives just one command
217            if (type (res) is not dict):
218                logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
219                logger.error ('Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración')
220                return False
221
222            logger.info (res)
223            if ('NoComandosPtes' == res['nfn']):
224                break
225
226            ## TODO manage the rest of cases... we might have to do something similar to ejecutaArchivo
227            #if (!gestionaTrama (ptrTrama)){   // Análisis de la trama
228            #    logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
229            #    return False
230            #}
231            ## ATM let's just return false to avoid a possible infinite loop
232            return False
233
234        return True
235
236    def procesaComandos (self):
237        res = self.enviaMensajeServidor ('DisponibilidadComandos', { 'tpc': 'OPG' })     ## Activar disponibilidad
238        logger.debug ('res ({})'.format (res))
239
240        if (type (res) is not dict):
241            logger.error ('Ha ocurrido algún problema al enviar una petición de comandos interactivos al Servidor de Administración')
242            return False
243
244        logger.info ('Disponibilidad de comandos activada')     ## Disponibilidad de cliente activada
245
246        ## we now return true and the outer agent code gets to wait for requests from outside
247        ## TODO thing is, ogAdmClient always calls comandosPendientes() after every received request. How do we do that here?
248        #
249        #ptrTrama=recibeMensaje (&socket_c);
250        #if (!ptrTrama){
251        #    errorLog (modulo,46,FALSE);     'Ha ocurrido algún problema al recibir un comando interactivo desde el Servidor de Administración'
252        #    return;
253        #}
254        #close (socket_c);
255        #if (!gestionaTrama (ptrTrama)){   // Análisis de la trama
256        #    errorLog (modulo,39,FALSE);     'Ha ocurrido algún problema al procesar la trama recibida'
257        #    return;
258        #}
259        #if (!comandosPendientes (ptrTrama)){
260        #    errorLog (modulo,42,FALSE);     'Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración'
261        #}
262
263    def onActivation (self):
264        super().onActivation()
265        self.exec_level = 'full'
266        self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(32))
267
268        try:
269            logger.info ('Inicio de sesion')
270            logger.info ('Abriendo sesión en el servidor de Administración')
271            if (not self.inclusionCliente()):
272                raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
273
274            logger.info ('Cliente iniciado')
275            logger.info ('Procesando caché')
276            if not self.cuestionCache():
277                raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
278
279            if self.idproautoexec > 0:
280                logger.info ('Ejecución de archivo Autoexec')
281                if not self.autoexecCliente():
282                    raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
283
284            logger.info ('Procesa comandos pendientes')
285            if not self.comandosPendientes():
286                raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
287
288            logger.info ('Acciones pendientes procesadas')
289        except:
290            self.muestraError()
291            logger.warning ('onActivation failed')
292            return
293
294        self.muestraMenu()
295        self.procesaComandos()
296
297        logger.info ('onActivation ok')
298
299    def onDeactivation (self):
300        """
301        Sends OGAgent stopping notification to OpenGnsys server
302        """
303        logger.debug ('onDeactivation')
304        self.REST.sendMessage ('ogagent/stopped', {'mac': self.mac, 'ip': self.IPlocal, 'idcentro': self.idcentro, 'idaula': self.idaula,
305                                                   'idordenador': self.idordenador, 'nombreordenador': self.nombreordenador})
306
307
308
309
310
311
312
313
314
315    def do_CrearImagen (self, post_params):
316        for k in ['dsk', 'par', 'cpt', 'idi', 'nci', 'ipr', 'nfn', 'ids']:
317            if k not in post_params:
318                logger.error (f'required parameter ({k}) not in POST params')
319                return {}
320
321        dsk = post_params['dsk'] ## Disco
322        par = post_params['par'] ## Número de partición
323        cpt = post_params['cpt'] ## Código de la partición
324        idi = post_params['idi'] ## Identificador de la imagen
325        nci = post_params['nci'] ## Nombre canónico de la imagen
326        ipr = post_params['ipr'] ## Ip del repositorio
327        nfn = post_params['nfn']
328        ids = post_params['ids']
329
330        self.muestraMensaje (7)
331
332        try:
333            res = self.InventariandoSoftware (dsk, par, 'InventarioSoftware')   ## Crea inventario Software previamente
334        except:
335            logger.warning ('Error al ejecutar el comando')
336            return {}
337
338        if res['contents']:
339            self.muestraMensaje (2)
340            inv_sft = res['contents']
341            try:
342                self.interfaceAdmin (nfn, [dsk, par, nci, ipr])
343                self.muestraMensaje (9)
344                herror = 0
345            except:
346                logger.warning ('Error al ejecutar el comando')
347                self.muestraMensaje (10)
348                herror = 1
349        else:
350            logger.warning ('Error al ejecutar el comando')
351            herror = 1
352            inv_sft = ''
353
354        self.muestraMenu()
355
356        cmd = {
357            'nfn':     'RESPUESTA_CrearImagen',
358            'idi':     idi,    ## Identificador de la imagen
359            'dsk':     dsk,    ## Número de disco
360            'par':     par,    ## Número de partición de donde se creó
361            'cpt':     cpt,    ## Tipo o código de partición
362            'ipr':     ipr,    ## Ip del repositorio donde se alojó
363            'inv_sft': inv_sft,
364        }
365        return self.respuestaEjecucionComando (cmd, herror, ids)
366
367    def do_CrearImagenGit (self, post_params):
368        for k in ['nfn', 'ids', 'disk', 'partition', 'repository', 'image_name']:
369            if k not in post_params:
370                logger.error (f'required parameter ({k}) not in POST params')
371                return {}
372
373        nfn = post_params['nfn']
374        ids = post_params['ids']
375
376        disk       = post_params['disk']        ## Numero de disco
377        partition  = post_params['partition']   ## Numero de partición
378        repo       = post_params['repository']  ## URL a repositorio
379        image_name = post_params['image_name']  ## Nombre de imagen
380
381        msg        = post_params['message']     ## Mensaje de commit (opcional)
382
383
384        self.muestraMensaje (7)
385
386        try:
387            res = self.InventariandoSoftware (disk, partition, 'InventarioSoftware')   ## Crea inventario Software previamente
388        except:
389            logger.warning ('Error al ejecutar el comando')
390            return {}
391
392        if res['contents']:
393            self.muestraMensaje (2)
394            inv_sft = res['contents']
395            try:
396                create_args = ["--disk", disk, "--partition", partition, "--repository", repo, "--image-name", image_name]
397                if msg:
398                    create_args = create_args + ['--message', msg]
399
400                self.interfaceAdmin (nfn, create_args)
401                self.muestraMensaje (9)
402                herror = 0
403            except:
404                logger.warning ('Error al ejecutar el comando')
405                self.muestraMensaje (10)
406                herror = 1
407        else:
408            logger.warning ('Error al ejecutar el comando')
409            herror = 1
410            inv_sft = ''
411
412        self.muestraMenu()
413
414        cmd = {
415            'nfn':     'RESPUESTA_CrearImagenGit',
416            'disk'     : disk,         ## Número de disco
417            'partition': partition,    ## Número de partición de donde se creó
418            'repo'     : repo,         ## Ip del repositorio donde se alojó
419            'inv_sft'  : inv_sft
420        }
421        return self.respuestaEjecucionComando (cmd, herror, ids)
422
423
424    def do_ModificarImagenGit (self, post_params):
425        for k in ['nfn', 'ids', 'disk', 'partition', 'repository', 'branch', 'options', 'message']:
426            if k not in post_params:
427                logger.error (f'required parameter ({k}) not in POST params')
428                return {}
429
430        nfn = post_params['nfn']
431        ids = post_params['ids']
432
433        disk       = post_params['disk']         ## Disco
434        partition  = post_params['partition']    ## Número de partición
435        repo       = post_params['repository']   ## Ip del repositorio
436
437        branch     = post_params['branch']       ## Rama nueva a crear
438        options    = post_params['options']      ## Opciones (force push, etc)
439        msg        = post_params['message']      ## Commit message
440
441        self.muestraMensaje (7)
442
443        try:
444            res = self.InventariandoSoftware (disk, partition, 'InventarioSoftware')   ## Crea inventario Software previamente
445        except:
446            logger.warning ('Error al ejecutar el comando')
447            return {}
448
449        if res['contents']:
450            self.muestraMensaje (2)
451            inv_sft = res['contents']
452            try:
453                mod_args = ["--disk", disk, "--partition", partition, "--repository", repo, "--branch", branch, "--message", msg]
454                if options:
455                    mod_args = mod_args + ['--options', options]
456
457                self.interfaceAdmin (nfn, mod_args)
458                self.muestraMensaje (9)
459                herror = 0
460            except:
461                logger.warning ('Error al ejecutar el comando')
462                self.muestraMensaje (10)
463                herror = 1
464        else:
465            logger.warning ('Error al ejecutar el comando')
466            herror = 1
467            inv_sft = ''
468
469        self.muestraMenu()
470
471        cmd = {
472            'nfn':     'RESPUESTA_ModificarImagenGit',
473            'disk'      : disk,         ## Número de disco
474            'partition' : partition,    ## Número de partición de donde se creó
475            'repo'      : repo,         ## Ip del repositorio donde se alojó
476            'branch'    : branch,       ## Rama creada
477            'inv_sft'   : inv_sft
478        }
479        return self.respuestaEjecucionComando (cmd, herror, ids)
480
481
482    def do_RestaurarImagen (self, post_params):
483        for k in ['dsk', 'par', 'idi', 'ipr', 'nci', 'ifs', 'ptc', 'nfn', 'ids']:
484            if k not in post_params:
485                logger.error (f'required parameter ({k}) not in POST params')
486                return {}
487
488        dsk = post_params['dsk']
489        par = post_params['par']
490        idi = post_params['idi']
491        ipr = post_params['ipr']
492        nci = post_params['nci']
493        ifs = post_params['ifs']
494        ptc = post_params['ptc']    ## Protocolo de clonación: Unicast, Multicast, Torrent
495        nfn = post_params['nfn']
496        ids = post_params['ids']
497
498        self.muestraMensaje (3)
499
500        try:
501            ## the ptc.split() is useless right now, since interfaceAdmin() does ' '.join(params) in order to spawn a shell
502            ## however we're going to need it in the future (when everything gets translated into python), plus it's harmless now. So let's do it
503            #self.interfaceAdmin (nfn, [dsk, par, nci, ipr, ptc])
504            self.interfaceAdmin (nfn, [dsk, par, nci, ipr] + ptc.split())
505            self.muestraMensaje (11)
506            herror = 0
507        except:
508            logger.warning ('Error al ejecutar el comando')
509            self.muestraMensaje (12)
510            herror = 1
511
512        cfg = self.LeeConfiguracion()
513        if not cfg:
514            logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
515
516        self.muestraMenu()
517
518        cmd = {
519            'nfn': 'RESPUESTA_RestaurarImagen',
520            'idi': idi,                  ## Identificador de la imagen
521            'dsk': dsk,                  ## Número de disco
522            'par': par,                  ## Número de partición
523            'ifs': ifs,                  ## Identificador del perfil software
524            'cfg': self.cfg2obj(cfg),    ## Configuración de discos
525        }
526        return self.respuestaEjecucionComando (cmd, herror, ids)
527
528    def do_RestaurarImagenGit (self, post_params):
529        for k in ['nfn', 'ids', 'disk', 'partition', 'repository', 'image_name', 'commit']:
530            if k not in post_params:
531                logger.error (f'required parameter ({k}) not in POST params')
532                return {}
533
534        nfn       = post_params['nfn']
535        ids       = post_params['ids']
536
537        disk        = post_params['disk']          ## Numero de disco
538        partition   = post_params['partition']     ## Numero de partición
539        repo        = post_params['repository']    ## URL a repositorio
540        image_name  = post_params['image_name']   ## Nombre de imagen
541
542        branch      = post_params['branch']        ## Rama de git seleccionada para restaurar
543        commit      = post_params['commit']        ## Referencia de git a restaurar. Debe ser un commit dentro de la rama indicada.
544
545        self.muestraMensaje (3)
546
547        try:
548            restore_args = ["--disk", disk, "--partition", partition, "--repository", repo, "--image-name", image_name, "--commit", commit]
549            if branch:
550                restore_args = restore_args + ["--branch", branch]
551
552            self.interfaceAdmin (nfn, restore_args)
553            self.muestraMensaje (11)
554            herror = 0
555        except:
556            logger.warning ('Error al ejecutar el comando')
557            self.muestraMensaje (12)
558            herror = 1
559
560        cfg = self.LeeConfiguracion()
561        if not cfg:
562            logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
563
564        self.muestraMenu()
565
566        cmd = {
567            'nfn': 'RESPUESTA_RestaurarImagenGit',
568            'disk'     : disk,           ## Número de disco
569            'partition': partition,      ## Número de partición
570            'repo'     : repo,           ## Repositorio de git
571            'cfg': self.cfg2obj(cfg),    ## Configuración de discos
572        }
573        return self.respuestaEjecucionComando (cmd, herror, ids)
574
575
576    def do_Configurar (self, post_params):
577        for k in ['nfn', 'dsk', 'cfg', 'ids']:
578            if k not in post_params:
579                logger.error (f'required parameter ({k}) not in POST params')
580                return {}
581
582        nfn = post_params['nfn']
583        dsk = post_params['dsk']
584        cfg = post_params['cfg']
585        ids = post_params['ids']
586        check_sizes = str ('check-sizes' in post_params and 'true' == post_params['check-sizes']).lower()
587        gen_script = str ('gen-script' in post_params and 'true' == post_params['gen-script']).lower()
588
589        if 'true' != check_sizes: self.muestraMensaje (4)
590
591        params = []
592        disk_info = cfg.pop (0)
593        logger.debug (f'disk_info ({disk_info})')
594        for k in ['dis']:
595            params.append (f'{k}={disk_info[k]}')
596        disk_info_str = '*'.join (params)
597
598        partitions = []
599        for entry in cfg:
600            logger.debug (f'entry ({entry})')
601            params = []
602            for k in ['par', 'cpt', 'sfi', 'tam', 'ope']:
603                params.append (f'{k}={entry[k]}')
604            partitions.append ('*'.join (params))
605        part_info_str = '%'.join (partitions)
606
607        cfg_str = f'{disk_info_str}!{part_info_str}%'
608
609        script_out = f'/tmp/ConfigurarScript-{self.IPlocal}'
610        try:
611            self.interfaceAdmin (nfn, ['ignored', cfg_str, script_out, check_sizes, gen_script])
612            if 'true' != check_sizes: self.muestraMensaje (14)
613            herror = 0
614        except:
615            logger.warning ('Error al ejecutar el comando')
616            if 'true' != check_sizes: self.muestraMensaje (13)
617            herror = 1
618
619        cmd = {
620            'nfn': 'RESPUESTA_Configurar',
621        }
622
623        if 'true' == gen_script:
624            script_contents = Path (script_out).read_bytes()
625            cmd['script'] = base64.b64encode (script_contents).decode ('utf-8')
626
627        ## si check_sizes y gen_script son falsos, entonces hemos reparticionado el disco: leer configuración de nuevo
628        if 'true' != check_sizes and 'true' != gen_script:
629            cfg = self.LeeConfiguracion()
630            if not cfg:
631                logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
632                return {}
633            cmd['cfg'] = self.cfg2obj (cfg)
634
635        self.muestraMenu()
636        return self.respuestaEjecucionComando (cmd, herror, ids)
637
638    def do_InventarioHardware (self, post_params):
639        self.muestraMensaje (6)
640
641        hrdsrc = f'/tmp/Chrd-{self.IPlocal}'      ## Nombre que tendra el archivo de inventario
642        try:
643            self.interfaceAdmin ('InventarioHardware', [hrdsrc])
644            hrdsrc_contents = Path (hrdsrc).read_bytes()
645            herror = 0
646        except:
647            logger.warning ('Error al ejecutar el comando')
648            self.muestraMensaje (18)
649            herror = 1
650
651        cmd = {}
652
653        if not herror:
654            cmd['hrd'] = base64.b64encode (hrdsrc_contents).decode ('utf-8')
655
656        self.muestraMenu()
657        return self.respuestaEjecucionComando (cmd, herror)
658
659    def do_InventarioSoftware (self, post_params):
660        for k in ['nfn', 'dsk', 'par', 'ids']:
661            if k not in post_params:
662                logger.error (f'required parameter ({k}) not in POST params')
663                return {}
664
665        nfn = post_params['nfn']
666        dsk = post_params['dsk']
667        par = post_params['par']
668        ids = post_params['ids']
669
670        self.muestraMensaje (7)
671
672        try:
673            cmd = self.InventariandoSoftware (dsk, par, 'InventarioSoftware')
674            herror = 0
675        except:
676            logger.warning ('Error al ejecutar el comando')
677            cmd = { 'nfn': 'RESPUESTA_InventarioSoftware' }
678            herror = 1
679
680        self.muestraMenu()
681        return self.respuestaEjecucionComando (cmd, herror, ids)
682
683    def do_Actualizar (self, post_params):
684        self.muestraMensaje (1)
685        #if !comandosPendientes: error 84 'Ha ocurrido algún problema al reiniciar la sesión del cliente'
686        cfg = self.LeeConfiguracion()
687        if not cfg:
688            logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
689            logger.error ('LeeConfiguracion() failed')
690            return {}
691
692        cmd = {
693            'nfn': 'RESPUESTA_Actualizar',
694            'cfg': self.cfg2obj (cfg),
695        }
696        self.muestraMenu()
697        return self.respuestaEjecucionComando (cmd, 0)
698
699    def do_Comando (self, post_params):
700        for k in ['nfn', 'ids']:
701            if k not in post_params:
702                logger.error (f'required parameter ({k}) not in POST params')
703                return {}
704
705        nfn = post_params['nfn']
706        ids = post_params['ids']
707
708        try:
709            self.interfaceAdmin (nfn)
710            herror = 0
711        except:
712            logger.warning ('Error al ejecutar el comando')
713            herror = 1
714
715        cmd = {
716            'nfn': 'RESPUESTA_Comando',
717        }
718        return self.respuestaEjecucionComando (cmd, herror, ids)
719
720    def do_ConsolaRemota (self, post_params):
721        for k in ['nfn', 'scp']:
722            if k not in post_params:
723                logger.error (f'required parameter ({k}) not in POST params')
724                return {}
725
726        nfn = post_params['nfn']
727        scp = base64.b64decode (unquote (post_params['scp'])).decode ('utf-8')
728        filescript = f'/tmp/_script_{self.IPlocal}'
729        ecosrc = f'/tmp/_econsola_{self.IPlocal}'
730        ecodst = f'/tmp/_Seconsola_{self.IPlocal}'    ## Nombre que tendra el archivo en el Servidor
731
732        with open (filescript, 'w') as fd:
733            fd.write (scp)
734
735        try:
736            self.interfaceAdmin (nfn, [filescript, ecosrc])
737            ecosrc_contents = Path (ecosrc).read_bytes()
738        except:
739            logger.error ('Error al ejecutar el comando')
740            return {}
741
742        logger.debug ('sending recibeArchivo to server')
743        res = self.enviaMensajeServidor ('recibeArchivo', { 'nfl': ecodst, 'contents': base64.b64encode (ecosrc_contents).decode ('utf-8') })
744        logger.debug (res)
745        if not res:
746            logger.error ('Ha ocurrido algún problema al enviar un archivo por la red')
747
748        return {}
749
750    def do_Apagar (self, post_params):
751        for k in ['nfn', 'ids']:
752            if k not in post_params:
753                logger.error (f'required parameter ({k}) not in POST params')
754                return {}
755
756        nfn = post_params['nfn']
757        ids = post_params['ids']
758
759        try:
760            self.interfaceAdmin (nfn)
761            herror = 0
762        except:
763            logger.warning ('Error al ejecutar el comando')
764            herror = 1
765
766        cmd = {
767            'nfn': 'RESPUESTA_Apagar',
768        }
769        return self.respuestaEjecucionComando (cmd, herror, ids)
770
771    def do_Reiniciar (self, post_params):
772        for k in ['nfn', 'ids']:
773            if k not in post_params:
774                logger.error (f'required parameter ({k}) not in POST params')
775                return {}
776
777        nfn = post_params['nfn']
778        ids = post_params['ids']
779
780        try:
781            self.interfaceAdmin (nfn)
782            herror = 0
783        except:
784            logger.warning ('Error al ejecutar el comando')
785            herror = 1
786
787        cmd = {
788            'nfn': 'RESPUESTA_Reiniciar',
789        }
790        return self.respuestaEjecucionComando (cmd, herror, ids)
791
792    def do_IniciarSesion (self, post_params):
793        for k in ['nfn', 'dsk', 'par', 'ids']:
794            if k not in post_params:
795                logger.error (f'required parameter ({k}) not in POST params')
796                return {}
797
798        nfn = post_params['nfn']
799        dsk = post_params['dsk']
800        par = post_params['par']
801        ids = post_params['ids']
802
803        try:
804            self.interfaceAdmin (nfn, [dsk, par])
805            herror = 0
806        except:
807            logger.warning ('Error al ejecutar el comando')
808            herror = 1
809
810        cmd = {
811            'nfn': 'RESPUESTA_IniciarSesion',
812        }
813        return self.respuestaEjecucionComando (cmd, herror, ids)
814
815    def do_EjecutarScript (self, post_params):
816        for k in ['nfn', 'scp', 'ids']:
817            if k not in post_params:
818                logger.error (f'required parameter ({k}) not in POST params')
819                return {}
820
821        nfn = post_params['nfn']
822        scp = base64.b64decode (unquote (post_params['scp'])).decode ('utf-8')
823        ids = post_params['ids']
824
825        self.muestraMensaje (8)
826
827        filescript = f'/tmp/_script_{self.IPlocal}'     ## Nombre del archivo de script
828        with open (filescript, 'w') as fd:
829            fd.write (scp)
830
831        try:
832            self.interfaceAdmin (nfn, [filescript])
833            self.muestraMensaje (22)
834            herror = 0
835        except:
836            logger.warning ('Error al ejecutar el comando')
837            self.muestraMensaje (21)
838            herror = 1
839
840        ## Toma configuración de particiones
841        cfg = self.LeeConfiguracion()
842        if not cfg:
843            logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
844            herror = 36
845
846        #herror=ejecutarCodigoBash(scp);       ## ogAdmClient.c:2004
847
848        cmd = {
849            'nfn': 'RESPUESTA_EjecutarScript',
850            'cfg': self.cfg2obj (cfg),
851        }
852        self.muestraMenu()
853        return self.respuestaEjecucionComando (cmd, herror, ids)
854
855
856
857
858
859    @execution_level('status')
860    def process_status (self, path, get_params, post_params, server):
861        logger.debug ('in process_status, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
862        if post_params is None:
863            logger.warning ('POST body is empty--setting it to an empty dictionary')
864            post_params = {}
865        full_config = 'full-config' in post_params and post_params['full-config']
866        thr_status = {}
867        for k in self.thread_list:
868            thr_status[k] = {
869                'running': self.thread_list[k]['running'],
870                'result': self.thread_list[k]['result'],
871            }
872        ret = {
873            'nfn': 'RESPUESTA_status',
874            'mac': self.mac,
875            'st': 'OGL',
876            'ip': self.IPlocal,
877            'threads': thr_status,
878        }
879        if full_config:
880            cfg = self.LeeConfiguracion()
881            ret['cfg'] = self.cfg2obj (cfg)
882        return ret
883
884    @check_secret
885    def process_popup (self, path, get_params, post_params, server):
886        logger.debug ('in process_popup, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
887        logger.debug ('type(post_params) "{}"'.format (type (post_params)))
888        ## in process_popup, should not happen, path "[]" get_params "{}" post_params "{'title': 'mi titulo', 'message': 'mi mensaje'}" server "<opengnsys.httpserver.HTTPServerHandler object at 0x7fa788cb8fa0>"
889        ## type(post_params) "<class 'dict'>"
890        return {'debug':'test'}
891
892    @execution_level('full')
893    @check_secret
894    def process_Actualizar (self, path, get_params, post_params, server):
895        logger.debug ('in process_Actualizar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
896        if post_params is None:
897            logger.warning ('POST body is empty--setting it to an empty dictionary')
898            post_params = {}
899        return self._long_running_job ('Actualizar', self.do_Actualizar, args=(post_params,))
900
901    @execution_level('full')
902    @check_secret
903    def process_Purgar (self, path, get_params, post_params, server):
904        logger.debug ('in process_Purgar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
905        os.kill (os.getpid(), signal.SIGTERM)
906        return {}
907        #exit (0)      ## ogAdmClient.c:905
908
909    @execution_level('full')
910    @check_secret
911    def process_Comando (self, path, get_params, post_params, server):
912        logger.debug ('in process_Comando, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
913        if post_params is None:
914            logger.warning ('POST body is empty--setting it to an empty dictionary')
915            post_params = {}
916        return self._long_running_job ('Comando', self.do_Comando, args=(post_params,))
917
918    def process_Sondeo (self, path, get_params, post_params, server):
919        logger.debug ('in process_Sondeo, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
920        if post_params is None:
921            logger.warning ('POST body is empty--setting it to an empty dictionary')
922            post_params = {}
923        return {}      ## ogAdmClient.c:920
924
925    @execution_level('full')
926    @check_secret
927    def process_ConsolaRemota (self, path, get_params, post_params, server):
928        logger.debug ('in process_ConsolaRemota, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
929        if post_params is None:
930            logger.warning ('POST body is empty--setting it to an empty dictionary')
931            post_params = {}
932        return self._long_running_job ('ConsolaRemota', self.do_ConsolaRemota, args=(post_params,))
933
934    @execution_level('full')
935    @check_secret
936    def process_Arrancar (self, path, get_params, post_params, server):
937        logger.debug ('in process_Arrancar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
938        if post_params is None:
939            logger.warning ('POST body is empty--setting it to an empty dictionary')
940            post_params = {}
941
942        for k in ['ids']:
943            if k not in post_params:
944                logger.error (f'required parameter ({k}) not in POST params')
945                return {}
946
947        ids = post_params['ids']
948
949        cmd = {
950            'nfn': 'RESPUESTA_Arrancar',
951            'tpc': 'OPG',
952        }
953        return self.respuestaEjecucionComando (cmd, 0, ids)
954
955    @execution_level('halt')
956    @check_secret
957    def process_Apagar (self, path, get_params, post_params, server):
958        logger.debug ('in process_Apagar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
959        if post_params is None:
960            logger.warning ('POST body is empty--setting it to an empty dictionary')
961            post_params = {}
962        return self._long_running_job ('Apagar', self.do_Apagar, args=(post_params,))
963
964    @execution_level('halt')
965    @check_secret
966    def process_Reiniciar (self, path, get_params, post_params, server):
967        logger.debug ('in process_Reiniciar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
968        if post_params is None:
969            logger.warning ('POST body is empty--setting it to an empty dictionary')
970            post_params = {}
971        return self._long_running_job ('Reiniciar', self.do_Reiniciar, args=(post_params,))
972
973    @execution_level('full')
974    @check_secret
975    def process_IniciarSesion (self, path, get_params, post_params, server):
976        logger.debug ('in process_IniciarSesion, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
977        if post_params is None:
978            logger.warning ('POST body is empty--setting it to an empty dictionary')
979            post_params = {}
980        return self._long_running_job ('IniciarSesion', self.do_IniciarSesion, args=(post_params,))
981
982    @execution_level('full')
983    @check_secret
984    def process_EjecutarScript (self, path, get_params, post_params, server):
985        logger.debug ('in process_EjecutarScript, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
986        if post_params is None:
987            logger.warning ('POST body is empty--setting it to an empty dictionary')
988            post_params = {}
989        return self._long_running_job ('EjecutarScript', self.do_EjecutarScript, args=(post_params,))
990
991    @execution_level('full')
992    @check_secret
993    def process_EjecutaComandosPendientes (self, path, get_params, post_params, server):
994        logger.debug ('in process_EjecutaComandosPendientes, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
995        if post_params is None:
996            logger.warning ('POST body is empty--setting it to an empty dictionary')
997            post_params = {}
998        return {'true':'true'}         ## ogAdmClient.c:2138
999
1000    @execution_level('full')
1001    @check_secret
1002    def process_CrearImagen (self, path, get_params, post_params, server):
1003        logger.debug ('in process_CrearImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1004        logger.debug ('type(post_params) "{}"'.format (type (post_params)))
1005        if post_params is None:
1006            logger.warning ('POST body is empty--setting it to an empty dictionary')
1007            post_params = {}
1008        return self._long_running_job ('CrearImagen', self.do_CrearImagen, args=(post_params,))
1009
1010    def process_CrearImagenGit (self, path, get_params, post_params, server):
1011        logger.debug ('in process_CrearImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1012        logger.debug ('type(post_params) "{}"'.format (type (post_params)))
1013        if post_params is None:
1014            logger.warning ('POST body is empty--setting it to an empty dictionary')
1015            post_params = {}
1016        return self._long_running_job ('CrearImagenGit', self.do_CrearImagenGit, args=(post_params,))
1017
1018    def process_ModificarImagenGit (self, path, get_params, post_params, server):
1019        logger.debug ('in process_ModificarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1020        logger.debug ('type(post_params) "{}"'.format (type (post_params)))
1021        if post_params is None:
1022            logger.warning ('POST body is empty--setting it to an empty dictionary')
1023            post_params = {}
1024        return self._long_running_job ('ModificarImagenGit', self.do_ModificarImagenGit, args=(post_params,))
1025
1026    def process_RestaurarImagenGit (self, path, get_params, post_params, server):
1027        logger.debug ('in process_RestaurarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1028        logger.debug ('type(post_params) "{}"'.format (type (post_params)))
1029        if post_params is None:
1030            logger.warning ('POST body is empty--setting it to an empty dictionary')
1031            post_params = {}
1032        return self._long_running_job ('RestaurarImagenGit', self.do_RestaurarImagenGit, args=(post_params,))
1033
1034    @execution_level('full')
1035    @check_secret
1036    def process_RestaurarImagen (self, path, get_params, post_params, server):
1037        logger.debug ('in process_RestaurarImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1038        logger.debug ('type(post_params) "{}"'.format (type (post_params)))
1039        if post_params is None:
1040            logger.warning ('POST body is empty--setting it to an empty dictionary')
1041            post_params = {}
1042        return self._long_running_job ('RestaurarImagen', self.do_RestaurarImagen, args=(post_params,))
1043
1044    @execution_level('full')
1045    @check_secret
1046    def process_Configurar (self, path, get_params, post_params, server):
1047        logger.debug ('in process_Configurar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1048        if post_params is None:
1049            logger.warning ('POST body is empty--setting it to an empty dictionary')
1050            post_params = {}
1051
1052        check_sizes = str ('check-sizes' in post_params and 'true' == post_params['check-sizes']).lower()
1053        gen_script = str ('gen-script' in post_params and 'true' == post_params['gen-script']).lower()
1054        if 'true' == check_sizes or 'true' == gen_script:
1055            return self.do_Configurar (post_params)
1056
1057        return self._long_running_job ('Configurar', self.do_Configurar, args=(post_params,))
1058
1059    @execution_level('full')
1060    @check_secret
1061    def process_InventarioHardware (self, path, get_params, post_params, server):
1062        logger.debug ('in process_InventarioHardware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1063        if post_params is None:
1064            logger.warning ('POST body is empty--setting it to an empty dictionary')
1065            post_params = {}
1066        return self.do_InventarioHardware (post_params)
1067
1068    @execution_level('full')
1069    @check_secret
1070    def process_InventarioSoftware (self, path, get_params, post_params, server):
1071        logger.debug ('in process_InventarioSoftware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1072        if post_params is None:
1073            logger.warning ('POST body is empty--setting it to an empty dictionary')
1074            post_params = {}
1075        return self._long_running_job ('InventarioSoftware', self.do_InventarioSoftware, args=(post_params,))
1076
1077    @execution_level('full')
1078    @check_secret
1079    def process_KillJob (self, path, get_params, post_params, server):
1080        logger.debug ('in process_KillJob, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1081        if post_params is None:
1082            logger.warning ('POST body is empty--setting it to an empty dictionary')
1083            post_params = {}
1084        jid = post_params['job_id']
1085        r = self.killer (jid)
1086        logger.debug (f'r bef ({r})')
1087        r.update ({ 'nfn':'RESPUESTA_KillJob', 'job':jid })
1088        logger.debug (f'r aft ({r})')
1089        return r
1090
1091    @execution_level('full')
1092    @check_secret
1093    def process_GetGitData (self, path, get_params, post_params, server):
1094        logger.debug ('in process_GetGitData, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
1095        if post_params is None:
1096            logger.warning ('POST body is empty--setting it to an empty dictionary')
1097            post_params = {}
1098
1099        for k in ['nfn', 'dsk', 'par', 'nfn']:
1100            if k not in post_params:
1101                logger.error (f'required parameter ({k}) not in POST params')
1102                return {}
1103
1104        tmp_gitdata = f'/tmp/gitdata-{self.IPlocal}'
1105        nfn = post_params['nfn']
1106        dsk = post_params['dsk']
1107        par = post_params['par']
1108
1109        try:
1110            self.interfaceAdmin (nfn, [dsk, par, tmp_gitdata])
1111            herror = 0
1112        except:
1113            herror = 1
1114
1115        if not os.path.exists (tmp_gitdata):
1116            return self.respuestaEjecucionComando ({'nfn':'RESPUESTA_GetGitData'}, 1)
1117
1118        with open (tmp_gitdata, 'r') as fd:
1119            gitdata = fd.read().strip()
1120
1121        branch, repo = gitdata.split (':')
1122        cmd = {
1123            'nfn':    'RESPUESTA_GetGitData',
1124            'branch': branch,
1125            'repo':   repo,
1126        }
1127        return self.respuestaEjecucionComando (cmd, herror)
Note: See TracBrowser for help on using the repository browser.