Compare commits

..

No commits in common. "5cf45c580eb2e01e809e4131137237928cee9057" and "9d6596668f84e4f8141954904bd15e9dd83bc416" have entirely different histories.

3 changed files with 63 additions and 261 deletions

View File

@ -3,7 +3,6 @@ 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
@ -15,7 +14,8 @@ logging.basicConfig(level=logging.INFO)
@app.route('/opengnsys/rest/ogagent/<cucu>', methods=['POST'])
def og_agent(cucu):
logging.info(f'{request.get_json()}')
c = request
logging.info(f"{request.get_json()}")
return jsonify({})
@ -24,19 +24,20 @@ def og_agent(cucu):
@app.route('/opengnsys/rest/__ogAdmClient/InclusionCliente', methods=['POST'])
def inclusion_cliente():
logging.info(f'{request.get_json()}')
c = request
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");
@ -80,18 +81,19 @@ def _recorreProcedimientos(parametros, fileexe, idp):
@app.route('/opengnsys/rest/__ogAdmClient/AutoexecCliente', methods=['POST'])
def autoexec_client():
logging.info(f'{request.get_json()}')
c = request
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,14 +106,13 @@ def autoexec_client():
@app.route('/opengnsys/rest/__ogAdmClient/enviaArchivo', methods=['POST'])
def envia_archivo():
logging.info(f'{request.get_json()}')
c = request
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})")
contents = subprocess.run (['cat', nfl], capture_output=True).stdout
b64 = base64.b64encode (contents).decode ('utf-8')
return jsonify({'contents': b64})
return jsonify({'contents': subprocess.run (['cat', nfl], capture_output=True).stdout.decode('utf-8')})
def clienteExistente(iph):
## esto queda totalmente del lado del servidor, no lo implemento en python
@ -131,16 +132,17 @@ def buscaComandos(ido):
@app.route('/opengnsys/rest/__ogAdmClient/ComandosPendientes', methods=['POST'])
def comandos_pendientes():
logging.info(f'{request.get_json()}')
c = request
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,50 +154,44 @@ def comandos_pendientes():
@app.route('/opengnsys/rest/__ogAdmClient/DisponibilidadComandos', methods=['POST'])
def disponibilidad_comandos():
logging.info(f'{request.get_json()}')
c = request
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/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/__ogAdmClient/<cucu>', methods=['GET', 'POST'])
@app.route('/opengnsys/rest/__ogAdmClient/<cucu>', methods=['POST'])
def 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)
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'")
return jsonify({'cucu': j['cucu']})
@app.errorhandler(404)
def _page_not_found(e):
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
return render_template_string('''<!DOCTYPE html><html>not found</html>''')
@app.errorhandler(500)
def _internal_server_error(e):
return render_template_string('''<!DOCTYPE html><html>err</html>'''), e.code
return render_template_string('''<!DOCTYPE html><html>err</html>''')
@app.errorhandler(Exception)
def _exception(e):
print(e)
return render_template_string('''<!DOCTYPE html><html>exception</html>'''), e.code
return render_template_string('''<!DOCTYPE html><html>exception</html>''')
if __name__ == '__main__':
app.run(host = '192.168.1.249', port = 443, debug=True)

View File

@ -142,10 +142,6 @@ 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)

View File

@ -35,7 +35,7 @@
import base64
import os
#import random
import random
import shutil
import string
import threading
@ -44,7 +44,6 @@ import subprocess
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
from configparser import NoOptionError
from opengnsys import REST, operations, VERSION
@ -75,89 +74,19 @@ def check_secret (fnc):
class ogAdmClientWorker (ServerWorker):
name = 'ogAdmClient' # Module name
#interface = None # Bound interface for OpenGnsys (el otro modulo lo usa para obtener .ip y .mac
interface = None # Bound interface for OpenGnsys
REST = None # REST object
#random = None # Random string for secure connections
#length = 32 # Random string length
random = None # Random string for secure connections
length = 32 # Random string length
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",
]
pathinterface = None
IPlocal = None
idordenador = None
nombreordenador = None
cache = None
idproautoexec = None
idcentro = None
idaula = None
def onDeactivation (self):
"""
@ -273,7 +202,6 @@ class ogAdmClientWorker (ServerWorker):
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;
@ -284,28 +212,20 @@ class ogAdmClientWorker (ServerWorker):
else:
proc = ['bash', '-c', '{} {}'.format (devel_bash_prefix, exe)]
logger.debug ('subprocess.run ("{}", capture_output=True)'.format (proc))
p = subprocess.run (proc, capture_output=True)
if 0 != p.returncode:
raise Exception ('command failed') ## TODO mejorar este mensaje
return p.stdout.strip().decode ('utf-8')
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
logger.info ('local IP is "{}"'.format (self.IPlocal))
return True
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
def LeeConfiguracion(self):
parametroscfg = self.interfaceAdmin ('getConfiguration') ## Configuración de los Sistemas Operativos del cliente
logger.debug ('parametroscfg ({})'.format (parametroscfg))
return parametroscfg
@ -380,9 +300,6 @@ class ogAdmClientWorker (ServerWorker):
def inclusionCliente (self):
cfg = self.LeeConfiguracion()
if not cfg:
logger.error ('LeeConfiguracion() failed')
return False
res = self.enviaMensajeServidor ('InclusionCliente', { 'cfg': cfg })
logger.debug ('res ({})'.format (res))
@ -468,11 +385,11 @@ class ogAdmClientWorker (ServerWorker):
return True
def cargaPaginaWeb (self, url=None):
def cargaPaginaWeb (url=None):
if (not url): url = self.urlMenu
os.system ('pkill -9 browser');
#p = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', url]) ## TODO
#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...
@ -488,9 +405,6 @@ class ogAdmClientWorker (ServerWorker):
def muestraMenu (self):
self.cargaPaginaWeb()
def muestraMensaje (self, idx):
self.cargaPaginaWeb (f'{self.urlMsg}?idx={idx}')
def procesaComandos (self):
res = self.enviaMensajeServidor ('DisponibilidadComandos', { 'tpc': 'OPG' }) ## Activar disponibilidad
logger.debug ('res ({})'.format (res))
@ -522,30 +436,21 @@ class ogAdmClientWorker (ServerWorker):
"""
Sends OGAgent activation notification to OpenGnsys server
"""
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
# Generate random secret to send on activation
#self.random = ''.join (random.choice (string.ascii_lowercase + string.digits) for _ in range (self.length))
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')
#self.servidorAdm = self.service.config.get ('ogAdmClient', 'servidorAdm')
#self.puerto = self.service.config.get ('ogAdmClient', 'puerto')
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')
self.urlMenu = self.service.config.get ('ogAdmClient', 'urlMenu')
self.urlMsg = self.service.config.get ('ogAdmClient', 'urlMsg')
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
logger.setLevel (loglevel)
self.REST = REST (url)
if not self.tomaIPlocal():
@ -575,59 +480,8 @@ class ogAdmClientWorker (ServerWorker):
self.muestraMenu()
self.procesaComandos()
## este es una respuesta, y creo que nadie nos va a llamar nunca a este endpoint
## curl --insecure https://192.168.1.249:8000/ogAdmClient/NoComandosPtes
#def process_NoComandosPtes (self, path, get_params, post_params, server):
# logger.warn ('in process_NoComandosPtes')
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}) under /tmp')
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 process_NoComandosPtes(self, path, get_params, post_params, server):
logger.warn('in process_NoComandosPtes')
## curl --insecure https://192.168.1.249:8000/ogAdmClient/Actualizar
def process_Actualizar (self, path, get_params, post_params, server):
@ -655,51 +509,7 @@ class ogAdmClientWorker (ServerWorker):
logger.warn ('in process_IniciarSesion')
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)))
logger.debug ('Ejecución de comando');
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:
output = self.interfaceAdmin (nfn, [dsk, par, nci, ipr])
self.muestraMensaje (9)
herror = 0
except Exception as e:
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)
logger.warn ('in process_CrearImagen')
def process_CrearImagenBasica (self, path, get_params, post_params, server):
logger.warn ('in process_CrearImagenBasica')