ogagent unification3 #10

Merged
nserrano merged 29 commits from unification3 into main 2024-10-14 09:20:31 +02:00
6 changed files with 546 additions and 169 deletions

View File

@ -1,3 +1,22 @@
ogagent (1.4.1-1) stable; urgency=medium
* Bugfix: move data structure to the right class
-- OpenGnsys developers <info@opengnsys.es> Fri, 11 Oct 2024 13:51:55 +0200
ogagent (1.4.0-1) stable; urgency=medium
* Add more functionality
* Begin using semantic versioning
-- OpenGnsys developers <info@opengnsys.es> Fri, 11 Oct 2024 13:06:51 +0200
ogagent (1.3.8-1) stable; urgency=medium
* Add more functionality to the ogAdmClient module
-- OpenGnsys developers <info@opengnsys.es> Tue, 01 Oct 2024 13:41:48 +0200
ogagent (1.3.7-1) stable; urgency=medium ogagent (1.3.7-1) stable; urgency=medium
* CloningEngine: RESTfully keep a list of long-running jobs * CloningEngine: RESTfully keep a list of long-running jobs

View File

@ -167,6 +167,22 @@ def disponibilidad_comandos():
return jsonify({}) return jsonify({})
@app.route('/opengnsys/rest/ogAdmClient/recibeArchivo', methods=['POST'])
def oac_recibe_archivo():
logging.info(f'{request.get_json()}')
j = request.get_json(force=True)
nfl = j['nfl']
contents = j['contents']
logging.info(f'nfl ({nfl}) contents ({contents})')
dec = base64.b64decode (contents).decode ('utf-8')
logging.info(f'dec ({dec})')
return jsonify({'anything':'anything'}) ## if we return {}, then we trigger "if not {}" which happens to be true
@app.route('/opengnsys/rest/ogAdmClient/callback', methods=['POST'])
def oac_callback():
logging.info(f'{request.get_json()}')
return jsonify({'anything':'anything'})
@app.route('/opengnsys/rest/ogAdmClient/<cucu>', methods=['GET', 'POST']) @app.route('/opengnsys/rest/ogAdmClient/<cucu>', methods=['GET', 'POST'])
def oac_cucu(cucu): def oac_cucu(cucu):
#j = request.get_json(force=True) #j = request.get_json(force=True)
@ -181,10 +197,21 @@ def oac_cucu(cucu):
## agente oglive: modulo CloningEngine ## agente oglive: modulo CloningEngine
@app.route('/opengnsys/rest/CloningEngine/recibeArchivo', methods=['POST']) @app.route('/opengnsys/rest/CloningEngine/recibeArchivo', methods=['POST'])
def recibe_archivo(): def ce_recibe_archivo():
logging.info(f'{request.get_json()}') logging.info(f'{request.get_json()}')
j = request.get_json(force=True)
nfl = j['nfl']
contents = j['contents']
logging.info(f'nfl ({nfl}) contents ({contents})')
dec = base64.b64decode (contents).decode ('utf-8')
logging.info(f'dec ({dec})')
return jsonify({'anything':'anything'}) ## if we return {}, then we trigger "if not {}" which happens to be true return jsonify({'anything':'anything'}) ## if we return {}, then we trigger "if not {}" which happens to be true
@app.route('/opengnsys/rest/CloningEngine/callback', methods=['POST'])
def ce_callback():
logging.info(f'{request.get_json()}')
return jsonify({'anything':'anything'})
@app.route('/opengnsys/rest/CloningEngine/<cucu>', methods=['GET', 'POST']) @app.route('/opengnsys/rest/CloningEngine/<cucu>', methods=['GET', 'POST'])
def ce_cucu(cucu): def ce_cucu(cucu):
abort (404) abort (404)

View File

@ -1 +1 @@
1.3.7 1.4.1

View File

@ -32,12 +32,10 @@
import base64 import base64
import os import os
import time
import random
from pathlib import Path from pathlib import Path
from opengnsys.log import logger from opengnsys.log import logger
from opengnsys.workers import ogLiveWorker, ThreadWithResult from opengnsys.workers import ogLiveWorker
class CloningEngineWorker (ogLiveWorker): class CloningEngineWorker (ogLiveWorker):
name = 'CloningEngine' # Module name name = 'CloningEngine' # Module name
@ -50,21 +48,6 @@ class CloningEngineWorker (ogLiveWorker):
def onDeactivation (self): def onDeactivation (self):
logger.debug ('onDeactivation') logger.debug ('onDeactivation')
## en C, esto envia una trama de respuesta al servidor. Devuelve un boolean
## en python, simplemente termina de construir la respuesta y la devuelve; no envía nada por la red. El caller la usa en return() para enviar implícitamente la respuesta
def respuestaEjecucionComando (self, cmd, herror, ids):
if ids: ## Existe seguimiento
cmd['ids'] = ids ## Añade identificador de la sesión
if 0 == herror: ## el comando terminó con resultado satisfactorio
cmd['res'] = 1
cmd['der'] = ''
else: ## el comando tuvo algún error
cmd['res'] = 2
cmd['der'] = self.tbErroresScripts[herror] ## XXX
return cmd
def InventariandoSoftware (self, dsk, par, sw, nfn): def InventariandoSoftware (self, dsk, par, sw, nfn):
sft_src = f'/tmp/CSft-{self.IPlocal}-{par}' sft_src = f'/tmp/CSft-{self.IPlocal}-{par}'
try: try:
@ -118,7 +101,13 @@ class CloningEngineWorker (ogLiveWorker):
self.muestraMensaje (7) self.muestraMensaje (7)
if self.InventariandoSoftware (dsk, par, False, 'InventarioSoftware'): ## Crea inventario Software previamente try:
res = self.InventariandoSoftware (dsk, par, False, 'InventarioSoftware') ## Crea inventario Software previamente
except:
logger.warning ('Error al ejecutar el comando')
return {}
if res:
self.muestraMensaje (2) self.muestraMensaje (2)
try: try:
self.interfaceAdmin (nfn, [dsk, par, nci, ipr]) self.interfaceAdmin (nfn, [dsk, par, nci, ipr])
@ -186,40 +175,7 @@ class CloningEngineWorker (ogLiveWorker):
} }
return self.respuestaEjecucionComando (cmd, herror, ids) return self.respuestaEjecucionComando (cmd, herror, ids)
def _long_running_job (self, name, f, args):
any_job_running = False
for k in self.thread_list:
if self.thread_list[k]['running']:
any_job_running = True
break
if any_job_running:
logger.info ('some job is already running, refusing to launch another one')
return { 'job_id': None, 'message': 'some job is already running, refusing to launch another one' }
job_id = '{}-{}'.format (name, ''.join (random.choice ('0123456789abcdef') for _ in range (8)))
self.thread_list[job_id] = {
'thread': ThreadWithResult (target=f, args=args),
'starttime': time.time(),
'running': True,
'result': None
}
self.thread_list[job_id]['thread'].start()
return { 'job_id': job_id }
def process_status (self, path, get_params, post_params, server): def process_status (self, path, get_params, post_params, server):
## join finished threads
for k in self.thread_list:
logger.debug (f'considering thread ({k})')
elem = self.thread_list[k]
if 'thread' in elem:
elem['thread'].join (0.05)
if not elem['thread'].is_alive():
logger.debug (f'is no longer alive, k ({k}) thread ({elem["thread"]})')
elem['running'] = False
elem['result'] = elem['thread'].result
del elem['thread']
## return status of threads
thr_status = {} thr_status = {}
for k in self.thread_list: for k in self.thread_list:
thr_status[k] = { thr_status[k] = {
@ -228,6 +184,109 @@ class CloningEngineWorker (ogLiveWorker):
} }
return thr_status return thr_status
def do_Configurar (self, post_params):
for k in ['nfn', 'dsk', 'cfg', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
dsk = post_params['dsk']
cfg = post_params['cfg'].replace('\n','$').replace('\t','#')
ids = post_params['ids']
self.muestraMensaje (4)
try:
self.interfaceAdmin (nfn, [dsk, cfg])
self.muestraMensaje (14)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (13)
herror = 1
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
return {}
cmd = {
'nfn': 'RESPUESTA_Configurar',
'cfg': cfg,
}
self.muestraMenu()
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_InventarioHardware (self, post_params):
for k in ['nfn', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
ids = post_params['ids']
self.muestraMensaje (6)
hrdsrc = f'/tmp/Chrd-{self.IPlocal}' ## Nombre que tendra el archivo de inventario
hrddst = f'/tmp/Shrd-{self.IPlocal}' ## Nombre que tendra el archivo en el Servidor
try:
self.interfaceAdmin (nfn, [hrdsrc])
hrdsrc_contents = Path (hrdsrc).read_bytes()
logger.debug (f'hrdsrc_contents 1 ({hrdsrc_contents})')
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (18)
herror = 1
if herror:
hrddst = ''
else:
logger.debug (f'hrdsrc_contents 2 ({hrdsrc_contents})')
## Envía fichero de inventario al servidor
res = self.enviaMensajeServidor ('recibeArchivo', { 'nfl': hrddst, 'contents': base64.b64encode (hrdsrc_contents).decode ('utf-8') })
logger.debug (res)
if not res:
logger.error ('Ha ocurrido algún problema al enviar un archivo por la red')
herror = 12 ## Error de envío de fichero por la red
self.muestraMensaje (17)
## Envia respuesta de ejecución de la función de interface
cmd = {
'nfn': 'RESPUESTA_InventarioHardware',
'hrd': hrddst,
}
self.muestraMenu()
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_InventarioSoftware (self, post_params):
for k in ['nfn', 'dsk', 'par', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
dsk = post_params['dsk']
par = post_params['par']
ids = post_params['ids']
self.muestraMensaje (7)
try:
self.InventariandoSoftware (dsk, par, True, 'InventarioSoftware')
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
herror = 1
self.muestraMenu()
cmd = {
'nfn': 'RESPUESTA_InventarioSoftware',
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def process_CrearImagen (self, path, get_params, post_params, server): def process_CrearImagen (self, path, get_params, post_params, server):
logger.debug ('in process_CrearImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_CrearImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params))) logger.debug ('type(post_params) "{}"'.format (type (post_params)))
@ -258,3 +317,15 @@ class CloningEngineWorker (ogLiveWorker):
logger.debug ('in process_RestaurarSoftIncremental, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_RestaurarSoftIncremental, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.warning ('this method has been removed') logger.warning ('this method has been removed')
raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' }) raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
def process_Configurar (self, path, get_params, post_params, server):
logger.debug ('in process_Configurar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Configurar', self.do_Configurar, args=(post_params,))
def process_InventarioHardware (self, path, get_params, post_params, server):
logger.debug ('in process_InventarioHardware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('InventarioHardware', self.do_InventarioHardware, args=(post_params,))
def process_InventarioSoftware (self, path, get_params, post_params, server):
logger.debug ('in process_InventarioSoftware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('InventarioSoftware', self.do_InventarioSoftware, args=(post_params,))

View File

@ -35,7 +35,11 @@
import base64 import base64
#import threading #import threading
#import time #import time
import os
import signal
import subprocess import subprocess
from pathlib import Path
from urllib.parse import unquote
#from opengnsys import operations #from opengnsys import operations
from opengnsys.log import logger from opengnsys.log import logger
@ -68,85 +72,6 @@ class ogAdmClientWorker (ogLiveWorker):
#interface = None # Bound interface for OpenGnsys (el otro modulo lo usa para obtener .ip y .mac #interface = None # Bound interface for OpenGnsys (el otro modulo lo usa para obtener .ip y .mac
REST = None # REST object REST = None # REST object
tbErroresScripts = [
"Se han generado errores desconocidos. No se puede continuar la ejecución de este módulo", ## 0
"001-Formato de ejecución incorrecto.",
"002-Fichero o dispositivo no encontrado",
"003-Error en partición de disco",
"004-Partición o fichero bloqueado",
"005-Error al crear o restaurar una imagen",
"006-Sin sistema operativo",
"007-Programa o función BOOLEAN no ejecutable",
"008-Error en la creación del archivo de eco para consola remota",
"009-Error en la lectura del archivo temporal de intercambio",
"010-Error al ejecutar la llamada a la interface de administración",
"011-La información retornada por la interface de administración excede de la longitud permitida",
"012-Error en el envío de fichero por la red",
"013-Error en la creación del proceso hijo",
"014-Error de escritura en destino",
"015-Sin Cache en el Cliente",
"016-No hay espacio en la cache para almacenar fichero-imagen",
"017-Error al Reducir el Sistema Archivos",
"018-Error al Expandir el Sistema Archivos",
"019-Valor fuera de rango o no válido.",
"020-Sistema de archivos desconocido o no se puede montar",
"021-Error en partición de caché local",
"022-El disco indicado no contiene una particion GPT",
"023-Error no definido",
"024-Error no definido",
"025-Error no definido",
"026-Error no definido",
"027-Error no definido",
"028-Error no definido",
"029-Error no definido",
"030-Error al restaurar imagen - Imagen mas grande que particion",
"031-Error al realizar el comando updateCache",
"032-Error al formatear",
"033-Archivo de imagen corrupto o de otra versión de partclone",
"034-Error no definido",
"035-Error no definido",
"036-Error no definido",
"037-Error no definido",
"038-Error no definido",
"039-Error no definido",
"040-Error imprevisto no definido",
"041-Error no definido",
"042-Error no definido",
"043-Error no definido",
"044-Error no definido",
"045-Error no definido",
"046-Error no definido",
"047-Error no definido",
"048-Error no definido",
"049-Error no definido",
"050-Error en la generación de sintaxis de transferenica unicast",
"051-Error en envio UNICAST de una particion",
"052-Error en envio UNICAST de un fichero",
"053-Error en la recepcion UNICAST de una particion",
"054-Error en la recepcion UNICAST de un fichero",
"055-Error en la generacion de sintaxis de transferenica Multicast",
"056-Error en envio MULTICAST de un fichero",
"057-Error en la recepcion MULTICAST de un fichero",
"058-Error en envio MULTICAST de una particion",
"059-Error en la recepcion MULTICAST de una particion",
"060-Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER",
"061-Error no definido",
"062-Error no definido",
"063-Error no definido",
"064-Error no definido",
"065-Error no definido",
"066-Error no definido",
"067-Error no definido",
"068-Error no definido",
"069-Error no definido",
"070-Error al montar una imagen sincronizada.",
"071-Imagen no sincronizable (es monolitica).",
"072-Error al desmontar la imagen.",
"073-No se detectan diferencias entre la imagen basica y la particion.",
"074-Error al sincronizar, puede afectar la creacion/restauracion de la imagen.",
"Error desconocido",
]
def onDeactivation (self): def onDeactivation (self):
""" """
Sends OGAgent stopping notification to OpenGnsys server Sends OGAgent stopping notification to OpenGnsys server
@ -162,10 +87,6 @@ class ogAdmClientWorker (ogLiveWorker):
#def onLogout (self, user): #def onLogout (self, user):
# logger.warning ('in onLogout, should not happen') # logger.warning ('in onLogout, should not happen')
@check_secret
def process_status (self, path, get_params, post_params, server):
return {self.name: 'in process_status'} ## XXX
#@check_secret #@check_secret
#def process_reboot (self, path, get_params, post_params, server): #def process_reboot (self, path, get_params, post_params, server):
# """ # """
@ -203,15 +124,6 @@ class ogAdmClientWorker (ogLiveWorker):
# threading.Thread (target=pwoff).start() # threading.Thread (target=pwoff).start()
# return {'op': 'launched'} # return {'op': 'launched'}
## curl --insecure -X POST --data '{"nfn": "popup", "title": "my title", "message": "my message"}' https://192.168.1.249:8000/ogAdmClient/popup
@check_secret
def process_popup (self, path, get_params, post_params, server):
logger.debug ('in process_popup, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
## in process_popup, should not happen, path "[]" get_params "{}" post_params "{'title': 'mi titulo', 'message': 'mi mensaje'}" server "<opengnsys.httpserver.HTTPServerHandler object at 0x7fa788cb8fa0>"
## type(post_params) "<class 'dict'>"
return {'debug':'test'}
@ -394,42 +306,250 @@ class ogAdmClientWorker (ogLiveWorker):
logger.info ('onActivation ok') logger.info ('onActivation ok')
## curl --insecure https://192.168.1.249:8000/ogAdmClient/Actualizar @check_secret
def process_status (self, path, get_params, post_params, server):
thr_status = {}
for k in self.thread_list:
thr_status[k] = {
'running': self.thread_list[k]['running'],
'result': self.thread_list[k]['result'],
}
return thr_status
@check_secret
def process_popup (self, path, get_params, post_params, server):
logger.debug ('in process_popup, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
## in process_popup, should not happen, path "[]" get_params "{}" post_params "{'title': 'mi titulo', 'message': 'mi mensaje'}" server "<opengnsys.httpserver.HTTPServerHandler object at 0x7fa788cb8fa0>"
## type(post_params) "<class 'dict'>"
return {'debug':'test'}
def do_Actualizar (self, post_params):
self.muestraMensaje (1)
#if !comandosPendientes: error 84 'Ha ocurrido algún problema al reiniciar la sesión del cliente'
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
logger.error ('LeeConfiguracion() failed')
return {}
cmd = {
'nfn': 'RESPUESTA_Actualizar',
'cfg': cfg,
}
self.muestraMenu()
return self.respuestaEjecucionComando (cmd, 0)
def do_Comando (self, post_params):
for k in ['nfn', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
ids = post_params['ids']
try:
self.interfaceAdmin (nfn)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
herror = 1
cmd = {
'nfn': 'RESPUESTA_Comando',
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_ConsolaRemota (self, post_params):
for k in ['nfn', 'scp']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
scp = unquote (post_params['scp'])
filescript = f'/tmp/_script_{self.IPlocal}'
ecosrc = f'/tmp/_econsola_{self.IPlocal}'
ecodst = f'/tmp/_Seconsola_{self.IPlocal}' ## Nombre que tendra el archivo en el Servidor
with open (filescript, 'w') as fd:
fd.write (scp)
try:
self.interfaceAdmin (nfn, [filescript, ecosrc])
ecosrc_contents = Path (ecosrc).read_bytes()
except:
logger.error ('Error al ejecutar el comando')
return {}
logger.debug ('sending recibeArchivo to server')
res = self.enviaMensajeServidor ('recibeArchivo', { 'nfl': ecodst, 'contents': base64.b64encode (ecosrc_contents).decode ('utf-8') })
logger.debug (res)
if not res:
logger.error ('Ha ocurrido algún problema al enviar un archivo por la red')
return {}
def do_Apagar (self, post_params):
for k in ['nfn', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
ids = post_params['ids']
try:
self.interfaceAdmin (nfn)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
herror = 1
cmd = {
'nfn': 'RESPUESTA_Apagar',
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_Reiniciar (self, post_params):
for k in ['nfn', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
ids = post_params['ids']
try:
self.interfaceAdmin (nfn)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
herror = 1
cmd = {
'nfn': 'RESPUESTA_Reiniciar',
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_IniciarSesion (self, post_params):
for k in ['nfn', 'dsk', 'par', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
dsk = post_params['dsk']
par = post_params['par']
ids = post_params['ids']
try:
self.interfaceAdmin (nfn, [dsk, par])
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
herror = 1
cmd = {
'nfn': 'RESPUESTA_IniciarSesion',
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_EjecutarScript (self, post_params):
for k in ['nfn', 'scp', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
nfn = post_params['nfn']
scp = unquote (post_params['scp'])
ids = post_params['ids']
self.muestraMensaje (8)
filescript = f'/tmp/_script_{self.IPlocal}' ## Nombre del archivo de script
with open (filescript, 'w') as fd:
fd.write (scp)
try:
self.interfaceAdmin (nfn, [filescript])
self.muestraMensaje (22)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (21)
herror = 1
## Toma configuración de particiones
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
herror = 36
#herror=ejecutarCodigoBash(scp); ## ogAdmClient.c:2004
cmd = {
'nfn': 'RESPUESTA_EjecutarScript',
'cfg': cfg,
}
self.muestraMenu()
return self.respuestaEjecucionComando (cmd, herror, ids)
def process_Actualizar (self, path, get_params, post_params, server): def process_Actualizar (self, path, get_params, post_params, server):
logger.warning ('in process_Actualizar') logger.debug ('in process_Actualizar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Actualizar', self.do_Actualizar, args=(post_params,))
def process_Purgar (self, path, get_params, post_params, server): def process_Purgar (self, path, get_params, post_params, server):
logger.warning ('in process_Purgar') logger.debug ('in process_Purgar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
os.kill (os.getpid(), signal.SIGTERM)
return {}
#exit (0) ## ogAdmClient.c:905
def process_ConsolaRemota (self, path, get_params, post_params, server): def process_Comando (self, path, get_params, post_params, server):
logger.warning ('in process_ConsolaRemota') logger.debug ('in process_Comando, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Comando', self.do_Comando, args=(post_params,))
def process_Sondeo (self, path, get_params, post_params, server): def process_Sondeo (self, path, get_params, post_params, server):
logger.warning ('in process_Sondeo') logger.debug ('in process_Sondeo, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return {} ## ogAdmClient.c:920
def process_ConsolaRemota (self, path, get_params, post_params, server):
logger.debug ('in process_ConsolaRemota, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('ConsolaRemota', self.do_ConsolaRemota, args=(post_params,))
def process_Arrancar (self, path, get_params, post_params, server): def process_Arrancar (self, path, get_params, post_params, server):
logger.warning ('in process_Arrancar') logger.debug ('in process_Arrancar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
for k in ['ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
ids = post_params['ids']
cmd = {
'nfn': 'RESPUESTA_Arrancar',
'tpc': 'OPG',
}
return self.respuestaEjecucionComando (cmd, 0, ids)
def process_Apagar (self, path, get_params, post_params, server): def process_Apagar (self, path, get_params, post_params, server):
logger.warning ('in process_Apagar') logger.debug ('in process_Apagar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Apagar', self.do_Apagar, args=(post_params,))
def process_Reiniciar (self, path, get_params, post_params, server): def process_Reiniciar (self, path, get_params, post_params, server):
logger.warning ('in process_Reiniciar') logger.debug ('in process_Reiniciar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Reiniciar', self.do_Reiniciar, args=(post_params,))
def process_IniciarSesion (self, path, get_params, post_params, server): def process_IniciarSesion (self, path, get_params, post_params, server):
logger.warning ('in process_IniciarSesion') logger.debug ('in process_IniciarSesion, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('IniciarSesion', self.do_IniciarSesion, args=(post_params,))
def process_Configurar (self, path, get_params, post_params, server):
logger.warning ('in process_Configurar')
def process_EjecutarScript (self, path, get_params, post_params, server): def process_EjecutarScript (self, path, get_params, post_params, server):
logger.warning ('in process_EjecutarScript') logger.debug ('in process_EjecutarScript, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('EjecutarScript', self.do_EjecutarScript, args=(post_params,))
def process_InventarioHardware (self, path, get_params, post_params, server):
logger.warning ('in process_InventarioHardware')
def process_InventarioSoftware (self, path, get_params, post_params, server):
logger.warning ('in process_InventarioSoftware')
def process_EjecutaComandosPendientes (self, path, get_params, post_params, server): def process_EjecutaComandosPendientes (self, path, get_params, post_params, server):
logger.warning ('in process_EjecutaComandosPendientes') logger.debug ('in process_EjecutaComandosPendientes, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return {'true':'true'} ## ogAdmClient.c:2138

View File

@ -31,6 +31,8 @@
# pylint: disable=unused-wildcard-import,wildcard-import # pylint: disable=unused-wildcard-import,wildcard-import
import os import os
import time
import random
import subprocess import subprocess
import threading import threading
@ -56,6 +58,107 @@ class ThreadWithResult (threading.Thread):
class ogLiveWorker(ServerWorker): class ogLiveWorker(ServerWorker):
thread_list = {} thread_list = {}
tbErroresScripts = [
"Se han generado errores desconocidos. No se puede continuar la ejecución de este módulo", ## 0
"001-Formato de ejecución incorrecto.",
"002-Fichero o dispositivo no encontrado",
"003-Error en partición de disco",
"004-Partición o fichero bloqueado",
"005-Error al crear o restaurar una imagen",
"006-Sin sistema operativo",
"007-Programa o función BOOLEAN no ejecutable",
"008-Error en la creación del archivo de eco para consola remota",
"009-Error en la lectura del archivo temporal de intercambio",
"010-Error al ejecutar la llamada a la interface de administración",
"011-La información retornada por la interface de administración excede de la longitud permitida",
"012-Error en el envío de fichero por la red",
"013-Error en la creación del proceso hijo",
"014-Error de escritura en destino",
"015-Sin Cache en el Cliente",
"016-No hay espacio en la cache para almacenar fichero-imagen",
"017-Error al Reducir el Sistema Archivos",
"018-Error al Expandir el Sistema Archivos",
"019-Valor fuera de rango o no válido.",
"020-Sistema de archivos desconocido o no se puede montar",
"021-Error en partición de caché local",
"022-El disco indicado no contiene una particion GPT",
"023-Error no definido",
"024-Error no definido",
"025-Error no definido",
"026-Error no definido",
"027-Error no definido",
"028-Error no definido",
"029-Error no definido",
"030-Error al restaurar imagen - Imagen mas grande que particion",
"031-Error al realizar el comando updateCache",
"032-Error al formatear",
"033-Archivo de imagen corrupto o de otra versión de partclone",
"034-Error no definido",
"035-Error no definido",
"036-Error no definido",
"037-Error no definido",
"038-Error no definido",
"039-Error no definido",
"040-Error imprevisto no definido",
"041-Error no definido",
"042-Error no definido",
"043-Error no definido",
"044-Error no definido",
"045-Error no definido",
"046-Error no definido",
"047-Error no definido",
"048-Error no definido",
"049-Error no definido",
"050-Error en la generación de sintaxis de transferenica unicast",
"051-Error en envio UNICAST de una particion",
"052-Error en envio UNICAST de un fichero",
"053-Error en la recepcion UNICAST de una particion",
"054-Error en la recepcion UNICAST de un fichero",
"055-Error en la generacion de sintaxis de transferenica Multicast",
"056-Error en envio MULTICAST de un fichero",
"057-Error en la recepcion MULTICAST de un fichero",
"058-Error en envio MULTICAST de una particion",
"059-Error en la recepcion MULTICAST de una particion",
"060-Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER",
"061-Error no definido",
"062-Error no definido",
"063-Error no definido",
"064-Error no definido",
"065-Error no definido",
"066-Error no definido",
"067-Error no definido",
"068-Error no definido",
"069-Error no definido",
"070-Error al montar una imagen sincronizada.",
"071-Imagen no sincronizable (es monolitica).",
"072-Error al desmontar la imagen.",
"073-No se detectan diferencias entre la imagen basica y la particion.",
"074-Error al sincronizar, puede afectar la creacion/restauracion de la imagen.",
"Error desconocido",
]
def notifier (self, result):
logger.debug (f'notifier() called, result ({result})')
res = self.REST.sendMessage ('/'.join ([self.name, 'callback']), result)
def mon (self):
while True:
#print ('mon(): iterating')
for k in self.thread_list:
elem = self.thread_list[k]
if 'thread' not in elem: continue
logger.debug (f'considering thread ({k})')
try: elem['thread'].join (0.05)
except RuntimeError: pass ## race condition: a thread is created and this code runs before it is start()ed
if not elem['thread'].is_alive():
logger.debug (f'is no longer alive, k ({k}) thread ({elem["thread"]})')
elem['running'] = False
elem['result'] = elem['thread'].result
del elem['thread']
self.notifier (elem['result'])
time.sleep (1)
def interfaceAdmin (self, method, parametros=[]): def interfaceAdmin (self, method, parametros=[]):
exe = '{}/{}'.format (self.pathinterface, method) exe = '{}/{}'.format (self.pathinterface, method)
## for development only. Will be removed when the referenced bash code (/opt/opengnsys/lib/engine/bin/*.lib) is translated into python ## for development only. Will be removed when the referenced bash code (/opt/opengnsys/lib/engine/bin/*.lib) is translated into python
@ -66,9 +169,9 @@ class ogLiveWorker(ServerWorker):
''' '''
if parametros: if parametros:
proc = ['bash', '-c', '{} set -x; bash -x {} {}; set +x'.format (devel_bash_prefix, exe, ' '.join (parametros))] proc = ['bash', '-c', '{} bash -x {} {}'.format (devel_bash_prefix, exe, ' '.join (parametros))]
else: else:
proc = ['bash', '-c', '{} set -x; bash -x {}; set +x'.format (devel_bash_prefix, exe)] proc = ['bash', '-c', '{} bash -x {}'.format (devel_bash_prefix, exe)]
logger.debug ('subprocess.run ("{}", capture_output=True)'.format (proc)) logger.debug ('subprocess.run ("{}", capture_output=True)'.format (proc))
p = subprocess.run (proc, capture_output=True) p = subprocess.run (proc, capture_output=True)
## DEBUG ## DEBUG
@ -114,6 +217,21 @@ class ogLiveWorker(ServerWorker):
return res return res
## en C, esto envia una trama de respuesta al servidor. Devuelve un boolean
## en python, simplemente termina de construir la respuesta y la devuelve; no envía nada por la red. El caller la usa en return() para enviar implícitamente la respuesta
def respuestaEjecucionComando (self, cmd, herror, ids=None):
if ids: ## Existe seguimiento
cmd['ids'] = ids ## Añade identificador de la sesión
if 0 == herror: ## el comando terminó con resultado satisfactorio
cmd['res'] = 1
cmd['der'] = ''
else: ## el comando tuvo algún error
cmd['res'] = 2
cmd['der'] = self.tbErroresScripts[herror] ## XXX
return cmd
def cargaPaginaWeb (self, url=None): def cargaPaginaWeb (self, url=None):
if (not url): url = self.urlMenu if (not url): url = self.urlMenu
os.system ('pkill -9 browser') os.system ('pkill -9 browser')
@ -175,3 +293,25 @@ class ogLiveWorker(ServerWorker):
if not self.tomaIPlocal(): if not self.tomaIPlocal():
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo') raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
threading.Thread (name='monitoring_thread', target=self.mon, daemon=True).start()
def _long_running_job (self, name, f, args):
any_job_running = False
for k in self.thread_list:
if self.thread_list[k]['running']:
any_job_running = True
break
if any_job_running:
logger.info ('some job is already running, refusing to launch another one')
return { 'job_id': None, 'message': 'some job is already running, refusing to launch another one' }
job_id = '{}-{}'.format (name, ''.join (random.choice ('0123456789abcdef') for _ in range (8)))
self.thread_list[job_id] = {
'thread': ThreadWithResult (target=f, args=args),
'starttime': time.time(),
'running': True,
'result': None
}
self.thread_list[job_id]['thread'].start()
return { 'job_id': job_id }