From 18649950663bcf5fb1032afdd835b67ae823f783 Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Fri, 20 Sep 2024 14:20:33 +0200 Subject: [PATCH] refs #708 move duplicated code into its own parent class --- .../modules/server/CloningEngine/__init__.py | 106 +----------- .../modules/server/OpenGnSys/__init__.py | 2 + .../modules/server/ogAdmClient/__init__.py | 123 +------------- src/opengnsys/workers/__init__.py | 1 + src/opengnsys/workers/oglive_worker.py | 160 ++++++++++++++++++ 5 files changed, 172 insertions(+), 220 deletions(-) create mode 100644 src/opengnsys/workers/oglive_worker.py diff --git a/src/opengnsys/modules/server/CloningEngine/__init__.py b/src/opengnsys/modules/server/CloningEngine/__init__.py index bba0923..0d7aea5 100644 --- a/src/opengnsys/modules/server/CloningEngine/__init__.py +++ b/src/opengnsys/modules/server/CloningEngine/__init__.py @@ -32,15 +32,12 @@ import base64 import os -import subprocess from pathlib import Path -from configparser import NoOptionError -from opengnsys import REST from opengnsys.log import logger -from opengnsys.workers import ServerWorker +from opengnsys.workers import ogLiveWorker -class CloningEngineWorker (ServerWorker): +class CloningEngineWorker (ogLiveWorker): name = 'CloningEngine' # Module name REST = None # REST object @@ -50,105 +47,8 @@ class CloningEngineWorker (ServerWorker): def process_status (self, path, get_params, post_params, server): return {self.name: 'in process_status'} ## XXX - def interfaceAdmin (self, method, parametros=[]): - exe = '{}/{}'.format (self.pathinterface, method) - ## for development only. Will be removed when the referenced bash code (/opt/opengnsys/lib/engine/bin/*.lib) is translated into python - devel_bash_prefix = ''' - PATH=/opt/opengnsys/scripts/:$PATH; - for I in /opt/opengnsys/lib/engine/bin/*.lib; do source $I; done; - for i in $(declare -F |cut -f3 -d" "); do export -f $i; done; - ''' - - if parametros: - proc = ['bash', '-c', '{} {} {}'.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)) - p = subprocess.run (proc, capture_output=True) - if 0 != p.returncode: - cmd_txt = ' '.join (proc) - logger.error (f'command ({cmd_txt}) failed, stderr follows:') - for l in p.stderr.strip().decode ('utf-8').splitlines(): - logger.error (f' {l}') - raise Exception (f'command ({cmd_txt}) failed, see log for details') - return p.stdout.strip().decode ('utf-8') - - def tomaIPlocal (self): - try: - self.IPlocal = self.interfaceAdmin ('getIpAddress') - except Exception as e: - logger.error (e) - logger.error ('No se ha podido recuperar la dirección IP del cliente') - return False - logger.info ('local IP is "{}"'.format (self.IPlocal)) - return True - - def enviaMensajeServidor (self, path, obj={}): - obj['iph'] = self.IPlocal ## Ip del ordenador - obj['ido'] = self.idordenador ## Identificador del ordenador - obj['npc'] = self.nombreordenador ## Nombre del ordenador - obj['idc'] = self.idcentro ## Identificador del centro - obj['ida'] = self.idaula ## Identificador del aula - - res = self.REST.sendMessage ('/'.join (self.name, path), obj) - - if (type (res) is not dict): - #logger.error ('No se ha podido establecer conexión con el Servidor de Administración') ## Error de conexión con el servidor - logger.debug (f'res ({res})') - logger.error ('Error al enviar trama ***send() fallo') - return False - - return res - - def cargaPaginaWeb (self, url=None): - if (not url): url = self.urlMenu - os.system ('pkill -9 browser') - - p = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', url]) - try: - p.wait (2) ## if the process dies before 2 seconds... - logger.error ('Error al ejecutar la llamada a la interface de administración') - logger.error ('Error en la creación del proceso hijo') - logger.error ('return code "{}"'.format (p.returncode)) - return False - except subprocess.TimeoutExpired: - pass - - return True - - def muestraMenu (self): - self.cargaPaginaWeb() - - def muestraMensaje (self, idx): - self.cargaPaginaWeb (f'{self.urlMsg}?idx={idx}') - def onActivation (self): - if not os.path.exists ('/scripts/oginit'): - ## no estamos en oglive, este modulo no debe cargarse - ## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar - raise Exception ('Refusing to load within an operating system') - - self.pathinterface = None - self.IPlocal = None ## Ip del ordenador - self.idordenador = None ## Identificador del ordenador - self.nombreordenador = None ## Nombre del ordenador - self.cache = None - self.idproautoexec = None - self.idcentro = None ## Identificador del centro - self.idaula = None ## Identificador del aula - - try: - url = self.service.config.get (self.name, 'remote') - loglevel = self.service.config.get (self.name, 'log') - self.pathinterface = self.service.config.get (self.name, 'pathinterface') - self.urlMenu = self.service.config.get (self.name, 'urlMenu') - self.urlMsg = self.service.config.get (self.name, 'urlMsg') - except NoOptionError as e: - logger.error ("Configuration error: {}".format (e)) - raise e - logger.setLevel (loglevel) - self.REST = REST (url) - + super().onActivation() logger.info ('onActivation ok') ## en C, esto envia una trama de respuesta al servidor. Devuelve un boolean diff --git a/src/opengnsys/modules/server/OpenGnSys/__init__.py b/src/opengnsys/modules/server/OpenGnSys/__init__.py index 7778d0f..a6b2493 100644 --- a/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -189,6 +189,8 @@ class OpenGnSysWorker(ServerWorker): if os.path.isfile(new_hosts_file): shutil.copyfile(new_hosts_file, hosts_file) + logger.info ('onActivation ok') + def onDeactivation(self): """ Sends OGAgent stopping notification to OpenGnsys server diff --git a/src/opengnsys/modules/server/ogAdmClient/__init__.py b/src/opengnsys/modules/server/ogAdmClient/__init__.py index 86e35ee..3c617ea 100644 --- a/src/opengnsys/modules/server/ogAdmClient/__init__.py +++ b/src/opengnsys/modules/server/ogAdmClient/__init__.py @@ -32,18 +32,14 @@ @author: Natalia Serrano, nserrano at qindel dot com """ - import base64 -import os #import threading #import time import subprocess -from pathlib import Path -from configparser import NoOptionError -from opengnsys import REST, operations +#from opengnsys import operations from opengnsys.log import logger -from opengnsys.workers import ServerWorker +from opengnsys.workers import ogLiveWorker # Check authorization header decorator def check_secret (fnc): @@ -67,7 +63,7 @@ def check_secret (fnc): return wrapper -class ogAdmClientWorker (ServerWorker): +class ogAdmClientWorker (ogLiveWorker): name = 'ogAdmClient' # Module name #interface = None # Bound interface for OpenGnsys (el otro modulo lo usa para obtener .ip y .mac REST = None # REST object @@ -228,65 +224,6 @@ class ogAdmClientWorker (ServerWorker): ## 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) - ## for development only. Will be removed when the referenced bash code (/opt/opengnsys/lib/engine/bin/*.lib) is translated into python - devel_bash_prefix = ''' - PATH=/opt/opengnsys/scripts/:$PATH; - for I in /opt/opengnsys/lib/engine/bin/*.lib; do source $I; done; - for i in $(declare -F |cut -f3 -d" "); do export -f $i; done; - ''' - if parametros: - proc = ['bash', '-c', '{} {} {}'.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)) - p = subprocess.run (proc, capture_output=True) - if 0 != p.returncode: - cmd_txt = ' '.join (proc) - logger.error (f'command ({cmd_txt}) failed, stderr follows:') - for l in p.stderr.strip().decode ('utf-8').splitlines(): - logger.error (f' {l}') - raise Exception (f'command ({cmd_txt}) failed, see log for details') - return p.stdout.strip().decode ('utf-8') - - def tomaIPlocal (self): - try: - self.IPlocal = self.interfaceAdmin ('getIpAddress') - except Exception as e: - logger.error (e) - logger.error ('No se ha podido recuperar la dirección IP del cliente') - return False - logger.info ('local IP is "{}"'.format (self.IPlocal)) - return True - - def LeeConfiguracion (self): - try: - parametroscfg = self.interfaceAdmin ('getConfiguration') ## Configuración de los Sistemas Operativos del cliente - except Exception as e: - logger.error (e) - logger.error ('No se ha podido recuperar la dirección IP del cliente') - return None - logger.debug ('parametroscfg ({})'.format (parametroscfg)) - return parametroscfg - - def enviaMensajeServidor (self, path, obj={}): - obj['iph'] = self.IPlocal ## Ip del ordenador - obj['ido'] = self.idordenador ## Identificador del ordenador - obj['npc'] = self.nombreordenador ## Nombre del ordenador - obj['idc'] = self.idcentro ## Identificador del centro - obj['ida'] = self.idaula ## Identificador del aula - - res = self.REST.sendMessage ('/'.join (self.name, path), obj) - - if (type (res) is not dict): - #logger.error ('No se ha podido establecer conexión con el Servidor de Administración') ## Error de conexión con el servidor - logger.debug (f'res ({res})') - logger.error ('Error al enviar trama ***send() fallo') - return False - - return res - def ejecutaArchivo (self,fn): logger.debug ('fn ({})'.format (fn)) @@ -402,25 +339,6 @@ class ogAdmClientWorker (ServerWorker): return True - def cargaPaginaWeb (self, url=None): - if (not url): url = self.urlMenu - os.system ('pkill -9 browser') - - p = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', url]) - try: - p.wait (2) ## if the process dies before 2 seconds... - logger.error ('Error al ejecutar la llamada a la interface de administración') - logger.error ('Error en la creación del proceso hijo') - logger.error ('return code "{}"'.format (p.returncode)) - return False - except subprocess.TimeoutExpired: - pass - - return True - - def muestraMenu (self): - self.cargaPaginaWeb() - def procesaComandos (self): res = self.enviaMensajeServidor ('DisponibilidadComandos', { 'tpc': 'OPG' }) ## Activar disponibilidad logger.debug ('res ({})'.format (res)) @@ -449,38 +367,7 @@ class ogAdmClientWorker (ServerWorker): #} def onActivation (self): - """ - Sends OGAgent activation notification to OpenGnsys server - """ - if not os.path.exists ('/scripts/oginit'): - ## no estamos en oglive, este modulo no debe cargarse - ## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar - raise Exception ('Refusing to load within an operating system') - - self.pathinterface = None - self.IPlocal = None ## Ip del ordenador - self.idordenador = None ## Identificador del ordenador - self.nombreordenador = None ## Nombre del ordenador - self.cache = None - self.idproautoexec = None - self.idcentro = None ## Identificador del centro - self.idaula = None ## Identificador del aula - - try: - url = self.service.config.get (self.name, 'remote') - loglevel = self.service.config.get (self.name, 'log') - self.pathinterface = self.service.config.get (self.name, 'pathinterface') - self.urlMenu = self.service.config.get (self.name, 'urlMenu') - self.urlMsg = self.service.config.get (self.name, 'urlMsg') - except NoOptionError as e: - logger.error ("Configuration error: {}".format (e)) - raise e - logger.setLevel (loglevel) - self.REST = REST (url) - - if not self.tomaIPlocal(): - raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo') - + super().onActivation() logger.info ('Inicio de sesion') logger.info ('Abriendo sesión en el servidor de Administración') if (not self.inclusionCliente()): @@ -505,6 +392,8 @@ class ogAdmClientWorker (ServerWorker): self.muestraMenu() self.procesaComandos() + logger.info ('onActivation ok') + ## curl --insecure https://192.168.1.249:8000/ogAdmClient/Actualizar def process_Actualizar (self, path, get_params, post_params, server): logger.warning ('in process_Actualizar') diff --git a/src/opengnsys/workers/__init__.py b/src/opengnsys/workers/__init__.py index f2bcd7d..dc0c01d 100644 --- a/src/opengnsys/workers/__init__.py +++ b/src/opengnsys/workers/__init__.py @@ -1,2 +1,3 @@ from .server_worker import ServerWorker from .client_worker import ClientWorker +from .oglive_worker import ogLiveWorker diff --git a/src/opengnsys/workers/oglive_worker.py b/src/opengnsys/workers/oglive_worker.py new file mode 100644 index 0000000..a63df10 --- /dev/null +++ b/src/opengnsys/workers/oglive_worker.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2024 Qindel Formación y Servicios S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" +@author: Natalia Serrano, nserrano at qindel dot com +""" +# pylint: disable=unused-wildcard-import,wildcard-import + +import os +import subprocess + +from configparser import NoOptionError +from opengnsys import REST +from opengnsys.log import logger +from .server_worker import ServerWorker + +class ogLiveWorker(ServerWorker): + def interfaceAdmin (self, method, parametros=[]): + exe = '{}/{}'.format (self.pathinterface, method) + ## for development only. Will be removed when the referenced bash code (/opt/opengnsys/lib/engine/bin/*.lib) is translated into python + devel_bash_prefix = ''' + PATH=/opt/opengnsys/scripts/:$PATH; + for I in /opt/opengnsys/lib/engine/bin/*.lib; do source $I; done; + for i in $(declare -F |cut -f3 -d" "); do export -f $i; done; + ''' + + if parametros: + proc = ['bash', '-c', '{} set -x; bash -x {} {}; set +x'.format (devel_bash_prefix, exe, ' '.join (parametros))] + else: + proc = ['bash', '-c', '{} set -x; bash -x {}; set +x'.format (devel_bash_prefix, exe)] + logger.debug ('subprocess.run ("{}", capture_output=True)'.format (proc)) + p = subprocess.run (proc, capture_output=True) + ## DEBUG + logger.info (f'stdout follows:') + for l in p.stdout.strip().decode ('utf-8').splitlines(): + logger.info (f' {l}') + logger.info (f'stderr follows:') + for l in p.stderr.strip().decode ('utf-8').splitlines(): + logger.info (f' {l}') + ## /DEBUG + if 0 != p.returncode: + cmd_txt = ' '.join (proc) + logger.error (f'command ({cmd_txt}) failed, stderr follows:') + for l in p.stderr.strip().decode ('utf-8').splitlines(): + logger.error (f' {l}') + raise Exception (f'command ({cmd_txt}) failed, see log for details') + return p.stdout.strip().decode ('utf-8') + + def tomaIPlocal (self): + try: + self.IPlocal = self.interfaceAdmin ('getIpAddress') + except Exception as e: + logger.error (e) + logger.error ('No se ha podido recuperar la dirección IP del cliente') + return False + logger.info ('local IP is "{}"'.format (self.IPlocal)) + return True + + def enviaMensajeServidor (self, path, obj={}): + obj['iph'] = self.IPlocal ## Ip del ordenador + obj['ido'] = self.idordenador ## Identificador del ordenador + obj['npc'] = self.nombreordenador ## Nombre del ordenador + obj['idc'] = self.idcentro ## Identificador del centro + obj['ida'] = self.idaula ## Identificador del aula + + res = self.REST.sendMessage ('/'.join ([self.name, path]), obj) + + if (type (res) is not dict): + #logger.error ('No se ha podido establecer conexión con el Servidor de Administración') ## Error de conexión con el servidor + logger.debug (f'res ({res})') + logger.error ('Error al enviar trama ***send() fallo') + return False + + return res + + def cargaPaginaWeb (self, url=None): + if (not url): url = self.urlMenu + os.system ('pkill -9 browser') + + p = subprocess.Popen (['/opt/opengnsys/bin/browser', '-qws', url]) + try: + p.wait (2) ## if the process dies before 2 seconds... + logger.error ('Error al ejecutar la llamada a la interface de administración') + logger.error ('Error en la creación del proceso hijo') + logger.error ('return code "{}"'.format (p.returncode)) + return False + except subprocess.TimeoutExpired: + pass + + return True + + def muestraMenu (self): + self.cargaPaginaWeb() + + def muestraMensaje (self, idx): + self.cargaPaginaWeb (f'{self.urlMsg}?idx={idx}') + + def LeeConfiguracion (self): + try: + parametroscfg = self.interfaceAdmin ('getConfiguration') ## Configuración de los Sistemas Operativos del cliente + except Exception as e: + logger.error (e) + logger.error ('No se ha podido recuperar la dirección IP del cliente') + return None + logger.debug ('parametroscfg ({})'.format (parametroscfg)) + return parametroscfg + + def onActivation (self): + if not os.path.exists ('/scripts/oginit'): + ## no estamos en oglive, este modulo no debe cargarse + ## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar + raise Exception ('Refusing to load within an operating system') + + self.pathinterface = None + self.IPlocal = None ## Ip del ordenador + self.idordenador = None ## Identificador del ordenador + self.nombreordenador = None ## Nombre del ordenador + self.cache = None + self.idproautoexec = None + self.idcentro = None ## Identificador del centro + self.idaula = None ## Identificador del aula + + try: + url = self.service.config.get (self.name, 'remote') + loglevel = self.service.config.get (self.name, 'log') + self.pathinterface = self.service.config.get (self.name, 'pathinterface') + self.urlMenu = self.service.config.get (self.name, 'urlMenu') + self.urlMsg = self.service.config.get (self.name, 'urlMsg') + except NoOptionError as e: + logger.error ("Configuration error: {}".format (e)) + raise e + logger.setLevel (loglevel) + self.REST = REST (url) + + if not self.tomaIPlocal(): + raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')