Merge pull request 'Initial, very incomplete implementation of ogAdmClient in python' (#7) from ogadmcli into main
Reviewed-on: #7modules
commit
53f44ad3ab
|
@ -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)
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue