Merge pull request 'Initial, very incomplete implementation of ogAdmClient in python' (#7) from ogadmcli into main

Reviewed-on: #7
modules
Natalia Serrano 2024-08-27 11:36:40 +02:00
commit 53f44ad3ab
11 changed files with 761 additions and 15 deletions

197
ogcore-mock.py 100644
View File

@ -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/<cucu>', 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/<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'")
return jsonify({'cucu': j['cucu']})
@app.errorhandler(404)
def _page_not_found(e):
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>''')
@app.errorhandler(Exception)
def _exception(e):
print(e)
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

@ -65,7 +65,7 @@
<item>
<widget class="QLabel" name="VersionLabel">
<property name="text">
<string>Version 1.3.0</string>
<string>Version 0</string>
</property>
</widget>
</item>

View File

@ -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

View File

@ -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

View File

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 "<opengnsys.httpserver.HTTPServerHandler object at 0x7fa788cb8fa0>"
## type(post_params) "<class 'dict'>"
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')

View File

@ -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')