Merge pull request 'ogagent unification 2' (#9) from unification2 into main
Reviewed-on: #9pull/12/head
commit
1cf3c6bf2c
|
@ -1,3 +1,16 @@
|
|||
ogagent (1.3.7-1) stable; urgency=medium
|
||||
|
||||
* CloningEngine: RESTfully keep a list of long-running jobs
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 27 Sep 2024 18:03:16 +0200
|
||||
|
||||
ogagent (1.3.6-1) stable; urgency=medium
|
||||
|
||||
* Add more functionality to the ogAdmClient module
|
||||
* Add CloningEngine module
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 19 Sep 2024 13:28:17 +0200
|
||||
|
||||
ogagent (1.3.5-1) stable; urgency=medium
|
||||
|
||||
* Don't unconditionally load modules--dynamically load everything
|
||||
|
|
|
@ -3,6 +3,7 @@ import os
|
|||
import logging
|
||||
import json
|
||||
import subprocess
|
||||
import base64
|
||||
|
||||
## FLASK_APP=/path/to/ogcore-mock.py FLASK_ENV=development FLASK_RUN_CERT=adhoc sudo --preserve-env flask run --host 192.168.1.249 --port 443
|
||||
|
||||
|
@ -14,30 +15,28 @@ logging.basicConfig(level=logging.INFO)
|
|||
|
||||
@app.route('/opengnsys/rest/ogagent/<cucu>', methods=['POST'])
|
||||
def og_agent(cucu):
|
||||
c = request
|
||||
logging.info(f"{request.get_json()}")
|
||||
logging.info(f'{request.get_json()}')
|
||||
return jsonify({})
|
||||
|
||||
|
||||
|
||||
## agente oglive
|
||||
## agente oglive: modulo ogAdmClient
|
||||
|
||||
@app.route('/opengnsys/rest/__ogAdmClient/InclusionCliente', methods=['POST'])
|
||||
@app.route('/opengnsys/rest/ogAdmClient/InclusionCliente', methods=['POST'])
|
||||
def inclusion_cliente():
|
||||
c = request
|
||||
logging.info(f"{request.get_json()}")
|
||||
logging.info(f'{request.get_json()}')
|
||||
#procesoInclusionCliente() or { return (jsonify { 'res': 0 }) }
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph'] ## Toma ip
|
||||
cfg = j['cfg'] ## Toma configuracion
|
||||
logging.info(f"iph ({iph}) cfg ({cfg})")
|
||||
logging.info(f'iph ({iph}) cfg ({cfg})')
|
||||
|
||||
# dbi->query (sprintf "SELECT ordenadores.*,aulas.idaula,centros.idcentro FROM ordenadores INNER JOIN aulas ON aulas.idaula=ordenadores.idaula INNER JOIN centros ON centros.idcentro=aulas.idcentro WHERE ordenadores.ip = '%s'", iph);
|
||||
# if (!dbi_result_next_row(result)) { log_error ('client does not exist in database') }
|
||||
# log_debug (sprintf 'Client %s requesting inclusion', iph);
|
||||
|
||||
idordenador = 42 #dbi_result_get_uint(result, "idordenador")
|
||||
nombreordenador = "hal9000" #dbi_result_get_string(result, "nombreordenador");
|
||||
nombreordenador = 'hal9000' #dbi_result_get_string(result, "nombreordenador");
|
||||
cache = 42 #dbi_result_get_uint(result, "cache");
|
||||
idproautoexec = 42 #dbi_result_get_uint(result, "idproautoexec");
|
||||
idaula = 42 #dbi_result_get_uint(result, "idaula");
|
||||
|
@ -79,21 +78,20 @@ def _recorreProcedimientos(parametros, fileexe, idp):
|
|||
|
||||
return 1
|
||||
|
||||
@app.route('/opengnsys/rest/__ogAdmClient/AutoexecCliente', methods=['POST'])
|
||||
@app.route('/opengnsys/rest/ogAdmClient/AutoexecCliente', methods=['POST'])
|
||||
def autoexec_client():
|
||||
c = request
|
||||
logging.info(f"{request.get_json()}")
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph'] ## Toma dirección IP del cliente
|
||||
exe = j['exe'] ## Toma identificador del procedimiento inicial
|
||||
logging.info(f"iph ({iph}) exe ({exe})")
|
||||
logging.info(f'iph ({iph}) exe ({exe})')
|
||||
|
||||
fileautoexec = '/tmp/Sautoexec-{}'.format(iph)
|
||||
logging.info ("fileautoexec ({})".format (fileautoexec));
|
||||
logging.info ('fileautoexec ({})'.format (fileautoexec));
|
||||
try:
|
||||
fileexe = open (fileautoexec, 'w')
|
||||
except Exception as e:
|
||||
logging.error ("cannot create temporary file: {}".format (e))
|
||||
logging.error ('cannot create temporary file: {}'.format (e))
|
||||
return jsonify({})
|
||||
|
||||
if (_recorreProcedimientos ('', fileexe, exe)):
|
||||
|
@ -104,15 +102,16 @@ def autoexec_client():
|
|||
fileexe.close()
|
||||
return res
|
||||
|
||||
@app.route('/opengnsys/rest/__ogAdmClient/enviaArchivo', methods=['POST'])
|
||||
@app.route('/opengnsys/rest/ogAdmClient/enviaArchivo', methods=['POST'])
|
||||
def envia_archivo():
|
||||
c = request
|
||||
logging.info(f"{request.get_json()}")
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
nfl = j['nfl'] ## Toma nombre completo del archivo
|
||||
logging.info(f"nfl ({nfl})")
|
||||
logging.info(f'nfl ({nfl})')
|
||||
|
||||
return jsonify({'contents': subprocess.run (['cat', nfl], capture_output=True).stdout.decode('utf-8')})
|
||||
contents = subprocess.run (['cat', nfl], capture_output=True).stdout
|
||||
b64 = base64.b64encode (contents).decode ('utf-8')
|
||||
return jsonify({'contents': b64})
|
||||
|
||||
def clienteExistente(iph):
|
||||
## esto queda totalmente del lado del servidor, no lo implemento en python
|
||||
|
@ -130,19 +129,18 @@ def buscaComandos(ido):
|
|||
## convertirlo a json, aqui lo pongo a capon
|
||||
#return jsonify ({ 'nfn': 'popup', 'title': 'my title', 'message': 'my message', 'ids': ids })
|
||||
|
||||
@app.route('/opengnsys/rest/__ogAdmClient/ComandosPendientes', methods=['POST'])
|
||||
@app.route('/opengnsys/rest/ogAdmClient/ComandosPendientes', methods=['POST'])
|
||||
def comandos_pendientes():
|
||||
c = request
|
||||
logging.info(f"{request.get_json()}")
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph'] ## Toma dirección IP
|
||||
ido = j['ido'] ## Toma identificador del ordenador
|
||||
logging.info(f"iph ({iph}) ido ({ido})")
|
||||
logging.info(f'iph ({iph}) ido ({ido})')
|
||||
|
||||
idx = clienteExistente(iph) ## Busca índice del cliente
|
||||
if not idx:
|
||||
## que devuelvo?? pongamos un 404...
|
||||
abort(404, "Client does not exist")
|
||||
abort(404, 'Client does not exist')
|
||||
|
||||
param = buscaComandos(ido) ## Existen comandos pendientes, buscamos solo uno
|
||||
if param is None:
|
||||
|
@ -152,46 +150,62 @@ def comandos_pendientes():
|
|||
|
||||
return jsonify(param)
|
||||
|
||||
@app.route('/opengnsys/rest/__ogAdmClient/DisponibilidadComandos', methods=['POST'])
|
||||
@app.route('/opengnsys/rest/ogAdmClient/DisponibilidadComandos', methods=['POST'])
|
||||
def disponibilidad_comandos():
|
||||
c = request
|
||||
logging.info(f"{request.get_json()}")
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph']
|
||||
tpc = j['tpc']
|
||||
logging.info(f"iph ({iph}) tpc ({tpc})")
|
||||
logging.info(f'iph ({iph}) tpc ({tpc})')
|
||||
|
||||
idx = clienteExistente(iph) ## Busca índice del cliente
|
||||
if not idx:
|
||||
## que devuelvo?? pongamos un 404...
|
||||
abort(404, "Client does not exist")
|
||||
abort(404, 'Client does not exist')
|
||||
|
||||
#strcpy(tbsockets[idx].estado, tpc); ## esto queda totalmente del lado del servidor, no lo implemento en python
|
||||
|
||||
return jsonify({})
|
||||
|
||||
@app.route('/opengnsys/rest/__ogAdmClient/<cucu>', methods=['POST'])
|
||||
def cucu(cucu):
|
||||
c = request
|
||||
j = c.get_json(force=True)
|
||||
logging.info(f"{request.get_json()} {j}")
|
||||
if 'cucu' not in j:
|
||||
abort(400, "missing parameter 'cucu'")
|
||||
@app.route('/opengnsys/rest/ogAdmClient/<cucu>', methods=['GET', 'POST'])
|
||||
def oac_cucu(cucu):
|
||||
#j = request.get_json(force=True)
|
||||
#logging.info(f'{request.get_json()} {j}')
|
||||
#if 'cucu' not in j:
|
||||
# abort(400, 'missing parameter 'cucu'')
|
||||
#return jsonify({'cucu': j['cucu']})
|
||||
abort (404)
|
||||
|
||||
|
||||
|
||||
## agente oglive: modulo CloningEngine
|
||||
|
||||
@app.route('/opengnsys/rest/CloningEngine/recibeArchivo', methods=['POST'])
|
||||
def recibe_archivo():
|
||||
logging.info(f'{request.get_json()}')
|
||||
return jsonify({'anything':'anything'}) ## if we return {}, then we trigger "if not {}" which happens to be true
|
||||
|
||||
@app.route('/opengnsys/rest/CloningEngine/<cucu>', methods=['GET', 'POST'])
|
||||
def ce_cucu(cucu):
|
||||
abort (404)
|
||||
|
||||
|
||||
return jsonify({'cucu': j['cucu']})
|
||||
|
||||
@app.errorhandler(404)
|
||||
def _page_not_found(e):
|
||||
return render_template_string('''<!DOCTYPE html><html>not found</html>''')
|
||||
if type(e.description) is dict:
|
||||
return jsonify (e.description), e.code
|
||||
else:
|
||||
return render_template_string('''<!DOCTYPE html><html>not found</html>'''), e.code
|
||||
|
||||
@app.errorhandler(500)
|
||||
def _internal_server_error(e):
|
||||
return render_template_string('''<!DOCTYPE html><html>err</html>''')
|
||||
return render_template_string('''<!DOCTYPE html><html>err</html>'''), e.code
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def _exception(e):
|
||||
print(e)
|
||||
return render_template_string('''<!DOCTYPE html><html>exception</html>''')
|
||||
return render_template_string('''<!DOCTYPE html><html>exception</html>'''), e.code
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host = '192.168.1.249', port = 443, debug=True)
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.3.5
|
||||
1.3.7
|
||||
|
|
|
@ -7,7 +7,7 @@ port=8000
|
|||
#path=test_modules/server,more_modules/server
|
||||
|
||||
# Remote OpenGnsys Service
|
||||
remote=https://192.168.2.10/opengnsys/rest
|
||||
remote=https://192.168.2.1/opengnsys/rest
|
||||
# Alternate OpenGnsys Service (comment out to enable this option)
|
||||
#altremote=https://10.0.2.2/opengnsys/rest
|
||||
|
||||
|
@ -22,12 +22,16 @@ log=DEBUG
|
|||
# This section will be passes on activation to module
|
||||
[ogAdmClient]
|
||||
#path=test_modules/server,more_modules/server
|
||||
## this URL will probably be left equal to the other one, but let's see
|
||||
remote=https://192.168.2.10/opengnsys/rest
|
||||
log=DEBUG
|
||||
|
||||
#servidorAdm=192.168.2.1
|
||||
#puerto=2008
|
||||
remote=https://192.168.2.1/opengnsys/rest
|
||||
log=DEBUG
|
||||
pathinterface=/opt/opengnsys/interfaceAdm
|
||||
urlMenu=https://192.168.2.1/opengnsys/varios/menubrowser.php
|
||||
urlMsg=http://localhost/cgi-bin/httpd-log.sh
|
||||
|
||||
[CloningEngine]
|
||||
remote=https://192.168.2.1/opengnsys/rest
|
||||
log=DEBUG
|
||||
pathinterface=/opt/opengnsys/interfaceAdm
|
||||
urlMenu=https://192.168.2.1/opengnsys/varios/menubrowser.php
|
||||
urlMsg=http://localhost/cgi-bin/httpd-log.sh
|
||||
|
|
|
@ -142,6 +142,10 @@ class REST(object):
|
|||
else:
|
||||
r = requests.post(url, data=data, headers={'content-type': 'application/json'})
|
||||
|
||||
r.raise_for_status()
|
||||
ct = r.headers['Content-Type']
|
||||
if 'application/json' != ct:
|
||||
raise Exception (f'response content-type is not "application/json" but "{ct}"')
|
||||
r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise ConnectionError(e)
|
||||
|
|
|
@ -88,11 +88,32 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
|
|||
Locates witch module will process the message based on path (first folder on url path)
|
||||
"""
|
||||
try:
|
||||
if module is None:
|
||||
raise Exception ({ '_httpcode': 404, '_msg': f'Module {path[0]} not found' })
|
||||
data = module.processServerMessage(path, get_params, post_params, self)
|
||||
self.sendJsonResponse(data)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
n_args = len (e.args)
|
||||
if 0 == n_args:
|
||||
logger.error ('Empty exception raised from message processor for "{}"'.format(path[0]))
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
else:
|
||||
arg0 = e.args[0]
|
||||
if type (arg0) is str:
|
||||
logger.error ('Message processor for "{}" returned exception string "{}"'.format(path[0], str(e)))
|
||||
self.sendJsonError (500, exceptionToMessage(e))
|
||||
elif type (arg0) is dict:
|
||||
if '_httpcode' in arg0:
|
||||
logger.warning ('Message processor for "{}" returned HTTP code "{}" with exception string "{}"'.format(path[0], str(arg0['_httpcode']), str(arg0['_msg'])))
|
||||
self.sendJsonError (arg0['_httpcode'], arg0['_msg'])
|
||||
else:
|
||||
logger.error ('Message processor for "{}" returned exception dict "{}" with no HTTP code'.format(path[0], str(e)))
|
||||
self.sendJsonError (500, exceptionToMessage(e))
|
||||
else:
|
||||
logger.error ('Message processor for "{}" returned non-string and non-dict exception "{}"'.format(path[0], str(e)))
|
||||
self.sendJsonError (500, exceptionToMessage(e))
|
||||
## not reached
|
||||
|
||||
def do_GET(self):
|
||||
module, path, params = self.parseUrl()
|
||||
|
|
|
@ -43,6 +43,8 @@ from opengnsys.workers import ServerWorker
|
|||
from opengnsys.workers import ClientWorker
|
||||
from .log import logger
|
||||
|
||||
PY3_12 = sys.version_info[0:2] >= (3, 12)
|
||||
|
||||
|
||||
def loadModules(controller, client=False):
|
||||
'''
|
||||
|
@ -89,7 +91,11 @@ def loadModules(controller, client=False):
|
|||
for (module_loader, name, ispkg) in pkgutil.iter_modules(paths, modPath + '.'):
|
||||
if ispkg:
|
||||
logger.debug('Found module package {}'.format(name))
|
||||
module_loader.find_module(name).load_module(name)
|
||||
if PY3_12:
|
||||
loader = module_loader.find_spec(name).loader
|
||||
else:
|
||||
loader = module_loader.find_module(name)
|
||||
loader.load_module(name)
|
||||
|
||||
|
||||
if controller.config.has_option('opengnsys', 'path') is True:
|
||||
|
|
|
@ -79,6 +79,9 @@ class Logger(object):
|
|||
def warn(self, message):
|
||||
self.log(WARN, message)
|
||||
|
||||
def warning(self, message):
|
||||
self.log(WARN, message)
|
||||
|
||||
def info(self, message):
|
||||
self.log(INFO, message)
|
||||
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2024 Qindel Formación y Servicios S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
@author: Natalia Serrano, nserrano at qindel dot com
|
||||
"""
|
||||
|
||||
import base64
|
||||
import os
|
||||
import time
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.workers import ogLiveWorker, ThreadWithResult
|
||||
|
||||
class CloningEngineWorker (ogLiveWorker):
|
||||
name = 'CloningEngine' # Module name
|
||||
REST = None # REST object
|
||||
|
||||
def onActivation (self):
|
||||
super().onActivation()
|
||||
logger.info ('onActivation ok')
|
||||
|
||||
def onDeactivation (self):
|
||||
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):
|
||||
sft_src = f'/tmp/CSft-{self.IPlocal}-{par}'
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [dsk, par, sft_src])
|
||||
herror = 0
|
||||
except:
|
||||
herror = 1
|
||||
|
||||
if herror:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
self.muestraMensaje (20)
|
||||
else:
|
||||
if not os.path.exists (sft_src):
|
||||
raise Exception (f'interfaceAdmin({nfn}) returned success but did not create file ({sft_src})')
|
||||
sft_src_contents = Path (sft_src).read_bytes()
|
||||
|
||||
## Envía fichero de inventario al servidor
|
||||
sft_dst = f'/tmp/Ssft-{self.IPlocal}-{par}' ## Nombre que tendra el archivo en el Servidor
|
||||
logger.debug ('sending recibeArchivo to server')
|
||||
res = self.enviaMensajeServidor ('recibeArchivo', { 'nfl': sft_dst, 'contents': base64.b64encode (sft_src_contents).decode ('utf-8') })
|
||||
logger.debug (res)
|
||||
if not res:
|
||||
herror = 12 ## Error de envío de fichero por la red
|
||||
raise Exception ('Ha ocurrido algún problema al enviar un archivo por la red')
|
||||
self.muestraMensaje (19)
|
||||
|
||||
if not sw:
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_InventarioSoftware',
|
||||
'par': par,
|
||||
'sft': sft_dst,
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, 0)
|
||||
|
||||
return {'true':'true'} ## XXX
|
||||
|
||||
def do_CrearImagen (self, post_params):
|
||||
for k in ['dsk', 'par', 'cpt', 'idi', 'nci', 'ipr', 'nfn', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
dsk = post_params['dsk'] ## Disco
|
||||
par = post_params['par'] ## Número de partición
|
||||
cpt = post_params['cpt'] ## Código de la partición
|
||||
idi = post_params['idi'] ## Identificador de la imagen
|
||||
nci = post_params['nci'] ## Nombre canónico de la imagen
|
||||
ipr = post_params['ipr'] ## Ip del repositorio
|
||||
nfn = post_params['nfn']
|
||||
ids = post_params['ids']
|
||||
|
||||
self.muestraMensaje (7)
|
||||
|
||||
if self.InventariandoSoftware (dsk, par, False, 'InventarioSoftware'): ## Crea inventario Software previamente
|
||||
self.muestraMensaje (2)
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [dsk, par, nci, ipr])
|
||||
self.muestraMensaje (9)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
self.muestraMensaje (10)
|
||||
herror = 1
|
||||
else:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
|
||||
self.muestraMenu()
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_CrearImagen',
|
||||
'idi': idi, ## Identificador de la imagen
|
||||
'dsk': dsk, ## Número de disco
|
||||
'par': par, ## Número de partición de donde se creó
|
||||
'cpt': cpt, ## Tipo o código de partición
|
||||
'ipr': ipr, ## Ip del repositorio donde se alojó
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_RestaurarImagen (self, post_params):
|
||||
for k in ['dsk', 'par', 'idi', 'ipr', 'nci', 'ifs', 'ptc', 'nfn', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
dsk = post_params['dsk']
|
||||
par = post_params['par']
|
||||
idi = post_params['idi']
|
||||
ipr = post_params['ipr']
|
||||
nci = post_params['nci']
|
||||
ifs = post_params['ifs']
|
||||
ptc = post_params['ptc'] ## Protocolo de clonación: Unicast, Multicast, Torrent
|
||||
nfn = post_params['nfn']
|
||||
ids = post_params['ids']
|
||||
|
||||
self.muestraMensaje (3)
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [dsk, par, nci, ipr, ptc])
|
||||
self.muestraMensaje (11)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
self.muestraMensaje (12)
|
||||
herror = 1
|
||||
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
|
||||
|
||||
self.muestraMenu()
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_RestaurarImagen',
|
||||
'idi': idi, ## Identificador de la imagen
|
||||
'dsk': dsk, ## Número de disco
|
||||
'par': par, ## Número de partición
|
||||
'ifs': ifs, ## Identificador del perfil software
|
||||
'cfg': cfg, ## Configuración de discos
|
||||
}
|
||||
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):
|
||||
## 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 = {}
|
||||
for k in self.thread_list:
|
||||
thr_status[k] = {
|
||||
'running': self.thread_list[k]['running'],
|
||||
'result': self.thread_list[k]['result'],
|
||||
}
|
||||
return thr_status
|
||||
|
||||
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 ('type(post_params) "{}"'.format (type (post_params)))
|
||||
return self._long_running_job ('CrearImagen', self.do_CrearImagen, args=(post_params,))
|
||||
|
||||
def process_CrearImagenBasica (self, path, get_params, post_params, server):
|
||||
logger.debug ('in process_CrearImagenBasica, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.warning ('this method has been removed')
|
||||
raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
|
||||
|
||||
def process_CrearSoftIncremental (self, path, get_params, post_params, server):
|
||||
logger.debug ('in process_CrearSoftIncremental, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.warning ('this method has been removed')
|
||||
raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
|
||||
|
||||
def process_RestaurarImagen (self, path, get_params, post_params, server):
|
||||
logger.debug ('in process_RestaurarImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
|
||||
return self._long_running_job ('RestaurarImagen', self.do_RestaurarImagen, args=(post_params,))
|
||||
|
||||
def process_RestaurarImagenBasica (self, path, get_params, post_params, server):
|
||||
logger.debug ('in process_RestaurarImagenBasica, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.warning ('this method has been removed')
|
||||
raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
|
||||
|
||||
def process_RestaurarSoftIncremental (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_RestaurarSoftIncremental')
|
||||
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')
|
||||
raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
|
|
@ -106,20 +106,25 @@ class OpenGnSysWorker(ServerWorker):
|
|||
"""
|
||||
Sends OGAgent activation notification to OpenGnsys server
|
||||
"""
|
||||
if os.path.exists ('/scripts/oginit'):
|
||||
## estamos en oglive, este modulo no debe cargarse
|
||||
## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar
|
||||
raise Exception ('Refusing to load within an ogLive image')
|
||||
|
||||
e = None # Error info
|
||||
t = 0 # Count of time
|
||||
# Generate random secret to send on activation
|
||||
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
||||
# Ensure cfg has required configuration variables or an exception will be thrown
|
||||
try:
|
||||
url = self.service.config.get('opengnsys', 'remote')
|
||||
url = self.service.config.get(self.name, 'remote')
|
||||
except NoOptionError as e:
|
||||
logger.error("Configuration error: {}".format(e))
|
||||
raise e
|
||||
self.REST = REST(url)
|
||||
# Execution level ('full' by default)
|
||||
try:
|
||||
self.exec_level = self.service.config.get('opengnsys', 'level')
|
||||
self.exec_level = self.service.config.get(self.name, 'level')
|
||||
except NoOptionError:
|
||||
self.exec_level = 'full'
|
||||
# Get network interfaces until they are active or timeout (5 minutes)
|
||||
|
@ -156,7 +161,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
logger.warn (str (e))
|
||||
# Trying to initialize on alternative server, if defined
|
||||
# (used in "exam mode" from the University of Seville)
|
||||
self.REST = REST(self.service.config.get('opengnsys', 'altremote'))
|
||||
self.REST = REST(self.service.config.get(self.name, 'altremote'))
|
||||
self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'secret': self.random, 'ostype': operations.os_type,
|
||||
'osversion': operations.os_version, 'alt_url': True,
|
||||
|
@ -184,6 +189,8 @@ class OpenGnSysWorker(ServerWorker):
|
|||
if os.path.isfile(new_hosts_file):
|
||||
shutil.copyfile(new_hosts_file, hosts_file)
|
||||
|
||||
logger.info ('onActivation ok')
|
||||
|
||||
def onDeactivation(self):
|
||||
"""
|
||||
Sends OGAgent stopping notification to OpenGnsys server
|
||||
|
|
|
@ -32,154 +32,182 @@
|
|||
@author: Natalia Serrano, nserrano at qindel dot com
|
||||
"""
|
||||
|
||||
|
||||
import base64
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import string
|
||||
import threading
|
||||
import time
|
||||
#import threading
|
||||
#import time
|
||||
import subprocess
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from configparser import NoOptionError
|
||||
from opengnsys import REST, operations, VERSION
|
||||
#from opengnsys import operations
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.workers import ServerWorker
|
||||
from opengnsys.workers import ogLiveWorker
|
||||
|
||||
# Check authorization header decorator
|
||||
def check_secret(fnc):
|
||||
def check_secret (fnc):
|
||||
"""
|
||||
Decorator to check for received secret key and raise exception if it isn't valid.
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
return fnc(*args, **kwargs)
|
||||
def wrapper (*args, **kwargs):
|
||||
return fnc (*args, **kwargs)
|
||||
#try:
|
||||
# this, path, get_params, post_params, server = args
|
||||
# # Accept "status" operation with no arguments or any function with Authorization header
|
||||
# if fnc.__name__ == 'process_status' and not get_params:
|
||||
# return fnc(*args, **kwargs)
|
||||
# return fnc (*args, **kwargs)
|
||||
# elif this.random == server.headers['Authorization']:
|
||||
# return fnc(*args, **kwargs)
|
||||
# return fnc (*args, **kwargs)
|
||||
# else:
|
||||
# raise Exception('Unauthorized operation')
|
||||
# raise Exception ('Unauthorized operation')
|
||||
#except Exception as e:
|
||||
# logger.error(str(e))
|
||||
# raise Exception(e)
|
||||
# logger.error (str (e))
|
||||
# raise Exception (e)
|
||||
|
||||
return wrapper
|
||||
|
||||
class ogAdmClientWorker(ServerWorker):
|
||||
class ogAdmClientWorker (ogLiveWorker):
|
||||
name = 'ogAdmClient' # Module name
|
||||
interface = None # Bound interface for OpenGnsys
|
||||
#interface = None # Bound interface for OpenGnsys (el otro modulo lo usa para obtener .ip y .mac
|
||||
REST = None # REST object
|
||||
random = None # Random string for secure connections
|
||||
length = 32 # Random string length
|
||||
|
||||
pathinterface = None
|
||||
IPlocal = None
|
||||
idordenador = None
|
||||
nombreordenador = None
|
||||
cache = None
|
||||
idproautoexec = None
|
||||
idcentro = None
|
||||
idaula = None
|
||||
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
|
||||
"""
|
||||
logger.debug('onDeactivation')
|
||||
logger.debug ('onDeactivation')
|
||||
|
||||
def processClientMessage(self, message, data):
|
||||
logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data))
|
||||
#def processClientMessage (self, message, data):
|
||||
# logger.debug ('Got OpenGnsys message from client: {}, data {}'.format (message, data))
|
||||
|
||||
def onLogin(self, data):
|
||||
logger.warn('in onLogin, should not happen')
|
||||
#def onLogin (self, data):
|
||||
# logger.warning ('in onLogin, should not happen')
|
||||
|
||||
def onLogout(self, user):
|
||||
logger.warn('in onLogout, should not happen')
|
||||
|
||||
def process_ogclient(self, path, get_params, post_params, server):
|
||||
"""
|
||||
This method can be overridden to provide your own message processor, or better you can
|
||||
implement a method that is called exactly as "process_" + path[0] (module name has been removed from path
|
||||
array) and this default processMessage will invoke it
|
||||
* Example:
|
||||
Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z
|
||||
The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this:
|
||||
module.processMessage(["mazinger","Z"], get_params, post_params)
|
||||
|
||||
This method will process "mazinger", and look for a "self" method that is called "process_mazinger",
|
||||
and invoke it this way:
|
||||
return self.process_mazinger(["Z"], get_params, post_params)
|
||||
|
||||
In the case path is empty (that is, the path is composed only by the module name, like in
|
||||
"http://example.com/Sample", the "process" method will be invoked directly
|
||||
|
||||
The methods must return data that can be serialized to json (i.e. Objects are not serializable to json,
|
||||
basic type are)
|
||||
"""
|
||||
if not path:
|
||||
return "ok"
|
||||
try:
|
||||
operation = getattr(self, 'ogclient_' + path[0])
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(path[0]))
|
||||
return operation(path[1:], get_params, post_params)
|
||||
#def onLogout (self, user):
|
||||
# logger.warning ('in onLogout, should not happen')
|
||||
|
||||
@check_secret
|
||||
def process_status(self, path, get_params, post_params, server):
|
||||
return {'ogAdmClient': 'in process_status'}
|
||||
def process_status (self, path, get_params, post_params, server):
|
||||
return {self.name: 'in process_status'} ## XXX
|
||||
|
||||
#@check_secret
|
||||
#def process_reboot (self, path, get_params, post_params, server):
|
||||
# """
|
||||
# Launches a system reboot operation
|
||||
# :param path:
|
||||
# :param get_params:
|
||||
# :param post_params:
|
||||
# :param server: authorization header
|
||||
# :return: JSON object {"op": "launched"}
|
||||
# """
|
||||
# logger.debug ('Received reboot operation')
|
||||
|
||||
# # Rebooting thread
|
||||
# def rebt():
|
||||
# operations.reboot()
|
||||
# threading.Thread (target=rebt).start()
|
||||
# return {'op': 'launched'}
|
||||
|
||||
#@check_secret
|
||||
#def process_poweroff (self, path, get_params, post_params, server):
|
||||
# """
|
||||
# Launches a system power off operation
|
||||
# :param path:
|
||||
# :param get_params:
|
||||
# :param post_params:
|
||||
# :param server: authorization header
|
||||
# :return: JSON object {"op": "launched"}
|
||||
# """
|
||||
# logger.debug ('Received poweroff operation')
|
||||
|
||||
# # Powering off thread
|
||||
# def pwoff():
|
||||
# time.sleep (2)
|
||||
# operations.poweroff()
|
||||
# threading.Thread (target=pwoff).start()
|
||||
# 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_reboot(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Launches a system reboot operation
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server: authorization header
|
||||
:return: JSON object {"op": "launched"}
|
||||
"""
|
||||
logger.debug('Received reboot operation')
|
||||
|
||||
# Rebooting thread
|
||||
def rebt():
|
||||
operations.reboot()
|
||||
threading.Thread(target=rebt).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
@check_secret
|
||||
def process_poweroff(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Launches a system power off operation
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params:
|
||||
:param server: authorization header
|
||||
:return: JSON object {"op": "launched"}
|
||||
"""
|
||||
logger.debug('Received poweroff operation')
|
||||
|
||||
# Powering off thread
|
||||
def pwoff():
|
||||
time.sleep(2)
|
||||
operations.poweroff()
|
||||
threading.Thread(target=pwoff).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
@check_secret
|
||||
def process_logoff(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_logoff, should not happen')
|
||||
|
||||
@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)))
|
||||
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'}
|
||||
|
@ -191,95 +219,25 @@ class ogAdmClientWorker(ServerWorker):
|
|||
|
||||
|
||||
|
||||
#def process_client_popup(self, params):
|
||||
# logger.warn('in process_client_popup')
|
||||
|
||||
|
||||
## process_* are invoked from opengnsys/httpserver.py:99 "data = module.processServerMessage(path, get_params, post_params, self)" (via opengnsys/workers/server_worker.py)
|
||||
## process_client_* are invoked from opengnsys/service.py:123 "v.processClientMessage(message, json.loads(data))" (via opengnsys/workers/server_worker.py)
|
||||
## process_* are invoked from opengnsys/httpserver.py:99 "data = module.processServerMessage (path, get_params, post_params, self)" (via opengnsys/workers/server_worker.py)
|
||||
## process_client_* are invoked from opengnsys/service.py:123 "v.processClientMessage (message, json.loads (data))" (via opengnsys/workers/server_worker.py)
|
||||
|
||||
|
||||
def interfaceAdmin (self, method, parametros=[]):
|
||||
exe = '{}/{}'.format (self.pathinterface, method)
|
||||
devel_bash_prefix = '''
|
||||
PATH=/opt/opengnsys/scripts/:$PATH;
|
||||
for I in /opt/opengnsys/lib/engine/bin/*.lib; do source $I; done;
|
||||
for i in $(declare -F |cut -f3 -d" "); do export -f $i; done;
|
||||
'''
|
||||
if parametros:
|
||||
proc = ['bash', '-c', '{} {} {}'.format (devel_bash_prefix, exe, ' '.join (parametros))]
|
||||
else:
|
||||
proc = ['bash', '-c', '{} {}'.format (devel_bash_prefix, exe)]
|
||||
logger.debug ('subprocess.run ("{}", capture_output=True)'.format (proc))
|
||||
return subprocess.run (proc, capture_output=True).stdout.strip().decode ('utf-8')
|
||||
|
||||
def tomaIPlocal(self):
|
||||
try:
|
||||
self.IPlocal = self.interfaceAdmin ('getIpAddress');
|
||||
logger.info ('local IP is "{}"'.format (self.IPlocal))
|
||||
except Exception as e:
|
||||
logger.error (e)
|
||||
logger.error ('No se ha podido recuperar la dirección IP del cliente')
|
||||
return False
|
||||
return True
|
||||
|
||||
def LeeConfiguracion(self):
|
||||
parametroscfg = self.interfaceAdmin ('getConfiguration') ## Configuración de los Sistemas Operativos del cliente
|
||||
logger.debug ('parametroscfg ({})'.format (parametroscfg))
|
||||
return (parametroscfg)
|
||||
|
||||
def enviaMensajeServidor(self, path, obj={}):
|
||||
obj['iph'] = self.IPlocal ## Ip del ordenador
|
||||
obj['ido'] = self.idordenador ## Identificador del ordenador
|
||||
obj['npc'] = self.nombreordenador ## Nombre del ordenador
|
||||
obj['idc'] = self.idcentro ## Identificador del centro
|
||||
obj['ida'] = self.idaula ## Identificador del aula
|
||||
|
||||
res = self.REST.sendMessage (path, obj)
|
||||
|
||||
if (type(res) is not dict):
|
||||
#logger.error ('No se ha podido establecer conexión con el Servidor de Administración') ## Error de conexión con el servidor
|
||||
logger.debug (f'res ({res})')
|
||||
logger.error ('Error al enviar trama ***send() fallo')
|
||||
return False
|
||||
|
||||
return res
|
||||
|
||||
def ejecutaArchivo(self,fn):
|
||||
def ejecutaArchivo (self,fn):
|
||||
logger.debug ('fn ({})'.format (fn))
|
||||
|
||||
## TODO need to understand this code (ogAdmClient.c:2111) before translating it to python
|
||||
## in a function called "ejecutaArchivo" I'd expect a file to be run, however there's only a call to gestionaTrama() that I don't know where it leads to
|
||||
#char* buffer,*lineas[MAXIMAS_LINEAS];
|
||||
#int i,numlin;
|
||||
#char modulo[] = "ejecutaArchivo()";
|
||||
|
||||
#buffer = leeArchivo(filecmd);
|
||||
#if (buffer):
|
||||
# numlin = splitCadena(lineas, buffer, '@');
|
||||
# initParametros(ptrTrama,0);
|
||||
# for (i = 0; i < numlin; i++) {
|
||||
# if(strlen(lineas[i])>0){
|
||||
# strcpy(ptrTrama->parametros,lineas[i]);
|
||||
# if(!gestionaTrama(ptrTrama)){ // Análisis de la trama
|
||||
# errorLog(modulo,39,FALSE);
|
||||
# //return(FALSE);
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#liberaMemoria(buffer);
|
||||
|
||||
## let's test something, assuming that in the "file" there's not just some bash, but a sequence of parameters such as "nfn=Function\rparam1=foo\rparam2=bar"
|
||||
## in the "file" there's not just some bash, but a sequence of parameters such as "nfn=Function\rparam1=foo\rparam2=bar"
|
||||
buffer = subprocess.run (['cat', fn], capture_output=True).stdout.strip().decode ('utf-8')
|
||||
logger.debug ('buffer ({})'.format (buffer.replace('\r', '\\r'))) ## change \r so as not to mess with the log
|
||||
logger.debug ('buffer ({})'.format (buffer.replace ('\r', '\\r'))) ## change \r so as not to mess with the log
|
||||
if buffer:
|
||||
for l in buffer.split('@'):
|
||||
if not len(l): continue
|
||||
for l in buffer.split ('@'):
|
||||
if not len (l): continue
|
||||
logger.debug ('line ({})'.format (l))
|
||||
## 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:
|
||||
post_params = {}
|
||||
for param in l.split("\r"):
|
||||
k, v = param.split('=')
|
||||
for param in l.split ("\r"):
|
||||
k, v = param.split ('=')
|
||||
post_params[k] = v
|
||||
logger.debug ('post_params "{}"'.format (post_params))
|
||||
|
||||
|
@ -297,13 +255,18 @@ class ogAdmClientWorker(ServerWorker):
|
|||
logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
break
|
||||
|
||||
def inclusionCliente(self):
|
||||
def inclusionCliente (self):
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
|
||||
logger.warning ('Ha ocurrido algún problema en el proceso de inclusión del cliente')
|
||||
logger.error ('LeeConfiguracion() failed')
|
||||
return False
|
||||
res = self.enviaMensajeServidor ('InclusionCliente', { 'cfg': cfg })
|
||||
logger.debug ('res ({})'.format (res))
|
||||
|
||||
## RESPUESTA_InclusionCliente
|
||||
if (type(res) is not dict or 0 == res['res']) :
|
||||
if (type (res) is not dict or 0 == res['res']) :
|
||||
logger.error ('Ha ocurrido algún problema en el proceso de inclusión del cliente')
|
||||
return False
|
||||
|
||||
|
@ -320,34 +283,26 @@ class ogAdmClientWorker(ServerWorker):
|
|||
|
||||
return True
|
||||
|
||||
def cuestionCache(self):
|
||||
def cuestionCache (self):
|
||||
return True ## ogAdmClient.c:425
|
||||
#>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
#try:
|
||||
# self.interfaceAdmin ('procesaCache', [ self.cache ]);
|
||||
#except Exception as e:
|
||||
# logger.error ('Ha habido algún problerma al procesar la caché')
|
||||
# return False
|
||||
#
|
||||
#return True
|
||||
|
||||
def autoexecCliente(self):
|
||||
def autoexecCliente (self):
|
||||
res = self.enviaMensajeServidor ('AutoexecCliente', { 'exe': self.idproautoexec })
|
||||
logger.debug ('res ({})'.format (res))
|
||||
|
||||
if (type(res) is not dict):
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
|
||||
logger.error ('Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración')
|
||||
return False
|
||||
|
||||
## RESPUESTA_AutoexecCliente
|
||||
if (type(res) is not dict or 0 == res['res']) :
|
||||
if (type (res) is not dict or 0 == res['res']) :
|
||||
logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
return False
|
||||
|
||||
logger.info (res)
|
||||
res = self.enviaMensajeServidor ('enviaArchivo', { 'nfl': res['nfl'] })
|
||||
if (type(res) is not dict):
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
|
||||
logger.error ('Ha ocurrido algún problema al recibir un archivo por la red')
|
||||
return False
|
||||
|
@ -356,16 +311,16 @@ class ogAdmClientWorker(ServerWorker):
|
|||
fileautoexec = '/tmp/_autoexec_{}'.format (self.IPlocal)
|
||||
logger.debug ('fileautoexec ({})'.format (fileautoexec))
|
||||
with open (fileautoexec, 'w') as fd:
|
||||
fd.write (base64.b64decode (res['contents']).decode('utf-8'))
|
||||
fd.write (base64.b64decode (res['contents']).decode ('utf-8'))
|
||||
|
||||
self.ejecutaArchivo (fileautoexec);
|
||||
self.ejecutaArchivo (fileautoexec)
|
||||
|
||||
return True
|
||||
|
||||
def comandosPendientes(self):
|
||||
def comandosPendientes (self):
|
||||
while (True):
|
||||
res = self.enviaMensajeServidor ('ComandosPendientes') ## receives just one command
|
||||
if (type(res) is not dict):
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
|
||||
logger.error ('Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración')
|
||||
return False
|
||||
|
@ -375,7 +330,7 @@ class ogAdmClientWorker(ServerWorker):
|
|||
break
|
||||
|
||||
## TODO manage the rest of cases... we might have to do something similar to ejecutaArchivo
|
||||
#if(!gestionaTrama(ptrTrama)){ // Análisis de la trama
|
||||
#if (!gestionaTrama (ptrTrama)){ // Análisis de la trama
|
||||
# logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
# return False
|
||||
#}
|
||||
|
@ -384,31 +339,11 @@ class ogAdmClientWorker(ServerWorker):
|
|||
|
||||
return True
|
||||
|
||||
def cargaPaginaWeb(url=None):
|
||||
if (not url): url = self.urlMenu
|
||||
os.system ('pkill -9 browser');
|
||||
|
||||
#p = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', url])
|
||||
p = subprocess.Popen (['/usr/bin/xeyes'])
|
||||
try:
|
||||
p.wait (2) ## if the process dies before 2 seconds...
|
||||
logger.error ('Error al ejecutar la llamada a la interface de administración')
|
||||
logger.error ('Error en la creación del proceso hijo')
|
||||
logger.error ('return code "{}"'.format (p.returncode))
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def muestraMenu(self):
|
||||
self.cargaPaginaWeb()
|
||||
|
||||
def procesaComandos(self):
|
||||
def procesaComandos (self):
|
||||
res = self.enviaMensajeServidor ('DisponibilidadComandos', { 'tpc': 'OPG' }) ## Activar disponibilidad
|
||||
logger.debug ('res ({})'.format (res))
|
||||
|
||||
if (type(res) is not dict):
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos interactivos al Servidor de Administración')
|
||||
return False
|
||||
|
||||
|
@ -417,44 +352,22 @@ class ogAdmClientWorker(ServerWorker):
|
|||
## we now return true and the outer agent code gets to wait for requests from outside
|
||||
## TODO thing is, ogAdmClient always calls comandosPendientes() after every received request. How do we do that here?
|
||||
#
|
||||
#ptrTrama=recibeMensaje(&socket_c);
|
||||
#if(!ptrTrama){
|
||||
# errorLog(modulo,46,FALSE); 'Ha ocurrido algún problema al recibir un comando interactivo desde el Servidor de Administración'
|
||||
#ptrTrama=recibeMensaje (&socket_c);
|
||||
#if (!ptrTrama){
|
||||
# errorLog (modulo,46,FALSE); 'Ha ocurrido algún problema al recibir un comando interactivo desde el Servidor de Administración'
|
||||
# return;
|
||||
#}
|
||||
#close(socket_c);
|
||||
#if(!gestionaTrama(ptrTrama)){ // Análisis de la trama
|
||||
# errorLog(modulo,39,FALSE); 'Ha ocurrido algún problema al procesar la trama recibida'
|
||||
#close (socket_c);
|
||||
#if (!gestionaTrama (ptrTrama)){ // Análisis de la trama
|
||||
# errorLog (modulo,39,FALSE); 'Ha ocurrido algún problema al procesar la trama recibida'
|
||||
# return;
|
||||
#}
|
||||
#if(!comandosPendientes(ptrTrama)){
|
||||
# errorLog(modulo,42,FALSE); 'Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración'
|
||||
#if (!comandosPendientes (ptrTrama)){
|
||||
# errorLog (modulo,42,FALSE); 'Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración'
|
||||
#}
|
||||
|
||||
def onActivation(self):
|
||||
"""
|
||||
Sends OGAgent activation notification to OpenGnsys server
|
||||
"""
|
||||
# Generate random secret to send on activation
|
||||
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
||||
# Ensure cfg has required configuration variables or an exception will be thrown
|
||||
try:
|
||||
url = self.service.config.get('ogAdmClient', 'remote')
|
||||
loglevel = self.service.config.get('ogAdmClient', 'log')
|
||||
#servidorAdm = self.service.config.get('ogAdmClient', 'servidorAdm')
|
||||
#puerto = self.service.config.get('ogAdmClient', 'puerto')
|
||||
self.pathinterface = self.service.config.get('ogAdmClient', 'pathinterface')
|
||||
urlMenu = self.service.config.get('ogAdmClient', 'urlMenu')
|
||||
#urlMsg = self.service.config.get('ogAdmClient', 'urlMsg')
|
||||
logger.setLevel(loglevel)
|
||||
except NoOptionError as e:
|
||||
logger.error("Configuration error: {}".format(e))
|
||||
raise e
|
||||
self.REST = REST(url)
|
||||
|
||||
if (not self.tomaIPlocal()):
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
def onActivation (self):
|
||||
super().onActivation()
|
||||
logger.info ('Inicio de sesion')
|
||||
logger.info ('Abriendo sesión en el servidor de Administración')
|
||||
if (not self.inclusionCliente()):
|
||||
|
@ -462,16 +375,16 @@ class ogAdmClientWorker(ServerWorker):
|
|||
|
||||
logger.info ('Cliente iniciado')
|
||||
logger.info ('Procesando caché')
|
||||
if (not self.cuestionCache()):
|
||||
if not self.cuestionCache():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
if (self.idproautoexec > 0):
|
||||
if self.idproautoexec > 0:
|
||||
logger.info ('Ejecución de archivo Autoexec')
|
||||
if (not self.autoexecCliente()):
|
||||
if not self.autoexecCliente():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
logger.info ('Procesa comandos pendientes')
|
||||
if (not self.comandosPendientes()):
|
||||
if not self.comandosPendientes():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
logger.info ('Acciones pendientes procesadas')
|
||||
|
@ -479,62 +392,44 @@ class ogAdmClientWorker(ServerWorker):
|
|||
self.muestraMenu()
|
||||
self.procesaComandos()
|
||||
|
||||
def process_NoComandosPtes(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_NoComandosPtes')
|
||||
logger.info ('onActivation ok')
|
||||
|
||||
def process_Actualizar(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Actualizar')
|
||||
## curl --insecure https://192.168.1.249:8000/ogAdmClient/Actualizar
|
||||
def process_Actualizar (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Actualizar')
|
||||
|
||||
def process_Purgar(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Purgar')
|
||||
def process_Purgar (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Purgar')
|
||||
|
||||
def process_ConsolaRemota(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_ConsolaRemota')
|
||||
def process_ConsolaRemota (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_ConsolaRemota')
|
||||
|
||||
def process_Sondeo(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Sondeo')
|
||||
def process_Sondeo (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Sondeo')
|
||||
|
||||
def process_Arrancar(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Arrancar')
|
||||
def process_Arrancar (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Arrancar')
|
||||
|
||||
def process_Apagar(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Apagar')
|
||||
def process_Apagar (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Apagar')
|
||||
|
||||
def process_Reiniciar(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Reiniciar')
|
||||
def process_Reiniciar (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Reiniciar')
|
||||
|
||||
def process_IniciarSesion(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_IniciarSesion')
|
||||
def process_IniciarSesion (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_IniciarSesion')
|
||||
|
||||
def process_CrearImagen(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_CrearImagen')
|
||||
def process_Configurar (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_Configurar')
|
||||
|
||||
def process_CrearImagenBasica(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_CrearImagenBasica')
|
||||
def process_EjecutarScript (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_EjecutarScript')
|
||||
|
||||
def process_CrearSoftIncremental(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_CrearSoftIncremental')
|
||||
def process_InventarioHardware (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_InventarioHardware')
|
||||
|
||||
def process_RestaurarImagen(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_RestaurarImagen')
|
||||
def process_InventarioSoftware (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_InventarioSoftware')
|
||||
|
||||
def process_RestaurarImagenBasica(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_RestaurarImagenBasica')
|
||||
|
||||
def process_RestaurarSoftIncremental(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_RestaurarSoftIncremental')
|
||||
|
||||
def process_Configurar(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_Configurar')
|
||||
|
||||
def process_EjecutarScript(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_EjecutarScript')
|
||||
|
||||
def process_InventarioHardware(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_InventarioHardware')
|
||||
|
||||
def process_InventarioSoftware(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_InventarioSoftware')
|
||||
|
||||
def process_EjecutaComandosPendientes(self, path, get_params, post_params, server):
|
||||
logger.warn('in process_EjecutaComandosPendientes')
|
||||
def process_EjecutaComandosPendientes (self, path, get_params, post_params, server):
|
||||
logger.warning ('in process_EjecutaComandosPendientes')
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
from .server_worker import ServerWorker
|
||||
from .client_worker import ClientWorker
|
||||
from .oglive_worker import ogLiveWorker, ThreadWithResult
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2024 Qindel Formación y Servicios S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
@author: Natalia Serrano, nserrano at qindel dot com
|
||||
"""
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from configparser import NoOptionError
|
||||
from opengnsys import REST
|
||||
from opengnsys.log import logger
|
||||
from .server_worker import ServerWorker
|
||||
|
||||
## https://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread
|
||||
class ThreadWithResult (threading.Thread):
|
||||
def run (self):
|
||||
try:
|
||||
self.result = None
|
||||
if self._target is not None:
|
||||
try:
|
||||
self.result = self._target (*self._args, **self._kwargs)
|
||||
except Exception as e:
|
||||
self.result = { 'res': 2, 'der': f'got exception: ({e})' } ## res=2 as defined in ogAdmClient.c:2048
|
||||
finally:
|
||||
# Avoid a refcycle if the thread is running a function with an argument that has a member that points to the thread.
|
||||
del self._target, self._args, self._kwargs
|
||||
|
||||
class ogLiveWorker(ServerWorker):
|
||||
thread_list = {}
|
||||
|
||||
def interfaceAdmin (self, method, parametros=[]):
|
||||
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
|
||||
devel_bash_prefix = '''
|
||||
PATH=/opt/opengnsys/scripts/:$PATH;
|
||||
for I in /opt/opengnsys/lib/engine/bin/*.lib; do source $I; done;
|
||||
for i in $(declare -F |cut -f3 -d" "); do export -f $i; done;
|
||||
'''
|
||||
|
||||
if parametros:
|
||||
proc = ['bash', '-c', '{} set -x; bash -x {} {}; set +x'.format (devel_bash_prefix, exe, ' '.join (parametros))]
|
||||
else:
|
||||
proc = ['bash', '-c', '{} set -x; bash -x {}; set +x'.format (devel_bash_prefix, exe)]
|
||||
logger.debug ('subprocess.run ("{}", capture_output=True)'.format (proc))
|
||||
p = subprocess.run (proc, capture_output=True)
|
||||
## DEBUG
|
||||
logger.info (f'stdout follows:')
|
||||
for l in p.stdout.strip().decode ('utf-8').splitlines():
|
||||
logger.info (f' {l}')
|
||||
logger.info (f'stderr follows:')
|
||||
for l in p.stderr.strip().decode ('utf-8').splitlines():
|
||||
logger.info (f' {l}')
|
||||
## /DEBUG
|
||||
if 0 != p.returncode:
|
||||
cmd_txt = ' '.join (proc)
|
||||
logger.error (f'command ({cmd_txt}) failed, stderr follows:')
|
||||
for l in p.stderr.strip().decode ('utf-8').splitlines():
|
||||
logger.error (f' {l}')
|
||||
raise Exception (f'command ({cmd_txt}) failed, see log for details')
|
||||
return p.stdout.strip().decode ('utf-8')
|
||||
|
||||
def tomaIPlocal (self):
|
||||
try:
|
||||
self.IPlocal = self.interfaceAdmin ('getIpAddress')
|
||||
except Exception as e:
|
||||
logger.error (e)
|
||||
logger.error ('No se ha podido recuperar la dirección IP del cliente')
|
||||
return False
|
||||
logger.info ('local IP is "{}"'.format (self.IPlocal))
|
||||
return True
|
||||
|
||||
def enviaMensajeServidor (self, path, obj={}):
|
||||
obj['iph'] = self.IPlocal ## Ip del ordenador
|
||||
obj['ido'] = self.idordenador ## Identificador del ordenador
|
||||
obj['npc'] = self.nombreordenador ## Nombre del ordenador
|
||||
obj['idc'] = self.idcentro ## Identificador del centro
|
||||
obj['ida'] = self.idaula ## Identificador del aula
|
||||
|
||||
res = self.REST.sendMessage ('/'.join ([self.name, path]), obj)
|
||||
|
||||
if (type (res) is not dict):
|
||||
#logger.error ('No se ha podido establecer conexión con el Servidor de Administración') ## Error de conexión con el servidor
|
||||
logger.debug (f'res ({res})')
|
||||
logger.error ('Error al enviar trama ***send() fallo')
|
||||
return False
|
||||
|
||||
return res
|
||||
|
||||
def cargaPaginaWeb (self, url=None):
|
||||
if (not url): url = self.urlMenu
|
||||
os.system ('pkill -9 browser')
|
||||
|
||||
p = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', url])
|
||||
try:
|
||||
p.wait (2) ## if the process dies before 2 seconds...
|
||||
logger.error ('Error al ejecutar la llamada a la interface de administración')
|
||||
logger.error ('Error en la creación del proceso hijo')
|
||||
logger.error ('return code "{}"'.format (p.returncode))
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def muestraMenu (self):
|
||||
self.cargaPaginaWeb()
|
||||
|
||||
def muestraMensaje (self, idx):
|
||||
self.cargaPaginaWeb (f'{self.urlMsg}?idx={idx}')
|
||||
|
||||
def LeeConfiguracion (self):
|
||||
try:
|
||||
parametroscfg = self.interfaceAdmin ('getConfiguration') ## Configuración de los Sistemas Operativos del cliente
|
||||
except Exception as e:
|
||||
logger.error (e)
|
||||
logger.error ('No se ha podido recuperar la dirección IP del cliente')
|
||||
return None
|
||||
logger.debug ('parametroscfg ({})'.format (parametroscfg))
|
||||
return parametroscfg
|
||||
|
||||
def onActivation (self):
|
||||
if not os.path.exists ('/scripts/oginit'):
|
||||
## no estamos en oglive, este modulo no debe cargarse
|
||||
## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar
|
||||
raise Exception ('Refusing to load within an operating system')
|
||||
|
||||
self.pathinterface = None
|
||||
self.IPlocal = None ## Ip del ordenador
|
||||
self.idordenador = None ## Identificador del ordenador
|
||||
self.nombreordenador = None ## Nombre del ordenador
|
||||
self.cache = None
|
||||
self.idproautoexec = None
|
||||
self.idcentro = None ## Identificador del centro
|
||||
self.idaula = None ## Identificador del aula
|
||||
|
||||
try:
|
||||
url = self.service.config.get (self.name, 'remote')
|
||||
loglevel = self.service.config.get (self.name, 'log')
|
||||
self.pathinterface = self.service.config.get (self.name, 'pathinterface')
|
||||
self.urlMenu = self.service.config.get (self.name, 'urlMenu')
|
||||
self.urlMsg = self.service.config.get (self.name, 'urlMsg')
|
||||
except NoOptionError as e:
|
||||
logger.error ("Configuration error: {}".format (e))
|
||||
raise e
|
||||
logger.setLevel (loglevel)
|
||||
self.REST = REST (url)
|
||||
|
||||
if not self.tomaIPlocal():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
|
@ -96,8 +96,8 @@ class ServerWorker(object):
|
|||
return self.process(getParams, postParams, server)
|
||||
try:
|
||||
operation = getattr(self, 'process_' + path[0])
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(path[0]))
|
||||
except:
|
||||
raise Exception ({ '_httpcode': 404, '_msg': f'{path[0]}: method not found' })
|
||||
|
||||
return operation(path[1:], getParams, postParams, server)
|
||||
|
||||
|
|
Loading…
Reference in New Issue