Compare commits

..

No commits in common. "main" and "ping3" have entirely different histories.
main ... ping3

23 changed files with 145 additions and 402 deletions

View File

@ -6,36 +6,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [5.1.1] - 2025-05-06
### Fixed
- Fixed URL for notifying stop to ogcore
## [5.1.0] - 2025-05-06
### Added
- Added powershell helper script for logging out from windows
## [5.0.0] - 2025-05-06
### Added
- Use TLS
## [4.0.0] - 2025-04-24
### Added
- Authn/authz to the oglive agent
## [3.3.0] - 2025-04-14
### Added
- Log stuff to a new json log
## [3.2.0] - 2025-04-10 ## [3.2.0] - 2025-04-10
### Added ### Added

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,47 +0,0 @@
## Crear tarea programada para matar el agente de Windows al cerrar sesión
1. Abrir el task scheduler y pinchar en Create task:
![image info](./sched01.png)
2. Rellenar el nombre y luego pinchar en Change user or group:
![image info](./sched02.png)
3. Pinchar en Advanced:
![image info](./sched03.png)
4. Pinchar en Find now:
![image info](./sched04.png)
5. Seleccionar Administrator y luego Ok, y luego Ok:
![image info](./sched05.png)
6. De vuelta en la pantalla de crear tarea, ir a la pestaña Triggers y pinchar en New:
![image info](./sched06.png)
7. Seleccionar "On disconnect from user session", seleccionar "Connection from local computer", y luego Ok:
![image info](./sched07.png)
8. Ir a la pestaña "Actions" y pinchar en New:
![image info](./sched08.png)
9. Pinchar en Browse, seleccionar C:\Program Files (x86)\OGAgent\stop-agent.ps1, y luego Ok:
![image info](./sched09.png)
10. Ir a la pestaña Conditions y desmarcar las condiciones relativas a Power:
![image info](./sched10.png)
11. Pinchar Ok y la tarea programada queda creada.
En caso de usar remotepc, hay que repetir todos estos pasos, es decir, crear una
tarea programada nueva, seleccionando "Connection from remote computer" en lugar
de "Connection from local computer".

View File

@ -1,39 +1,3 @@
ogagent (5.1.1-1) stable; urgency=medium
* Fix URL for notifying stop to ogcore
-- OpenGnsys developers <info@opengnsys.es> Tue, 06 May 2025 13:31:48 +0200
ogagent (5.1.0-1) stable; urgency=medium
* Include powershell helper script for logging out of windows
-- OpenGnsys developers <info@opengnsys.es> Tue, 06 May 2025 13:30:59 +0200
ogagent (5.0.0-1) stable; urgency=medium
* Use TLS
-- OpenGnsys developers <info@opengnsys.es> Fri, 25 Apr 2025 13:09:49 +0200
ogagent (4.0.0-1) stable; urgency=medium
* Handle authn/authz in the oglive agent
-- OpenGnsys developers <info@opengnsys.es> Thu, 24 Apr 2025 13:28:57 +0200
ogagent (3.3.0-1) stable; urgency=medium
* Create an additional json log file
-- OpenGnsys developers <info@opengnsys.es> Mon, 14 Apr 2025 13:50:32 +0200
ogagent (3.2.0-1) stable; urgency=medium
* Operating system: periodically ping ogcore
-- OpenGnsys developers <info@opengnsys.es> Thu, 10 Apr 2025 11:37:35 +0200
ogagent (3.1.0-1) stable; urgency=medium ogagent (3.1.0-1) stable; urgency=medium
* Oglive: periodically ping ogcore * Oglive: periodically ping ogcore

View File

@ -99,4 +99,3 @@ coll = COLLECT(
import shutil import shutil
shutil.copytree ('cfg', '{}/{}/cfg'.format(DISTPATH, dist_name)) shutil.copytree ('cfg', '{}/{}/cfg'.format(DISTPATH, dist_name))
shutil.copy ('stop-agent.ps1', '{}/{}/stop-agent.ps1'.format(DISTPATH, dist_name))

View File

@ -1 +1 @@
5.1.1 3.1.0

View File

@ -17,12 +17,6 @@ level=full
# Log Level, if omitted, will be set to INFO # Log Level, if omitted, will be set to INFO
log=DEBUG log=DEBUG
# TLS
ca=C:\Program Files (x86)\OGagent\ca.crt
crt=C:\Program Files (x86)\OGagent\ogagent.crt
key=C:\Program Files (x86)\OGagent\ogagent.key
# Module specific # Module specific
# The sections must match the module name # The sections must match the module name
# This section will be passes on activation to module # This section will be passes on activation to module
@ -34,8 +28,3 @@ log=DEBUG
pathinterface=/opt/opengnsys/interfaceAdm pathinterface=/opt/opengnsys/interfaceAdm
urlMenu={}://{}/menu-browser urlMenu={}://{}/menu-browser
urlMsg=http://localhost/cgi-bin/httpd-log.sh urlMsg=http://localhost/cgi-bin/httpd-log.sh
# TLS
ca=/opt/opengnsys/etc/ca.crt
crt=/opt/opengnsys/etc/ogagent.crt
key=/opt/opengnsys/etc/ogagent.key

View File

@ -34,7 +34,6 @@
import os
import requests import requests
import logging import logging
import json import json
@ -44,6 +43,7 @@ from .log import logger
from .utils import exceptionToMessage from .utils import exceptionToMessage
VERIFY_CERT = False # Do not check server certificate
TIMEOUT = 5 # Connection timout, in seconds TIMEOUT = 5 # Connection timout, in seconds
@ -58,12 +58,8 @@ class ConnectionError(RESTError):
# Disable warnings log messages # Disable warnings log messages
try: try:
import urllib3 # @UnusedImport import urllib3 # @UnusedImport
requests_log = logging.getLogger ('urllib3')
requests_log.setLevel (logging.INFO)
except Exception: except Exception:
from requests.packages import urllib3 # @Reimport from requests.packages import urllib3 # @Reimport
requests_log = logging.getLogger ('requests.packages.urllib3')
requests_log.setLevel (logging.INFO)
try: try:
urllib3.disable_warnings() # @UndefinedVariable urllib3.disable_warnings() # @UndefinedVariable
@ -88,7 +84,7 @@ class REST(object):
the deserialized JSON result or raises an exception in case of error the deserialized JSON result or raises an exception in case of error
""" """
def __init__(self, url, ca_file=None, crt_file=None, key_file=None): def __init__(self, url):
""" """
Initializes the REST helper Initializes the REST helper
url is the full url of the REST API Base, as for example "https://example.com/rest/v1". url is the full url of the REST API Base, as for example "https://example.com/rest/v1".
@ -105,26 +101,6 @@ class REST(object):
except Exception: except Exception:
self.newerRequestLib = False # I no version, guess this must be an old requests self.newerRequestLib = False # I no version, guess this must be an old requests
if not self.newerRequestLib:
logger.debug ('TLS not available: python requests library is old')
self.use_tls = url.startswith ('https')
if self.use_tls:
if not ca_file or not crt_file or not key_file:
raise Exception ('missing TLS parameters in REST constructor')
errs = 0
for f in [ca_file, crt_file, key_file]:
if not os.path.exists (f):
logger.error (f'{f}: No such file or directory')
errs += 1
if errs:
raise Exception ('TLS files not found')
self.ca_file = ca_file
self.crt_file = crt_file
self.key_file = key_file
# Disable logging requests messages except for errors, ... # Disable logging requests messages except for errors, ...
logging.getLogger("requests").setLevel(logging.CRITICAL) logging.getLogger("requests").setLevel(logging.CRITICAL)
# Tries to disable all warnings # Tries to disable all warnings
@ -155,19 +131,14 @@ class REST(object):
logger.debug('Requesting using GET (no data provided) {}'.format(url)) logger.debug('Requesting using GET (no data provided) {}'.format(url))
# Old requests version does not support verify, but it do not checks ssl certificate by default # Old requests version does not support verify, but it do not checks ssl certificate by default
if self.newerRequestLib: if self.newerRequestLib:
if self.use_tls: r = requests.get(url, verify=VERIFY_CERT, timeout=TIMEOUT)
r = requests.get(url, cert=(self.crt_file, self.key_file), verify=self.ca_file, timeout=TIMEOUT)
else:
r = requests.get(url, timeout=TIMEOUT)
else: else:
r = requests.get(url) r = requests.get(url)
else: # POST else: # POST
logger.debug('Requesting using POST {}, data: {}'.format(url, data)) logger.debug('Requesting using POST {}, data: {}'.format(url, data))
if self.newerRequestLib: if self.newerRequestLib:
if self.use_tls: r = requests.post(url, data=data, headers={'content-type': 'application/json'},
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, cert=(self.crt_file, self.key_file), verify=self.ca_file, timeout=TIMEOUT) verify=VERIFY_CERT, timeout=TIMEOUT)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, timeout=TIMEOUT)
else: else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}) r = requests.post(url, data=data, headers={'content-type': 'application/json'})
@ -177,9 +148,7 @@ class REST(object):
raise Exception (f'response content-type is not "application/json" but "{ct}"') raise Exception (f'response content-type is not "application/json" but "{ct}"')
r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
code = e.response.status_code raise ConnectionError(e)
logger.warning (f'request failed, HTTP code "{code}"')
return None
except Exception as e: except Exception as e:
raise ConnectionError(exceptionToMessage(e)) raise ConnectionError(exceptionToMessage(e))
@ -191,13 +160,13 @@ class REST(object):
@param data: if None or omitted, message will be a GET, else it will send a POST @param data: if None or omitted, message will be a GET, else it will send a POST
@param processData: if True, data will be serialized to json before sending, else, data will be sent as "raw" @param processData: if True, data will be serialized to json before sending, else, data will be sent as "raw"
""" """
#logger.debug('Invoking post message {} with data {}'.format(msg, data)) logger.debug('Invoking post message {} with data {}'.format(msg, data))
if processData and data is not None: if processData and data is not None:
data = json.dumps(data) data = json.dumps(data)
url = self._getUrl(msg) url = self._getUrl(msg)
#logger.debug('Requesting {}'.format(url)) logger.debug('Requesting {}'.format(url))
try: try:
res = self._request(url, data) res = self._request(url, data)

View File

@ -34,8 +34,6 @@ import logging
import os import os
import tempfile import tempfile
from ..log_format import JsonFormatter
# Logging levels # Logging levels
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6)) OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
@ -50,25 +48,15 @@ class LocalLogger(object):
for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()): for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()):
try: try:
fname1 = os.path.join (logDir, 'opengnsys.log') fname = os.path.join(logDir, 'opengnsys.log')
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s') logging.basicConfig(
fh1 = logging.FileHandler (filename=fname1, mode='a') filename=fname,
fh1.setFormatter (fmt1) filemode='a',
fh1.setLevel (logging.DEBUG) format='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s',
level=logging.DEBUG
fname2 = os.path.join (logDir, 'opengnsys.json.log') )
fmt2 = JsonFormatter ({"timestamp": "asctime", "severity": "levelname", "threadName": "threadName", "function": "funcName", "message": "message"}, time_format='%Y-%m-%d %H:%M:%S', msec_format='')
fh2 = logging.FileHandler (filename=fname2, mode='a')
fh2.setFormatter (fmt2)
fh2.setLevel (logging.DEBUG)
self.logger = logging.getLogger('opengnsys') self.logger = logging.getLogger('opengnsys')
self.logger.setLevel (logging.DEBUG) os.chmod(fname, 0o0600)
self.logger.addHandler (fh1)
self.logger.addHandler (fh2)
os.chmod (fname1, 0o0600)
os.chmod (fname2, 0o0600)
return return
except Exception: except Exception:
pass pass

View File

@ -1,55 +0,0 @@
import json
import logging
class JsonFormatter(logging.Formatter):
"""
Formatter that outputs JSON strings after parsing the LogRecord.
@param dict fmt_dict: Key: logging format attribute pairs. Defaults to {"message": "message"}.
@param str time_format: time.strftime() format string. Default: "%Y-%m-%dT%H:%M:%S"
@param str msec_format: Microsecond formatting. Appended at the end. Default: "%s.%03dZ"
"""
def __init__(self, fmt_dict: dict = None, time_format: str = "%Y-%m-%dT%H:%M:%S", msec_format: str = "%s.%03dZ"):
self.fmt_dict = fmt_dict if fmt_dict is not None else {"message": "message"}
self.default_time_format = time_format
self.default_msec_format = msec_format
self.datefmt = None
def usesTime(self) -> bool:
"""
Overwritten to look for the attribute in the format dict values instead of the fmt string.
"""
return "asctime" in self.fmt_dict.values()
def formatMessage(self, record) -> dict:
"""
Overwritten to return a dictionary of the relevant LogRecord attributes instead of a string.
KeyError is raised if an unknown attribute is provided in the fmt_dict.
"""
return {fmt_key: record.__dict__[fmt_val] for fmt_key, fmt_val in self.fmt_dict.items()}
def format(self, record) -> str:
"""
Mostly the same as the parent's class method, the difference being that a dict is manipulated and dumped as JSON
instead of a string.
"""
record.message = record.getMessage()
if self.usesTime():
record.asctime = self.formatTime(record, self.datefmt)
message_dict = self.formatMessage(record)
if record.exc_info:
# Cache the traceback text to avoid converting it multiple times
# (it's constant anyway)
if not record.exc_text:
record.exc_text = self.formatException(record.exc_info)
if record.exc_text:
message_dict["exc_info"] = record.exc_text
if record.stack_info:
message_dict["stack_info"] = self.formatStack(record.stack_info)
return json.dumps(message_dict, default=str)

View File

@ -131,13 +131,10 @@ class OpenGnSysWorker(ServerWorker):
# Ensure cfg has required configuration variables or an exception will be thrown # Ensure cfg has required configuration variables or an exception will be thrown
try: try:
url = self.service.config.get(self.name, 'remote') url = self.service.config.get(self.name, 'remote')
ca_file = self.service.config.get(self.name, 'ca')
crt_file = self.service.config.get(self.name, 'crt')
key_file = self.service.config.get(self.name, 'key')
except NoOptionError as e: except NoOptionError as e:
logger.error("Configuration error: {}".format(e)) logger.error("Configuration error: {}".format(e))
raise e raise e
self.REST = REST (url, ca_file=ca_file, crt_file=crt_file, key_file=key_file) self.REST = REST(url)
# Execution level ('full' by default) # Execution level ('full' by default)
try: try:
self.exec_level = self.service.config.get(self.name, 'level') self.exec_level = self.service.config.get(self.name, 'level')
@ -177,7 +174,7 @@ class OpenGnSysWorker(ServerWorker):
logger.warn (str (e)) logger.warn (str (e))
# Trying to initialize on alternative server, if defined # Trying to initialize on alternative server, if defined
# (used in "exam mode" from the University of Seville) # (used in "exam mode" from the University of Seville)
self.REST = REST(self.service.config.get(self.name, 'altremote'), ca_file=ca_file, crt_file=crt_file, key_file=key_file) self.REST = REST(self.service.config.get(self.name, 'altremote'))
self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip, self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip,
'secret': self.random, 'ostype': operations.os_type, 'secret': self.random, 'ostype': operations.os_type,
'osversion': operations.os_version, 'alt_url': True, 'osversion': operations.os_version, 'alt_url': True,

View File

@ -33,15 +33,14 @@
""" """
import base64 import base64
#import threading
#import time
import os import os
import signal import signal
import string
import random
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from urllib.parse import unquote from urllib.parse import unquote
from opengnsys import VERSION
from opengnsys.log import logger from opengnsys.log import logger
from opengnsys.workers import ogLiveWorker from opengnsys.workers import ogLiveWorker
@ -51,41 +50,22 @@ def check_secret (fnc):
Decorator to check for received secret key and raise exception if it isn't valid. Decorator to check for received secret key and raise exception if it isn't valid.
""" """
def wrapper (*args, **kwargs): def wrapper (*args, **kwargs):
try:
this, path, get_params, post_params, server = args
if not server: ## this happens on startup, eg. onActivation->autoexecCliente->ejecutaArchivo->popup->check_secret
return fnc (*args, **kwargs) return fnc (*args, **kwargs)
#try:
if this.random == server.headers['Authorization']: # this, path, get_params, post_params, server = args
return fnc (*args, **kwargs) # # Accept "status" operation with no arguments or any function with Authorization header
else: # if fnc.__name__ == 'process_status' and not get_params:
raise Exception ('Unauthorized operation') # return fnc (*args, **kwargs)
except Exception as e: # elif this.random == server.headers['Authorization']:
logger.error (str (e)) # return fnc (*args, **kwargs)
raise Exception (e) # else:
# raise Exception ('Unauthorized operation')
#except Exception as e:
# logger.error (str (e))
# raise Exception (e)
return wrapper return wrapper
# Check if operation is permitted
def execution_level(level):
def check_permitted(fnc):
def wrapper(*args, **kwargs):
levels = ['status', 'halt', 'full']
this = args[0]
try:
if levels.index(level) <= levels.index(this.exec_level):
return fnc(*args, **kwargs)
else:
raise Exception('Unauthorized operation')
except Exception as e:
logger.debug (str(e))
raise Exception(e)
return wrapper
return check_permitted
class ogAdmClientWorker (ogLiveWorker): class ogAdmClientWorker (ogLiveWorker):
name = 'ogAdmClient' # Module name name = 'ogAdmClient' # Module name
REST = None # REST object REST = None # REST object
@ -95,9 +75,63 @@ class ogAdmClientWorker (ogLiveWorker):
Sends OGAgent stopping notification to OpenGnsys server Sends OGAgent stopping notification to OpenGnsys server
""" """
logger.debug ('onDeactivation') logger.debug ('onDeactivation')
self.REST.sendMessage ('ogagent/stopped', {'mac': self.mac, 'ip': self.IPlocal, 'idcentro': self.idcentro, 'idaula': self.idaula, self.REST.sendMessage ('ogAdmClient/stopped', {'mac': self.mac, 'ip': self.IPlocal, 'idcentro': self.idcentro, 'idaula': self.idaula,
'idordenador': self.idordenador, 'nombreordenador': self.nombreordenador}) 'idordenador': self.idordenador, 'nombreordenador': self.nombreordenador})
#def processClientMessage (self, message, data):
# logger.debug ('Got OpenGnsys message from client: {}, data {}'.format (message, data))
#def onLogin (self, data):
# logger.warning ('in onLogin, should not happen')
#def onLogout (self, user):
# logger.warning ('in onLogout, should not happen')
#@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'}
## 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)
@ -168,7 +202,7 @@ class ogAdmClientWorker (ogLiveWorker):
logger.warning ('Ha ocurrido algún problema en el proceso de inclusión del cliente') logger.warning ('Ha ocurrido algún problema en el proceso de inclusión del cliente')
logger.error ('LeeConfiguracion() failed') logger.error ('LeeConfiguracion() failed')
return False return False
res = self.enviaMensajeServidor ('InclusionCliente', { 'cfg': self.cfg2obj (cfg), 'secret': self.random, 'agent_version': VERSION }) res = self.enviaMensajeServidor ('InclusionCliente', { 'cfg': self.cfg2obj (cfg) })
logger.debug ('res ({})'.format (res)) logger.debug ('res ({})'.format (res))
## RESPUESTA_InclusionCliente ## RESPUESTA_InclusionCliente
@ -274,9 +308,6 @@ class ogAdmClientWorker (ogLiveWorker):
def onActivation (self): def onActivation (self):
super().onActivation() super().onActivation()
self.exec_level = 'full'
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(32))
logger.info ('Inicio de sesion') logger.info ('Inicio de sesion')
logger.info ('Abriendo sesión en el servidor de Administración') logger.info ('Abriendo sesión en el servidor de Administración')
if (not self.inclusionCliente()): if (not self.inclusionCliente()):
@ -303,10 +334,35 @@ class ogAdmClientWorker (ogLiveWorker):
logger.info ('onActivation ok') logger.info ('onActivation ok')
@check_secret
def process_status (self, path, get_params, post_params, server):
logger.debug ('in process_status, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
full_config = 'full-config' in post_params and post_params['full-config']
thr_status = {}
for k in self.thread_list:
thr_status[k] = {
'running': self.thread_list[k]['running'],
'result': self.thread_list[k]['result'],
}
ret = {
'nfn': 'RESPUESTA_status',
'mac': self.mac,
'st': 'OGL',
'ip': self.IPlocal,
'threads': thr_status,
}
if full_config:
cfg = self.LeeConfiguracion()
ret['cfg'] = self.cfg2obj (cfg)
return ret
@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 do_CrearImagen (self, post_params): def do_CrearImagen (self, post_params):
for k in ['dsk', 'par', 'cpt', 'idi', 'nci', 'ipr', 'nfn', 'ids']: for k in ['dsk', 'par', 'cpt', 'idi', 'nci', 'ipr', 'nfn', 'ids']:
@ -699,54 +755,16 @@ class ogAdmClientWorker (ogLiveWorker):
@execution_level('status')
def process_status (self, path, get_params, post_params, server):
logger.debug ('in process_status, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
full_config = 'full-config' in post_params and post_params['full-config']
thr_status = {}
for k in self.thread_list:
thr_status[k] = {
'running': self.thread_list[k]['running'],
'result': self.thread_list[k]['result'],
}
ret = {
'nfn': 'RESPUESTA_status',
'mac': self.mac,
'st': 'OGL',
'ip': self.IPlocal,
'threads': thr_status,
}
if full_config:
cfg = self.LeeConfiguracion()
ret['cfg'] = self.cfg2obj (cfg)
return ret
@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'}
@execution_level('full')
@check_secret
def process_Actualizar (self, path, get_params, post_params, server): def process_Actualizar (self, path, get_params, post_params, server):
logger.debug ('in process_Actualizar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Actualizar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Actualizar', self.do_Actualizar, args=(post_params,)) return self._long_running_job ('Actualizar', self.do_Actualizar, args=(post_params,))
@execution_level('full')
@check_secret
def process_Purgar (self, path, get_params, post_params, server): def process_Purgar (self, path, get_params, post_params, server):
logger.debug ('in process_Purgar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Purgar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
os.kill (os.getpid(), signal.SIGTERM) os.kill (os.getpid(), signal.SIGTERM)
return {} return {}
#exit (0) ## ogAdmClient.c:905 #exit (0) ## ogAdmClient.c:905
@execution_level('full')
@check_secret
def process_Comando (self, path, get_params, post_params, server): def process_Comando (self, path, get_params, post_params, server):
logger.debug ('in process_Comando, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Comando, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Comando', self.do_Comando, args=(post_params,)) return self._long_running_job ('Comando', self.do_Comando, args=(post_params,))
@ -755,14 +773,10 @@ class ogAdmClientWorker (ogLiveWorker):
logger.debug ('in process_Sondeo, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Sondeo, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return {} ## ogAdmClient.c:920 return {} ## ogAdmClient.c:920
@execution_level('full')
@check_secret
def process_ConsolaRemota (self, path, get_params, post_params, server): def process_ConsolaRemota (self, path, get_params, post_params, server):
logger.debug ('in process_ConsolaRemota, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_ConsolaRemota, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('ConsolaRemota', self.do_ConsolaRemota, args=(post_params,)) return self._long_running_job ('ConsolaRemota', self.do_ConsolaRemota, args=(post_params,))
@execution_level('full')
@check_secret
def process_Arrancar (self, path, get_params, post_params, server): def process_Arrancar (self, path, get_params, post_params, server):
logger.debug ('in process_Arrancar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Arrancar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
@ -779,38 +793,26 @@ class ogAdmClientWorker (ogLiveWorker):
} }
return self.respuestaEjecucionComando (cmd, 0, ids) return self.respuestaEjecucionComando (cmd, 0, ids)
@execution_level('halt')
@check_secret
def process_Apagar (self, path, get_params, post_params, server): def process_Apagar (self, path, get_params, post_params, server):
logger.debug ('in process_Apagar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Apagar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Apagar', self.do_Apagar, args=(post_params,)) return self._long_running_job ('Apagar', self.do_Apagar, args=(post_params,))
@execution_level('halt')
@check_secret
def process_Reiniciar (self, path, get_params, post_params, server): def process_Reiniciar (self, path, get_params, post_params, server):
logger.debug ('in process_Reiniciar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Reiniciar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Reiniciar', self.do_Reiniciar, args=(post_params,)) return self._long_running_job ('Reiniciar', self.do_Reiniciar, args=(post_params,))
@execution_level('full')
@check_secret
def process_IniciarSesion (self, path, get_params, post_params, server): def process_IniciarSesion (self, path, get_params, post_params, server):
logger.debug ('in process_IniciarSesion, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_IniciarSesion, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('IniciarSesion', self.do_IniciarSesion, args=(post_params,)) return self._long_running_job ('IniciarSesion', self.do_IniciarSesion, args=(post_params,))
@execution_level('full')
@check_secret
def process_EjecutarScript (self, path, get_params, post_params, server): def process_EjecutarScript (self, path, get_params, post_params, server):
logger.debug ('in process_EjecutarScript, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_EjecutarScript, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('EjecutarScript', self.do_EjecutarScript, args=(post_params,)) return self._long_running_job ('EjecutarScript', self.do_EjecutarScript, args=(post_params,))
@execution_level('full')
@check_secret
def process_EjecutaComandosPendientes (self, path, get_params, post_params, server): def process_EjecutaComandosPendientes (self, path, get_params, post_params, server):
logger.debug ('in process_EjecutaComandosPendientes, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_EjecutaComandosPendientes, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return {'true':'true'} ## ogAdmClient.c:2138 return {'true':'true'} ## ogAdmClient.c:2138
@execution_level('full')
@check_secret
def process_CrearImagen (self, path, get_params, post_params, server): def process_CrearImagen (self, path, get_params, post_params, server):
logger.debug ('in process_CrearImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_CrearImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params))) logger.debug ('type(post_params) "{}"'.format (type (post_params)))
@ -826,8 +828,6 @@ class ogAdmClientWorker (ogLiveWorker):
# logger.warning ('this method has been removed') # logger.warning ('this method has been removed')
# raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' }) # raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
@execution_level('full')
@check_secret
def process_RestaurarImagen (self, path, get_params, post_params, server): def process_RestaurarImagen (self, path, get_params, post_params, server):
logger.debug ('in process_RestaurarImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_RestaurarImagen, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params))) logger.debug ('type(post_params) "{}"'.format (type (post_params)))
@ -844,27 +844,18 @@ class ogAdmClientWorker (ogLiveWorker):
# logger.warning ('this method has been removed') # logger.warning ('this method has been removed')
# raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' }) # raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
## una partición + cache en disco de 30 Gb:
@execution_level('full')
@check_secret
def process_Configurar (self, path, get_params, post_params, server): def process_Configurar (self, path, get_params, post_params, server):
logger.debug ('in process_Configurar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_Configurar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('Configurar', self.do_Configurar, args=(post_params,)) return self._long_running_job ('Configurar', self.do_Configurar, args=(post_params,))
@execution_level('full')
@check_secret
def process_InventarioHardware (self, path, get_params, post_params, server): def process_InventarioHardware (self, path, get_params, post_params, server):
logger.debug ('in process_InventarioHardware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_InventarioHardware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('InventarioHardware', self.do_InventarioHardware, args=(post_params,)) return self._long_running_job ('InventarioHardware', self.do_InventarioHardware, args=(post_params,))
@execution_level('full')
@check_secret
def process_InventarioSoftware (self, path, get_params, post_params, server): def process_InventarioSoftware (self, path, get_params, post_params, server):
logger.debug ('in process_InventarioSoftware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_InventarioSoftware, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return self._long_running_job ('InventarioSoftware', self.do_InventarioSoftware, args=(post_params,)) return self._long_running_job ('InventarioSoftware', self.do_InventarioSoftware, args=(post_params,))
@execution_level('full')
@check_secret
def process_KillJob (self, path, get_params, post_params, server): def process_KillJob (self, path, get_params, post_params, server):
logger.debug ('in process_KillJob, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server)) logger.debug ('in process_KillJob, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
jid = post_params['job_id'] jid = post_params['job_id']

View File

@ -36,8 +36,6 @@ import logging
import os import os
import tempfile import tempfile
from ..log_format import JsonFormatter
# Valid logging levels, from UDS Broker (uds.core.utils.log) # Valid logging levels, from UDS Broker (uds.core.utils.log)
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6)) OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
@ -46,24 +44,13 @@ class LocalLogger(object):
def __init__(self): def __init__(self):
# tempdir is different for "user application" and "service" # tempdir is different for "user application" and "service"
# service wil get c:\windows\temp, while user will get c:\users\XXX\appdata\local\temp # service wil get c:\windows\temp, while user will get c:\users\XXX\appdata\local\temp
logging.basicConfig(
fname1 = os.path.join (tempfile.gettempdir(), 'opengnsys.log') filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'),
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s') filemode='a',
fh1 = logging.FileHandler (filename=fname1, mode='a') format='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s',
fh1.setFormatter (fmt1) level=logging.DEBUG
fh1.setLevel (logging.DEBUG) )
fname2 = os.path.join (tempfile.gettempdir(), 'opengnsys.json.log')
fmt2 = JsonFormatter ({"timestamp": "asctime", "severity": "levelname", "threadName": "threadName", "function": "funcName", "message": "message"}, time_format='%Y-%m-%d %H:%M:%S', msec_format='')
fh2 = logging.FileHandler (filename=fname2, mode='a')
fh2.setFormatter (fmt2)
fh2.setLevel (logging.DEBUG)
self.logger = logging.getLogger('opengnsys') self.logger = logging.getLogger('opengnsys')
self.logger.setLevel (logging.DEBUG)
self.logger.addHandler (fh1)
self.logger.addHandler (fh2)
self.serviceLogger = False self.serviceLogger = False
def log(self, level, message): def log(self, level, message):

View File

@ -198,7 +198,7 @@ class ogLiveWorker(ServerWorker):
progress = None progress = None
for i in ary: for i in ary:
if m := re.search (r'^\[([0-9]+)\]', i): ## look for strings like '[10]', '[60]' if m := re.search (r'^\[([0-9]+)\]', i): ## look for strings like '[10]', '[60]'
#logger.debug (f"matched regex, m.groups ({m.groups()})") logger.debug (f"matched regex, m.groups ({m.groups()})")
progress = float (m.groups()[0]) / 100 progress = float (m.groups()[0]) / 100
return progress return progress
@ -211,7 +211,7 @@ class ogLiveWorker(ServerWorker):
for k in self.thread_list: for k in self.thread_list:
elem = self.thread_list[k] elem = self.thread_list[k]
if 'thread' not in elem: continue if 'thread' not in elem: continue
#logger.debug (f'considering thread ({k})') logger.debug (f'considering thread ({k})')
if self.pid_q: if self.pid_q:
if not self.pid_q.empty(): if not self.pid_q.empty():
@ -240,20 +240,11 @@ class ogLiveWorker(ServerWorker):
time.sleep (1) time.sleep (1)
n += 1 n += 1
if not n % 10: if not n % 10:
alive_threads = []
for k in self.thread_list:
elem = self.thread_list[k]
if 'thread' not in elem: continue
alive_threads.append (k)
if alive_threads:
s = ','.join (alive_threads)
logger.debug (f'alive threads: {s}')
body = { body = {
'iph': self.IPlocal, 'iph': self.IPlocal,
'timestamp': int (time.time()), 'timestamp': int (time.time()),
} }
#logger.debug (f'about to send ping ({body})') logger.debug (f'about to send ping ({body})')
self.REST.sendMessage ('clients/status/webhook', body) self.REST.sendMessage ('clients/status/webhook', body)
def interfaceAdmin (self, method, parametros=[]): def interfaceAdmin (self, method, parametros=[]):
@ -286,8 +277,7 @@ class ogLiveWorker(ServerWorker):
self.pid_q.put (p.pid) self.pid_q.put (p.pid)
else: else:
## esto sucede por ejemplo cuando arranca el agente, que estamos en interfaceAdmin() en el mismo hilo, sin _long_running_job ni hilo separado ## esto sucede por ejemplo cuando arranca el agente, que estamos en interfaceAdmin() en el mismo hilo, sin _long_running_job ni hilo separado
#logger.debug ('no queue--not writing any PID to it') logger.debug ('no queue--not writing any PID to it')
pass
sout = serr = '' sout = serr = ''
while p.poll() is None: while p.poll() is None:
@ -303,12 +293,12 @@ class ogLiveWorker(ServerWorker):
serr = serr.strip() serr = serr.strip()
## DEBUG ## DEBUG
logger.debug (f'stdout follows:') logger.info (f'stdout follows:')
for l in sout.splitlines(): for l in sout.splitlines():
logger.debug (f' {l}') logger.info (f' {l}')
#logger.debug (f'stderr follows:') logger.info (f'stderr follows:')
#for l in serr.splitlines(): for l in serr.splitlines():
# logger.debug (f' {l}') logger.info (f' {l}')
## /DEBUG ## /DEBUG
if 0 != p.returncode: if 0 != p.returncode:
cmd_txt = ' '.join (proc) cmd_txt = ' '.join (proc)
@ -347,7 +337,9 @@ class ogLiveWorker(ServerWorker):
res = self.REST.sendMessage ('/'.join ([self.name, path]), obj) res = self.REST.sendMessage ('/'.join ([self.name, path]), obj)
if (type (res) is not dict): if (type (res) is not dict):
logger.error (f'response is not a dict ({res})') #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 False
return res return res
@ -374,7 +366,9 @@ class ogLiveWorker(ServerWorker):
p = subprocess.Popen (['/usr/bin/browser', '-qws', url]) p = subprocess.Popen (['/usr/bin/browser', '-qws', url])
try: try:
p.wait (2) ## if the process dies before 2 seconds... p.wait (2) ## if the process dies before 2 seconds...
logger.error ('Error al ejecutar browser, return code "{}"'.format (p.returncode)) 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 return False
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
pass pass
@ -394,7 +388,7 @@ class ogLiveWorker(ServerWorker):
logger.error (e) logger.error (e)
logger.error ('No se ha podido recuperar la dirección IP del cliente') logger.error ('No se ha podido recuperar la dirección IP del cliente')
return None return None
#logger.debug ('parametroscfg ({})'.format (parametroscfg)) logger.debug ('parametroscfg ({})'.format (parametroscfg))
return parametroscfg return parametroscfg
def cfg2obj (self, cfg): def cfg2obj (self, cfg):
@ -449,16 +443,13 @@ class ogLiveWorker(ServerWorker):
self.urlMenu = self.service.config.get (self.name, 'urlMenu') self.urlMenu = self.service.config.get (self.name, 'urlMenu')
self.urlMsg = self.service.config.get (self.name, 'urlMsg') self.urlMsg = self.service.config.get (self.name, 'urlMsg')
ca_file = self.service.config.get (self.name, 'ca')
crt_file = self.service.config.get (self.name, 'crt')
key_file = self.service.config.get (self.name, 'key')
url = url.format (ogcore_scheme, ogcore_ip_port) url = url.format (ogcore_scheme, ogcore_ip_port)
self.urlMenu = self.urlMenu.format (urlmenu_scheme, urlmenu_ip_port) self.urlMenu = self.urlMenu.format (urlmenu_scheme, urlmenu_ip_port)
except NoOptionError as e: except NoOptionError as e:
logger.error ("Configuration error: {}".format (e)) logger.error ("Configuration error: {}".format (e))
raise e raise e
logger.setLevel (loglevel) logger.setLevel (loglevel)
self.REST = REST (url, ca_file=ca_file, crt_file=crt_file, key_file=key_file) self.REST = REST (url)
if not self.tomaIPlocal(): if not self.tomaIPlocal():
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo') raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')