diff --git a/ogcore-mock.py b/ogcore-mock.py new file mode 100644 index 0000000..5982355 --- /dev/null +++ b/ogcore-mock.py @@ -0,0 +1,197 @@ +from flask import Flask, request, jsonify, render_template_string, abort +import os +import logging +import json +import subprocess + +## 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 + + +app = Flask(__name__) +logging.basicConfig(level=logging.INFO) + +## agente lin/win/mac + +@app.route('/opengnsys/rest/ogagent/', methods=['POST']) +def og_agent(cucu): + c = request + logging.info(f"{request.get_json()}") + return jsonify({}) + + + +## agente oglive + +@app.route('/opengnsys/rest/__ogAdmClient/InclusionCliente', methods=['POST']) +def inclusion_cliente(): + 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})") + + # 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"); + cache = 42 #dbi_result_get_uint(result, "cache"); + idproautoexec = 42 #dbi_result_get_uint(result, "idproautoexec"); + idaula = 42 #dbi_result_get_uint(result, "idaula"); + idcentro = 42 #dbi_result_get_uint(result, "idcentro"); + + # resul = actualizaConfiguracion(dbi, cfg, idordenador); ## Actualiza la configuración del ordenador + # if (!resul) { log_error ('Cannot add client to database') } + # if (!registraCliente(iph)) { log_error ('client table is full') } ## Incluyendo al cliente en la tabla de sokets + + return jsonify({ + 'res': 1, ## int, Confirmación proceso correcto + 'ido': idordenador, ## int + 'npc': nombreordenador, ## string + 'che': cache, ## int + 'exe': idproautoexec, ## int + 'ida': idaula, ## int + 'idc': idcentro ## int + }) + +def _recorreProcedimientos(parametros, fileexe, idp): + #char idprocedimiento[LONPRM]; + #int procedimientoid, lsize; + #const char *msglog, *param; + #dbi_result result; + + #dbi->query (sprintf 'SELECT procedimientoid,parametros FROM procedimientos_acciones WHERE idprocedimiento=%s ORDER BY orden', $idp); + #if (!result) { log_error ('failed to query database'); return 0; } + if 1: #while (dbi_result_next_row(result)) { + procedimientoid = 0 ## dbi_result_get_uint(result, "procedimientoid"); + if (procedimientoid > 0): ## Procedimiento recursivo + if (not _recorreProcedimientos (parametros, fileexe, procedimientoid)): + return 0 + else: + #param = '@'.join (["nfn=EjecutarScript\rscp=uptime", "nfn=EjecutarScript\rscp=cat /proc/uptime"]) ## dbi_result_get_string(result, "parametros"); + param = '@'.join (["nfn=popup\rtitle=my title\rmessage=my message"]) + parametros = '{}@'.format (param) + fileexe.write (parametros) + #} + + return 1 + +@app.route('/opengnsys/rest/__ogAdmClient/AutoexecCliente', methods=['POST']) +def autoexec_client(): + 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})") + + fileautoexec = '/tmp/Sautoexec-{}'.format(iph) + logging.info ("fileautoexec ({})".format (fileautoexec)); + try: + fileexe = open (fileautoexec, 'w') + except Exception as e: + logging.error ("cannot create temporary file: {}".format (e)) + return jsonify({}) + + if (_recorreProcedimientos ('', fileexe, exe)): + res = jsonify ({ 'res': 1, 'nfl': fileautoexec }) + else: + res = jsonify ({ 'res': 0 }) + + fileexe.close() + return res + +@app.route('/opengnsys/rest/__ogAdmClient/enviaArchivo', methods=['POST']) +def envia_archivo(): + 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})") + + 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 + return 42 + +def buscaComandos(ido): + #dbi->query (sprintf "SELECT sesion, parametros FROM acciones WHERE idordenador=%s AND estado='%d' ORDER BY idaccion", ido, ACCION_INICIADA); + #dbi_result_next_row(result) ## cogemos solo una fila + + #if not row { return; } + return + + #ids = 42 #dbi_result_get_uint(result, "sesion"); + #param = "nfn=popup\rtitle=my title\rmessage=my message" #dbi_result_get_string(result, "parametros"); + ## 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']) +def comandos_pendientes(): + 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})") + + idx = clienteExistente(iph) ## Busca índice del cliente + if not idx: + ## que devuelvo?? pongamos un 404... + abort(404, "Client does not exist") + + param = buscaComandos(ido) ## Existen comandos pendientes, buscamos solo uno + if param is None: + return jsonify({'nfn': 'NoComandosPtes'}) + + #strcpy(tbsockets[idx].estado, CLIENTE_OCUPADO); ## esto queda totalmente del lado del servidor, no lo implemento en python + + return jsonify(param) + +@app.route('/opengnsys/rest/__ogAdmClient/DisponibilidadComandos', methods=['POST']) +def disponibilidad_comandos(): + 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})") + + idx = clienteExistente(iph) ## Busca índice del cliente + if not idx: + ## que devuelvo?? pongamos un 404... + 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/', 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'") + + return jsonify({'cucu': j['cucu']}) + +@app.errorhandler(404) +def _page_not_found(e): + return render_template_string('''not found''') + +@app.errorhandler(500) +def _internal_server_error(e): + return render_template_string('''err''') + +@app.errorhandler(Exception) +def _exception(e): + print(e) + return render_template_string('''exception''') + +if __name__ == '__main__': + app.run(host = '192.168.1.249', port = 443, debug=True) diff --git a/src/about-dialog.ui b/src/about-dialog.ui index df40da5..93f4817 100644 --- a/src/about-dialog.ui +++ b/src/about-dialog.ui @@ -65,7 +65,7 @@ - Version 1.3.0 + Version 0 diff --git a/src/cfg/ogagent.cfg b/src/cfg/ogagent.cfg index 0291e88..be977a2 100644 --- a/src/cfg/ogagent.cfg +++ b/src/cfg/ogagent.cfg @@ -20,7 +20,14 @@ log=DEBUG # Module specific # The sections must match the module name # This section will be passes on activation to module -#[Sample1] -#value1=Mariete -#value2=Yo -#remote=https://172.27.0.1:9999/rest +[ogAdmClient] +#path=test_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 +pathinterface=/opt/opengnsys/interfaceAdm +urlMenu=https://192.168.2.1/opengnsys/varios/menubrowser.php +urlMsg=http://localhost/cgi-bin/httpd-log.sh diff --git a/src/opengnsys/__init__.py b/src/opengnsys/__init__.py index e268ca7..cf7a335 100644 --- a/src/opengnsys/__init__.py +++ b/src/opengnsys/__init__.py @@ -37,7 +37,7 @@ import six from . import modules from .RESTApi import REST, RESTError -VERSION='1.3.0' +VERSION='0' __title__ = 'OpenGnsys Agent' __version__ = VERSION diff --git a/src/opengnsys/linux/OGAgentService.py b/src/opengnsys/linux/OGAgentService.py old mode 100644 new mode 100755 diff --git a/src/opengnsys/linux/log.py b/src/opengnsys/linux/log.py index 396a2db..393f5e8 100644 --- a/src/opengnsys/linux/log.py +++ b/src/opengnsys/linux/log.py @@ -52,7 +52,7 @@ class LocalLogger(object): logging.basicConfig( filename=fname, filemode='a', - format='%(levelname)s %(asctime)s %(message)s', + format='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s', level=logging.DEBUG ) self.logger = logging.getLogger('opengnsys') @@ -69,7 +69,7 @@ class LocalLogger(object): # our loglevels are 10000 (other), 20000 (debug), .... # logging levels are 10 (debug), 20 (info) # OTHER = logging.NOTSET - self.logger.log(int(level / 1000) - 10, message) + self.logger.log(int(level / 1000) - 10, message, stacklevel=4) def isWindows(self): return False diff --git a/src/opengnsys/loader.py b/src/opengnsys/loader.py index ca053b6..577fcf7 100644 --- a/src/opengnsys/loader.py +++ b/src/opengnsys/loader.py @@ -100,12 +100,12 @@ def loadModules(controller, client=False): # paths += (os.path.dirname(sys.modules[modPath].__file__),) - logger.debug('Loading modules from {}'.format(paths)) - # Load modules + logger.debug('Loading modules from {}'.format(paths)) doLoad(paths) # Add to list of available modules + logger.debug('Adding {} classes'.format('server' if modType == ServerWorker else 'client')) recursiveAdd(modType) return ogModules diff --git a/src/opengnsys/log.py b/src/opengnsys/log.py index 5c74783..eb1f309 100644 --- a/src/opengnsys/log.py +++ b/src/opengnsys/log.py @@ -94,7 +94,7 @@ class Logger(object): except Exception: tb = '(could not get traceback!)' - self.log(DEBUG, tb) + self.log(DEBUG, 'traceback follows: "{}"'.format(tb)) def flush(self): pass diff --git a/src/opengnsys/windows/log.py b/src/opengnsys/windows/log.py index 7b2bec2..ea875fd 100644 --- a/src/opengnsys/windows/log.py +++ b/src/opengnsys/windows/log.py @@ -47,7 +47,7 @@ class LocalLogger(object): logging.basicConfig( filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'), filemode='a', - format='%(levelname)s %(asctime)s (%(threadName)s) %(message)s', + format='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s', level=logging.DEBUG ) self.logger = logging.getLogger('opengnsys') @@ -58,7 +58,7 @@ class LocalLogger(object): # our loglevels are 10000 (other), 20000 (debug), .... # logging levels are 10 (debug), 20 (info) # OTHER = logging.NOTSET - self.logger.log(int(level / 1000 - 10), message) + self.logger.log(int(level / 1000 - 10), message, stacklevel=4) if level < INFO or self.serviceLogger is False: # Only information and above will be on event log return diff --git a/src/test_modules/server/ogAdmClient/__init__.py b/src/test_modules/server/ogAdmClient/__init__.py new file mode 100644 index 0000000..b107431 --- /dev/null +++ b/src/test_modules/server/ogAdmClient/__init__.py @@ -0,0 +1,540 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2014 Virtual Cable S.L. +# 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: Ramón M. Gómez, ramongomez at us dot es +@author: Natalia Serrano, nserrano at qindel dot com +""" + + +import base64 +import os +import random +import shutil +import string +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.log import logger +from opengnsys.workers import ServerWorker + +# Check authorization header decorator +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) + #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) + # elif this.random == server.headers['Authorization']: + # return fnc(*args, **kwargs) + # else: + # raise Exception('Unauthorized operation') + #except Exception as e: + # logger.error(str(e)) + # raise Exception(e) + + return wrapper + +class ogAdmClientWorker(ServerWorker): + name = 'ogAdmClient' # Module name + interface = None # Bound interface for OpenGnsys + 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 + + def onDeactivation(self): + """ + Sends OGAgent stopping notification to OpenGnsys server + """ + logger.debug('onDeactivation') + + 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 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) + + @check_secret + def process_status(self, path, get_params, post_params, server): + return {'ogAdmClient': 'in process_status'} + + @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))) + ## in process_popup, should not happen, path "[]" get_params "{}" post_params "{'title': 'mi titulo', 'message': 'mi mensaje'}" server "" + ## type(post_params) "" + return {'debug':'test'} + + + + + + + + + #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) + + + 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): + 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" + 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 + if buffer: + 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('=') + post_params[k] = v + logger.debug ('post_params "{}"'.format (post_params)) + + func_name = post_params.pop ('nfn', None) + if func_name is None: + logger.error ('Ha ocurrido algún problema al procesar la trama recibida') + break + func = getattr (self, 'process_' + func_name) + ## func is already a ref to self.func, so we don't have to call self.func(...) or func(self, ...) + + logger.debug ('calling function "{}" with post_params "{}"'.format (func_name, post_params)) + output = func ([], {}, post_params, None) + logger.debug ('output "{}"'.format (output)) + if not output: + logger.error ('Ha ocurrido algún problema al procesar la trama recibida') + break + + def inclusionCliente(self): + cfg = self.LeeConfiguracion() + res = self.enviaMensajeServidor ('InclusionCliente', { 'cfg': cfg }) + logger.debug ('res ({})'.format (res)) + + ## RESPUESTA_InclusionCliente + 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 + + if (not res['ido'] or not res['npc']): + logger.error ('Se han recibido parámetros con valores no válidos') + return False + + self.idordenador = res['ido'] ## Identificador del ordenador + self.nombreordenador = res['npc'] ## Nombre del ordenador + self.cache = res['che'] ## Tamaño de la caché reservada al cliente + self.idproautoexec = res['exe'] ## Procedimento de inicio (Autoexec) + self.idcentro = res['idc'] ## Identificador de la Unidad Organizativa + self.idaula = res['ida'] ## Identificador del aula + + return True + + 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): + res = self.enviaMensajeServidor ('AutoexecCliente', { 'exe': self.idproautoexec }) + logger.debug ('res ({})'.format (res)) + + 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']) : + 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): + 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 + logger.debug (f'res ({res})') + + 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')) + + self.ejecutaArchivo (fileautoexec); + + return True + + def comandosPendientes(self): + while (True): + res = self.enviaMensajeServidor ('ComandosPendientes') ## receives just one command + 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 + + logger.info (res) + if ('NoComandosPtes' == res['nfn']): + break + + ## TODO manage the rest of cases... we might have to do something similar to ejecutaArchivo + #if(!gestionaTrama(ptrTrama)){ // Análisis de la trama + # logger.error ('Ha ocurrido algún problema al procesar la trama recibida') + # return False + #} + ## ATM let's just return false to avoid a possible infinite loop + return False + + 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): + res = self.enviaMensajeServidor ('DisponibilidadComandos', { 'tpc': 'OPG' }) ## Activar disponibilidad + logger.debug ('res ({})'.format (res)) + + 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 + + logger.info ('Disponibilidad de comandos activada') ## Disponibilidad de cliente activada + + ## 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' + # 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' + # 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' + #} + + 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') + + logger.info ('Inicio de sesion') + logger.info ('Abriendo sesión en el servidor de Administración') + if (not self.inclusionCliente()): + raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo') + + logger.info ('Cliente iniciado') + logger.info ('Procesando caché') + 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): + logger.info ('Ejecución de archivo Autoexec') + 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()): + raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo') + + logger.info ('Acciones pendientes procesadas') + + self.muestraMenu() + self.procesaComandos() + + def process_NoComandosPtes(self, path, get_params, post_params, server): + logger.warn('in process_NoComandosPtes') + + def process_Actualizar(self, path, get_params, post_params, server): + logger.warn('in process_Actualizar') + + def process_Purgar(self, path, get_params, post_params, server): + logger.warn('in process_Purgar') + + def process_ConsolaRemota(self, path, get_params, post_params, server): + logger.warn('in process_ConsolaRemota') + + def process_Sondeo(self, path, get_params, post_params, server): + logger.warn('in process_Sondeo') + + def process_Arrancar(self, path, get_params, post_params, server): + logger.warn('in process_Arrancar') + + def process_Apagar(self, path, get_params, post_params, server): + logger.warn('in process_Apagar') + + def process_Reiniciar(self, path, get_params, post_params, server): + logger.warn('in process_Reiniciar') + + def process_IniciarSesion(self, path, get_params, post_params, server): + logger.warn('in process_IniciarSesion') + + def process_CrearImagen(self, path, get_params, post_params, server): + logger.warn('in process_CrearImagen') + + def process_CrearImagenBasica(self, path, get_params, post_params, server): + logger.warn('in process_CrearImagenBasica') + + def process_CrearSoftIncremental(self, path, get_params, post_params, server): + logger.warn('in process_CrearSoftIncremental') + + def process_RestaurarImagen(self, path, get_params, post_params, server): + logger.warn('in process_RestaurarImagen') + + 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') diff --git a/src/update.py b/src/update.py index 11f2648..caf18f1 100755 --- a/src/update.py +++ b/src/update.py @@ -17,11 +17,13 @@ def update_version(): with fileinput.FileInput ('about-dialog.ui', inplace=True) as file: for line in file: - print (line.replace ('Version [^<]*', f'Version {version}'), end='') + new = re.sub (r'Version [^<]*', 'Version {}'.format(version), line) + print (new, end='') with fileinput.FileInput ('opengnsys/__init__.py', inplace=True) as file: for line in file: - print(line.replace ('VERSION=.*', f"VERSION='{version}'"), end='') + new = re.sub (r'VERSION=.*', "VERSION='{}'".format(version), line) + print (new, end='') with open ('../windows/VERSION', 'w') as outfile: outfile.write (win_version + '\n')