Compare commits

..

No commits in common. "main" and "override-module" have entirely different histories.

27 changed files with 160 additions and 836 deletions

View File

@ -6,115 +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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [5.9.0] - 2025-06-16
### Added
- Added changes for oggit
## [5.8.1] - 2025-06-13
### Fixed
- Improve fail condition when no network is detected
## [5.8.0] - 2025-06-12
### Changed
- Agents aren't being sent any signals on user logout. On the server side, assume that client disconnection == logout
## [5.7.1] - 2025-06-05
### Fixed
- Correcly handle UNIX signals in the user instance of the agent
## [5.7.0] - 2025-05-27
### Changed
- Use TLS again
## [5.6.0] - 2025-05-21
### Changed
- Launch QT6 browser
- Change URLs using dbus
## [5.5.0] - 2025-05-19
### Changed
- Revert to the QT4 browser again
## [5.4.0] - 2025-05-19
### Changed
- Disabled TLS on request
## [5.3.0] - 2025-05-16
### Changed
- Execute 'launch_browser' rather than 'browser'
## [5.2.0] - 2025-05-14
### Added
- Log duration of user sessions
## [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
### Added
- Operating system: periodically ping ogcore
## [3.1.0] - 2025-04-07
### Added
- Oglive: periodically ping ogcore
## [3.0.0] - 2025-03-31
### Changed
- Ignore module provided in the URLs to the API
## [2.0.0] - 2025-03-26
### Changed

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,106 +1,3 @@
ogagent (5.9.0-1) stable; urgency=medium
* Add changes for oggit
-- OpenGnsys developers <info@opengnsys.es> Mon, 16 Jun 2025 11:12:55 +0200
ogagent (5.8.1-1) stable; urgency=medium
* Improve fail condition when no network is detected
-- OpenGnsys developers <info@opengnsys.es> Fri, 13 Jun 2025 10:01:43 +0200
ogagent (5.8.0-1) stable; urgency=medium
* When client disconnect, assume that the user logged out
-- OpenGnsys developers <info@opengnsys.es> Thu, 12 Jun 2025 15:30:50 +0200
ogagent (5.7.1-1) stable; urgency=medium
* Correctly handle UNIX signals
-- OpenGnsys developers <info@opengnsys.es> Thu, 05 Jun 2025 12:07:30 +0200
ogagent (5.7.0-1) stable; urgency=medium
* Use TLS again
-- OpenGnsys developers <info@opengnsys.es> Wed, 21 May 2025 17:39:13 +0200
ogagent (5.6.0-1) stable; urgency=medium
* Execute 'launch_browser' rather than 'browser'
* Change URLs using dbus
-- OpenGnsys developers <info@opengnsys.es> Wed, 21 May 2025 15:06:52 +0200
ogagent (5.5.0-1) stable; urgency=medium
* Return to the QT4 browser again
-- OpenGnsys developers <info@opengnsys.es> Mon, 19 May 2025 10:57:37 +0200
ogagent (5.4.0-1) stable; urgency=medium
* Disable TLS on request
-- OpenGnsys developers <info@opengnsys.es> Mon, 19 May 2025 09:46:42 +0200
ogagent (5.3.0-1) stable; urgency=medium
* Execute 'launch_browser' rather than 'browser'
-- OpenGnsys developers <info@opengnsys.es> Wed, 14 May 2025 10:50:15 +0200
ogagent (5.2.0-1) stable; urgency=medium
* Log length of user sessions
-- OpenGnsys developers <info@opengnsys.es> Mon, 12 May 2025 11:38:27 +0200
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
* Oglive: periodically ping ogcore
-- OpenGnsys developers <info@opengnsys.es> Mon, 07 Apr 2025 11:50:05 +0200
ogagent (3.0.0-1) stable; urgency=medium
* Ignore module provided in the URLs to the API

View File

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

View File

@ -35,8 +35,6 @@ import json
import sys
import time
import os
import socket
import signal
from PyQt6 import QtCore, QtGui, QtWidgets
from about_dialog_ui import Ui_OGAAboutDialog
@ -330,29 +328,6 @@ if __name__ == '__main__':
trayIcon.quit()
sys.exit(1)
## begin SIGTERM handling
signal_socket = socket.socketpair()
signal_socket[0].setblocking(False)
signal_socket[1].setblocking(False)
signal.set_wakeup_fd(signal_socket[0].fileno())
def signal_handler(signum, frame):
#print (f"Received signal {signum}")
pass
def qt_signal_handler():
data = signal_socket[1].recv(1)
#print(f"Signal ({data}) received via socket, shutting down gracefully...")
if trayIcon:
trayIcon.quit()
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
notifier = QtCore.QSocketNotifier(signal_socket[1].fileno(), QtCore.QSocketNotifier.Type.Read)
notifier.activated.connect(qt_signal_handler)
## end SIGTERM handling
app.aboutToQuit.connect(trayIcon.cleanup)
trayIcon.show()

View File

@ -1 +1 @@
5.9.0
3.0.0

View File

@ -17,16 +17,6 @@ level=full
# Log Level, if omitted, will be set to INFO
log=DEBUG
imgname=
# TLS
# The agent will look for these files in /opt/opengnsys/etc, /usr/share/OGAgent,
# windows "Program Files (x86)" and the current working directory
ca=ca.crt
crt=ogagent.crt
key=ogagent.key
# Module specific
# The sections must match the module name
# This section will be passes on activation to module
@ -38,8 +28,3 @@ log=DEBUG
pathinterface=/opt/opengnsys/interfaceAdm
urlMenu={}://{}/menu-browser
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 logging
import json
@ -44,8 +43,8 @@ from .log import logger
from .utils import exceptionToMessage
VERIFY_CERT = False # Do not check server certificate
TIMEOUT = 5 # Connection timout, in seconds
VERIFY_TLS=True
class RESTError(Exception):
@ -59,12 +58,8 @@ class ConnectionError(RESTError):
# Disable warnings log messages
try:
import urllib3 # @UnusedImport
requests_log = logging.getLogger ('urllib3')
requests_log.setLevel (logging.INFO)
except Exception:
from requests.packages import urllib3 # @Reimport
requests_log = logging.getLogger ('requests.packages.urllib3')
requests_log.setLevel (logging.INFO)
try:
urllib3.disable_warnings() # @UndefinedVariable
@ -89,14 +84,13 @@ class REST(object):
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
url is the full url of the REST API Base, as for example "https://example.com/rest/v1".
@param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired.
"""
self.endpoint = url
global VERIFY_TLS
if self.endpoint[-1] != '/':
self.endpoint += '/'
@ -107,52 +101,6 @@ class REST(object):
except Exception:
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')
certs_dirs = ['/opt/opengnsys/etc', '/usr/share/OGAgent']
pf = os.environ.get ('PROGRAMFILES(X86)')
if pf: certs_dirs.append (os.path.join (pf, 'OGAgent'))
certs_dirs.append (os.getcwd())
certs_dir = None
for sp in certs_dirs:
if os.path.exists (sp):
logger.debug (f'Looking for TLS files in ({sp})')
certs_dir = sp
break
if not certs_dir:
logger.debug ("Don't know where to look for TLS files")
errs = 1
else:
errs = 0
for f in [ca_file, crt_file, key_file]:
if os.path.exists (f'{certs_dir}/{f}'):
logger.debug (f'{certs_dir}/{f}: found')
else:
logger.error (f'{f}: No such file or directory')
errs += 1
if errs:
self.verify_tls = False
logger.debug ('HTTP client: using insecure TLS to talk to ogcore due to missing files')
else:
self.ca_file = f'{certs_dir}/{ca_file}'
self.crt_file = f'{certs_dir}/{crt_file}'
self.key_file = f'{certs_dir}/{key_file}'
self.verify_tls = VERIFY_TLS
if self.verify_tls:
logger.debug ('HTTP client: using TLS to talk to ogcore')
else:
logger.debug ('HTTP client: using insecure TLS as requested to talk to ogcore')
else:
logger.debug ('HTTP client: not using TLS to talk to ogcore')
# Disable logging requests messages except for errors, ...
logging.getLogger("requests").setLevel(logging.CRITICAL)
# Tries to disable all warnings
@ -183,27 +131,14 @@ class REST(object):
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
if self.newerRequestLib:
if self.use_tls:
if self.verify_tls:
r = requests.get(url, cert=(self.crt_file, self.key_file), verify=self.ca_file, timeout=TIMEOUT)
else:
logger.warning ('using insecure TLS for GET')
r = requests.get(url, verify=False, timeout=TIMEOUT)
else:
r = requests.get(url, timeout=TIMEOUT)
r = requests.get(url, verify=VERIFY_CERT, timeout=TIMEOUT)
else:
r = requests.get(url)
else: # POST
logger.debug('Requesting using POST {}, data: {}'.format(url, data))
if self.newerRequestLib:
if self.use_tls:
if self.verify_tls:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, cert=(self.crt_file, self.key_file), verify=self.ca_file, timeout=TIMEOUT)
else:
logger.warning ('using insecure TLS for POST')
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=False, timeout=TIMEOUT)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, timeout=TIMEOUT)
r = requests.post(url, data=data, headers={'content-type': 'application/json'},
verify=VERIFY_CERT, timeout=TIMEOUT)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'})
@ -213,9 +148,7 @@ class REST(object):
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
except requests.exceptions.RequestException as e:
code = e.response.status_code
logger.warning (f'request failed, HTTP code "{code}"')
return None
raise ConnectionError(e)
except Exception as e:
raise ConnectionError(exceptionToMessage(e))
@ -227,13 +160,13 @@ class REST(object):
@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"
"""
#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:
data = json.dumps(data)
url = self._getUrl(msg)
#logger.debug('Requesting {}'.format(url))
logger.debug('Requesting {}'.format(url))
try:
res = self._request(url, data)

View File

@ -43,7 +43,6 @@ from .utils import exceptionToMessage
from .certs import createSelfSignedCert
from .log import logger
VERIFY_TLS=True
class HTTPServerHandler(BaseHTTPRequestHandler):
service = None
@ -154,46 +153,15 @@ class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
class HTTPServerThread(threading.Thread):
def __init__(self, address, service):
super(self.__class__, self).__init__()
global VERIFY_TLS
HTTPServerHandler.service = service # Keep tracking of service so we can intercact with it
self.certFile = createSelfSignedCert()
self.server = HTTPThreadingServer(address, HTTPServerHandler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
pf = os.environ.get ('PROGRAMFILES(X86)')
if pf: pf = os.path.join (pf, 'OGAgent')
if os.path.exists ('/opt/opengnsys/etc/ogagent.crt') and os.path.exists ('/opt/opengnsys/etc/ogagent.key') and os.path.exists ('/opt/opengnsys/etc/ca.crt'):
logger.debug ('HTTP server: using certificate/CA from /opt/opengnsys/etc')
context.load_cert_chain (certfile='/opt/opengnsys/etc/ogagent.crt', keyfile='/opt/opengnsys/etc/ogagent.key')
context.load_verify_locations (cafile='/opt/opengnsys/etc/ca.crt')
elif pf and os.path.exists (os.path.join (pf, 'ogagent.crt')) and os.path.exists (os.path.join (pf, 'ogagent.key')) and os.path.exists (os.path.join (pf, 'ca.crt')):
logger.debug (f'HTTP server: using certificate/CA from the installation path ({pf})')
context.load_cert_chain (certfile=os.path.join (pf, 'ogagent.crt'), keyfile=os.path.join (pf, 'ogagent.key'))
context.load_verify_locations (cafile=os.path.join (pf, 'ca.crt'))
elif os.path.exists ('./ogagent.crt') and os.path.exists ('./ogagent.key') and os.path.exists ('./ca.crt'):
cwd = os.getcwd()
logger.debug (f'HTTP server: using certificate/CA from the current working directory ({cwd})')
context.load_cert_chain (certfile=f'{cwd}/ogagent.crt', keyfile=f'{cwd}/ogagent.key')
context.load_verify_locations (cafile=f'{cwd}/ca.crt')
else:
logger.debug ('HTTP server: using a self-signed certificate')
self.certFile = createSelfSignedCert()
context.load_cert_chain (certfile=self.certFile)
VERIFY_TLS = False
if VERIFY_TLS:
context.verify_mode = ssl.CERT_REQUIRED
context.verify_flags &= ssl.VERIFY_X509_STRICT
else:
context.verify_mode = ssl.CERT_NONE
context.verify_flags &= ~ssl.VERIFY_X509_STRICT
s = context.cert_store_stats()
if 'x509_ca' in s: logger.debug (f'HTTP server: {s['x509_ca']} CAs loaded')
if 'x509' in s: logger.debug (f'HTTP server: {s['x509']} certs loaded')
context.load_cert_chain(certfile=self.certFile)
self.server.socket = context.wrap_socket(self.server.socket, server_side=True)
logger.debug('Initialized HTTPS Server thread on {}'.format(address))
def getServerUrl(self):

View File

@ -30,7 +30,6 @@
"""
import os
import json
import queue
import socket
@ -194,8 +193,6 @@ class ClientProcessor(threading.Thread):
logger.error('Invalid message in queue: {}'.format(e))
logger.debug('Client processor stopped')
if os.path.exists ('/windows/temp'): open ('/windows/temp/ogagentuser_died', 'w').close()
else: open ( '/tmp/ogagentuser_died', 'w').close()
try:
self.clientSocket.close()
except Exception:

View File

@ -34,8 +34,6 @@ import logging
import os
import tempfile
from ..log_format import JsonFormatter
# Logging levels
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()):
try:
fname1 = os.path.join (logDir, 'opengnsys.log')
fmt1 = logging.Formatter (fmt='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s')
fh1 = logging.FileHandler (filename=fname1, mode='a')
fh1.setFormatter (fmt1)
fh1.setLevel (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.setLevel (logging.DEBUG)
self.logger.addHandler (fh1)
self.logger.addHandler (fh2)
os.chmod (fname1, 0o0600)
os.chmod (fname2, 0o0600)
fname = os.path.join(logDir, 'opengnsys.log')
logging.basicConfig(
filename=fname,
filemode='a',
format='%(levelname)s %(asctime)s (%(threadName)s) (%(funcName)s) %(message)s',
level=logging.DEBUG
)
self.logger = logging.getLogger('opengnsys')
os.chmod(fname, 0o0600)
return
except Exception:
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

@ -30,6 +30,7 @@
@author: Ramón M. Gómez, ramongomez at us dot es
"""
import base64
import os
import random
@ -101,20 +102,6 @@ class OpenGnSysWorker(ServerWorker):
exec_level = None # Execution level (permitted operations)
jobmgr = JobMgr()
## pings ogcore
def mon (self):
n = 0
while True:
time.sleep (1)
n += 1
if not n % 10:
body = {
"iph": self.interface.ip,
"timestamp": int (time.time()),
}
logger.debug (f'about to send ping ({body})')
self.REST.sendMessage ('clients/status/webhook', body)
def onActivation(self):
"""
Sends OGAgent activation notification to OpenGnsys server
@ -130,14 +117,11 @@ class OpenGnSysWorker(ServerWorker):
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(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')
url = self.service.config.get(self.name, 'remote')
except NoOptionError as e:
logger.error("Configuration error: {}".format(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)
try:
self.exec_level = self.service.config.get(self.name, 'level')
@ -147,11 +131,7 @@ class OpenGnSysWorker(ServerWorker):
for t in range(0, 300):
try:
# Get the first network interface
nets = list (operations.getNetworkInfo())
if 0 == len (nets):
logger.error ('No network interfaces found')
raise Exception ('No network interfaces found')
self.interface = nets[0]
self.interface = list(operations.getNetworkInfo())[0]
except Exception as e:
# Wait 1 sec. and retry
logger.warn (e)
@ -181,7 +161,7 @@ class OpenGnSysWorker(ServerWorker):
logger.warn (str (e))
# Trying to initialize on alternative server, if defined
# (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,
'secret': self.random, 'ostype': operations.os_type,
'osversion': operations.os_version, 'alt_url': True,
@ -209,18 +189,12 @@ class OpenGnSysWorker(ServerWorker):
if os.path.isfile(new_hosts_file):
shutil.copyfile(new_hosts_file, hosts_file)
threading.Thread (name='monitoring_thread', target=self.mon, daemon=True).start()
logger.debug ('onActivation ok')
def onDeactivation(self):
"""
Sends OGAgent stopping notification to OpenGnsys server
"""
now = time.time()
for elem in self.user:
sess_len = now - elem['login_ts']
logger.debug ('Session of logged in user {} took {} seconds'.format (elem['username'], int (sess_len)))
logger.debug('onDeactivation')
self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip,
'ostype': operations.os_type, 'osversion': operations.os_version})
@ -231,7 +205,7 @@ class OpenGnSysWorker(ServerWorker):
"""
user, language, self.session_type = tuple(data.split(','))
logger.debug('Received login for {0} using {2} with language {1}'.format(user, language, self.session_type))
self.user.append ({'username': user, 'login_ts': time.time() })
self.user.append(user)
self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, 'user': user, 'language': language,
'session': self.session_type,
'ostype': operations.os_type, 'osversion': operations.os_version})
@ -240,11 +214,7 @@ class OpenGnSysWorker(ServerWorker):
"""
Sends session logout notification to OpenGnsys server
"""
sess_len = 0
for elem in self.user:
if user != elem['username']: continue
sess_len = time.time() - elem['login_ts']
logger.debug ('Received logout for {}, session length {} seconds'.format (user, int (sess_len)))
logger.debug('Received logout for {}'.format(user))
try:
self.user.pop()
except IndexError:
@ -300,7 +270,7 @@ class OpenGnSysWorker(ServerWorker):
if get_params.get('detail', 'false') == 'true':
res.update({'agent_version': VERSION, 'os_version': operations.os_version, 'sys_load': os.getloadavg()})
if res['loggedin']:
res.update({'sessions': len(self.user), 'current_user': self.user[-1]['username']})
res.update({'sessions': len(self.user), 'current_user': self.user[-1]})
except KeyError:
# Unknown operating system
res = {'status': 'UNK'}

View File

@ -33,15 +33,14 @@
"""
import base64
#import threading
#import time
import os
import signal
import string
import random
import subprocess
from pathlib import Path
from urllib.parse import unquote
from opengnsys import VERSION
from opengnsys.log import logger
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.
"""
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)
if 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 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
# 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):
name = 'ogAdmClient' # Module name
REST = None # REST object
@ -95,8 +75,62 @@ class ogAdmClientWorker (ogLiveWorker):
Sends OGAgent stopping notification to OpenGnsys server
"""
logger.debug ('onDeactivation')
self.REST.sendMessage ('ogagent/stopped', {'mac': self.mac, 'ip': self.IPlocal, 'idcentro': self.idcentro, 'idaula': self.idaula,
'idordenador': self.idordenador, 'nombreordenador': self.nombreordenador})
self.REST.sendMessage ('ogAdmClient/stopped', {'mac': self.mac, 'ip': self.IPlocal, 'idcentro': self.idcentro, 'idaula': self.idaula,
'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.error ('LeeConfiguracion() failed')
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))
## RESPUESTA_InclusionCliente
@ -274,9 +308,6 @@ class ogAdmClientWorker (ogLiveWorker):
def onActivation (self):
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 ('Abriendo sesión en el servidor de Administración')
if (not self.inclusionCliente()):
@ -303,10 +334,35 @@ class ogAdmClientWorker (ogLiveWorker):
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):
for k in ['dsk', 'par', 'cpt', 'idi', 'nci', 'ipr', 'nfn', 'ids']:
@ -360,60 +416,6 @@ class ogAdmClientWorker (ogLiveWorker):
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_CrearImagenGit (self, post_params):
for k in ['dsk', 'par', 'cpt', 'idi', 'nci', 'ipr', 'nfn', 'ids']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
dsk = post_params['dsk'] ## Disco
par = post_params['par'] ## Número de partición
cpt = post_params['cpt'] ## Código de la partición
idi = post_params['idi'] ## Identificador de la imagen
nci = post_params['nci'] ## Nombre canónico de la imagen
ipr = post_params['ipr'] ## Ip del repositorio
nfn = post_params['nfn']
ids = post_params['ids']
tag = post_params['tag'] ## Tag a crear en git una vez hecho el commit
self.muestraMensaje (7)
try:
res = self.InventariandoSoftware (dsk, par, 'InventarioSoftware') ## Crea inventario Software previamente
except:
logger.warning ('Error al ejecutar el comando')
return {}
if res['contents']:
self.muestraMensaje (2)
inv_sft = res['contents']
try:
self.interfaceAdmin (nfn, [dsk, par, nci, ipr, tag])
self.muestraMensaje (9)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (10)
herror = 1
else:
logger.warning ('Error al ejecutar el comando')
herror = 1
inv_sft = ''
self.muestraMenu()
cmd = {
'nfn': 'RESPUESTA_CrearImagenGit',
'idi': idi, ## Identificador de la imagen
'dsk': dsk, ## Número de disco
'par': par, ## Número de partición de donde se creó
'cpt': cpt, ## Tipo o código de partición
'ipr': ipr, ## Ip del repositorio donde se alojó
'inv_sft': inv_sft
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_RestaurarImagen (self, post_params):
for k in ['dsk', 'par', 'idi', 'ipr', 'nci', 'ifs', 'ptc', 'nfn', 'ids']:
if k not in post_params:
@ -460,54 +462,6 @@ class ogAdmClientWorker (ogLiveWorker):
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_RestaurarImagenGit (self, post_params):
for k in ['dsk', 'par', 'idi', 'ipr', 'nci', 'ifs', 'ptc', 'nfn', 'ids', 'ref']:
if k not in post_params:
logger.error (f'required parameter ({k}) not in POST params')
return {}
dsk = post_params['dsk']
par = post_params['par']
idi = post_params['idi']
ipr = post_params['ipr']
nci = post_params['nci']
ifs = post_params['ifs']
ptc = post_params['ptc'] ## Protocolo de clonación: Unicast, Multicast, Torrent
nfn = post_params['nfn']
ids = post_params['ids']
ref = post_params['ref'] ## Referencia de git a restaurar
self.muestraMensaje (3)
try:
## the ptc.split() is useless right now, since interfaceAdmin() does ' '.join(params) in order to spawn a shell
## however we're going to need it in the future (when everything gets translated into python), plus it's harmless now. So let's do it
#self.interfaceAdmin (nfn, [dsk, par, nci, ipr, ptc])
self.interfaceAdmin (nfn, [dsk, par, nci, ipr, ref] + ptc.split())
self.muestraMensaje (11)
herror = 0
except:
logger.warning ('Error al ejecutar el comando')
self.muestraMensaje (12)
herror = 1
cfg = self.LeeConfiguracion()
if not cfg:
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
self.muestraMenu()
cmd = {
'nfn': 'RESPUESTA_RestaurarImagenGit',
'idi': idi, ## Identificador de la imagen
'dsk': dsk, ## Número de disco
'par': par, ## Número de partición
'ifs': ifs, ## Identificador del perfil software
'cfg': self.cfg2obj(cfg), ## Configuración de discos
}
return self.respuestaEjecucionComando (cmd, herror, ids)
def do_Configurar (self, post_params):
for k in ['nfn', 'dsk', 'cfg', 'ids']:
if k not in post_params:
@ -524,7 +478,7 @@ class ogAdmClientWorker (ogLiveWorker):
params = []
disk_info = cfg.pop (0)
logger.debug (f'disk_info ({disk_info})')
for k in ['dis']:
for k in ['dis', 'tch']:
params.append (f'{k}={disk_info[k]}')
disk_info_str = '*'.join (params)
@ -801,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):
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,))
@execution_level('full')
@check_secret
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))
os.kill (os.getpid(), signal.SIGTERM)
return {}
#exit (0) ## ogAdmClient.c:905
@execution_level('full')
@check_secret
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))
return self._long_running_job ('Comando', self.do_Comando, args=(post_params,))
@ -857,14 +773,10 @@ class ogAdmClientWorker (ogLiveWorker):
logger.debug ('in process_Sondeo, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
return {} ## ogAdmClient.c:920
@execution_level('full')
@check_secret
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))
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):
logger.debug ('in process_Arrancar, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
@ -881,53 +793,31 @@ class ogAdmClientWorker (ogLiveWorker):
}
return self.respuestaEjecucionComando (cmd, 0, ids)
@execution_level('halt')
@check_secret
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))
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):
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,))
@execution_level('full')
@check_secret
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))
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):
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,))
@execution_level('full')
@check_secret
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))
return {'true':'true'} ## ogAdmClient.c:2138
@execution_level('full')
@check_secret
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 ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('CrearImagen', self.do_CrearImagen, args=(post_params,))
def process_CrearImagenGit (self, path, get_params, post_params, server):
logger.debug ('in process_CrearImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('CrearImagenGit', self.do_CrearImagenGit, args=(post_params,))
def process_RestaurarImagenGit (self, path, get_params, post_params, server):
logger.debug ('in process_RestaurarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
return self._long_running_job ('RestaurarImagenGit', self.do_RestaurarImagenGit, args=(post_params,))
#def process_CrearImagenBasica (self, path, get_params, post_params, server):
# logger.debug ('in process_CrearImagenBasica, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
# logger.warning ('this method has been removed')
@ -938,8 +828,6 @@ class ogAdmClientWorker (ogLiveWorker):
# logger.warning ('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):
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)))
@ -956,27 +844,18 @@ class ogAdmClientWorker (ogLiveWorker):
# logger.warning ('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):
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,))
@execution_level('full')
@check_secret
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))
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):
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,))
@execution_level('full')
@check_secret
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))
jid = post_params['job_id']

View File

@ -30,7 +30,6 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import os
import json
import socket
import time
@ -198,16 +197,6 @@ class CommonService(object):
Invoked to wait a bit
CAN be OVERRIDDEN
"""
client_died=False
if os.path.exists ('/windows/temp/ogagentuser_died'):
os.unlink ('/windows/temp/ogagentuser_died')
client_died=True
elif os.path.exists ('/tmp/ogagentuser_died'):
os.unlink ('/tmp/ogagentuser_died')
client_died=True
if client_died:
self.notifyLogout (b'')
time.sleep(float(miliseconds) / 1000)
def notifyStop(self):

View File

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

View File

@ -33,8 +33,6 @@
import os
import re
import time
try: import dbus ## don't fail on windows (the worker will later refuse to load anyway)
except: pass
import random
import subprocess
import threading
@ -200,20 +198,17 @@ class ogLiveWorker(ServerWorker):
progress = None
for i in ary:
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
return progress
## monitors child threads, waits for them to finish
## pings ogcore
def mon (self):
n = 0
while True:
with self.thread_lock:
for k in self.thread_list:
elem = self.thread_list[k]
if 'thread' not in elem: continue
#logger.debug (f'considering thread ({k})')
logger.debug (f'considering thread ({k})')
if self.pid_q:
if not self.pid_q.empty():
@ -240,26 +235,9 @@ class ogLiveWorker(ServerWorker):
self.notifier (k, elem['result'])
time.sleep (1)
n += 1
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 = {
'iph': self.IPlocal,
'timestamp': int (time.time()),
}
#logger.debug (f'about to send ping ({body})')
self.REST.sendMessage ('clients/status/webhook', body)
def interfaceAdmin (self, method, parametros=[]):
if method in ['Apagar', 'CambiarAcceso', 'Configurar', 'CrearImagen', 'CrearImagenGit', 'EjecutarScript', 'getConfiguration', 'getIpAddress', 'IniciarSesion', 'InventarioHardware', 'InventarioSoftware', 'Reiniciar', 'RestaurarImagen', 'RestaurarImagenGit']:
if method in ['Apagar', 'CambiarAcceso', 'Configurar', 'CrearImagen', 'EjecutarScript', 'getConfiguration', 'getIpAddress', 'IniciarSesion', 'InventarioHardware', 'InventarioSoftware', 'Reiniciar', 'RestaurarImagen']:
## python
logger.debug (f'({method}) is a python method')
exe = '{}/{}.py'.format (self.pathinterface, method)
@ -288,8 +266,7 @@ class ogLiveWorker(ServerWorker):
self.pid_q.put (p.pid)
else:
## 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')
pass
logger.debug ('no queue--not writing any PID to it')
sout = serr = ''
while p.poll() is None:
@ -305,12 +282,12 @@ class ogLiveWorker(ServerWorker):
serr = serr.strip()
## DEBUG
logger.debug (f'stdout follows:')
logger.info (f'stdout follows:')
for l in sout.splitlines():
logger.debug (f' {l}')
#logger.debug (f'stderr follows:')
#for l in serr.splitlines():
# logger.debug (f' {l}')
logger.info (f' {l}')
logger.info (f'stderr follows:')
for l in serr.splitlines():
logger.info (f' {l}')
## /DEBUG
if 0 != p.returncode:
cmd_txt = ' '.join (proc)
@ -349,7 +326,9 @@ class ogLiveWorker(ServerWorker):
res = self.REST.sendMessage ('/'.join ([self.name, path]), obj)
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 res
@ -371,25 +350,17 @@ class ogLiveWorker(ServerWorker):
def cargaPaginaWeb (self, url=None):
if (not url): url = self.urlMenu
os.system ('pkill -9 browser')
dbus_address = os.environ.get ('DBUS_SESSION_BUS_ADDRESS')
if not dbus_address: logger.warning ('env var DBUS_SESSION_BUS_ADDRESS not set, cargaPaginaWeb() will likely not work')
b = dbus.SystemBus()
dest = 'es.opengnsys.OGBrowser.browser'
path = '/'
interface = None
method = 'setURL'
signature = 's'
p = subprocess.Popen (['/usr/bin/browser', '-qws', url])
try:
b.call_blocking (dest, path, interface, method, 's', [url])
except Exception as e:
if 'ServiceUnknown' in str(e):
## browser not running
subprocess.Popen (['/usr/bin/launch_browser', url])
else:
logger.error (f'Error al cambiar URL: ({e})')
return False
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
@ -406,7 +377,7 @@ class ogLiveWorker(ServerWorker):
logger.error (e)
logger.error ('No se ha podido recuperar la dirección IP del cliente')
return None
#logger.debug ('parametroscfg ({})'.format (parametroscfg))
logger.debug ('parametroscfg ({})'.format (parametroscfg))
return parametroscfg
def cfg2obj (self, cfg):
@ -461,16 +432,13 @@ class ogLiveWorker(ServerWorker):
self.urlMenu = self.service.config.get (self.name, 'urlMenu')
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)
self.urlMenu = self.urlMenu.format (urlmenu_scheme, urlmenu_ip_port)
except NoOptionError as e:
logger.error ("Configuration error: {}".format (e))
raise e
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():
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')