Feat(jobmgr): send START_SCRIPT webhook to mark trace as in_progress ; Fix(windows): prevent CLIXML output on errors by piping to Out-String
ogagent/pipeline/head This commit looks good Details

pull/76/head
Juan Manuel Bardallo 2026-07-22 14:19:52 +02:00
parent d0bde04e92
commit 70f85b1fc7
6 changed files with 75 additions and 6 deletions

View File

@ -1 +1 @@
8.5.6
8.5.9

View File

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

View File

@ -34,7 +34,7 @@
from . import modules
from .RESTApi import REST, RESTError
VERSION='0'
VERSION='8.5.6'
__title__ = 'OpenGnsys Agent'
__version__ = VERSION

View File

@ -53,6 +53,35 @@ class OpenGnSysWorker(ClientWorker):
job_id = self.jobmgr.launch_job (script, True)
self.sendServerMessage('script_launched', {'op': 'launched', 'job_id': job_id})
# Monitor the job in the background and notify the server when done
def monitor_job():
import time
import threading
job = self.jobmgr.jobs.get(job_id)
if not job:
return
p = job.get('p')
if not p:
return
p.wait()
# Wait for stdout/stderr threads to finish
if job.get('t1'): job['t1'].join()
if job.get('t2'): job['t2'].join()
logger.debug(f'Client job {job_id} finished, notifying server.')
self.sendServerMessage('script_finished', {
'job_id': job_id,
'rc': p.returncode,
'stderr': job.get('stderr', ''),
'stdout': job.get('stdout', '')
})
import threading
threading.Thread(target=monitor_job, daemon=True).start()
def process_terminatescript(self, json_params):
job_id = json_params['job_id']
logger.debug('Processing terminatescript request, job_id "{}"'.format (job_id))

View File

@ -103,6 +103,7 @@ class OpenGnSysWorker(ServerWorker):
exec_level = None # Execution level (permitted operations)
jobmgr = JobMgr()
_registered = False # Whether ogcore has acknowledged our ogagent/started
client_jobs = set() # Track active client job IDs for busy status
def _send_started(self):
"""Send ogagent/started to ogcore and mark as registered on success."""
@ -136,11 +137,14 @@ class OpenGnSysWorker(ServerWorker):
elem['status'] = 'finished'
elem['rc'] = elem['p'].returncode
stdout = elem.get('stdout', '')
stderr = elem.get('stderr', '')
der = stdout + ('\n' if stdout and stderr else '') + stderr
webhook_body = {
"nfn": "RUN_SCRIPT",
"nfn": "RESPUESTA_EjecutarScript",
"job_id": k,
"res": 1 if elem['rc'] == 0 else 0,
"der": elem.get('stderr', '')
"der": der
}
logger.info(f"Job {k} finished with return code {elem['rc']}. Sending webhook to ogcore.")
try:
@ -150,11 +154,13 @@ class OpenGnSysWorker(ServerWorker):
if not n % 10:
_st_map = {'linux': 'LNX', 'macos': 'OSX', 'windows': 'WIN'}
is_busy = any(j['p'].poll() is None for j in self.jobmgr.jobs.values()) or len(self.client_jobs) > 0
body = {
"iph": self.interface.ip,
"timestamp": int(time.time()),
"busy": any(j['p'].poll() is None for j in self.jobmgr.jobs.values()),
"busy": is_busy,
"st": _st_map.get(operations.os_type.lower(), 'UNK'),
"loggedin": len(self.user) > 0,
}
result = self.REST.sendMessage('clients/status/webhook', body)
if result is None:
@ -466,6 +472,13 @@ class OpenGnSysWorker(ServerWorker):
if post_params.get('client', 'false') == 'false':
job_id = self.jobmgr.launch_job (script, False)
try:
self.REST.sendMessage('clients/status/webhook', {
"nfn": "START_SCRIPT",
"job_id": job_id
})
except Exception as e:
logger.error(f"Failed to send START_SCRIPT webhook for job {job_id}: {e}")
return {'op': 'launched', 'job_id': job_id}
else: ## post_params.get('client') is not 'false'
@ -497,8 +510,33 @@ class OpenGnSysWorker(ServerWorker):
job_id_path = os.path.join(tempfile.gettempdir(), 'EjecutarScript-jobid')
with open (job_id_path, 'w') as fd:
fd.write (data['job_id'])
self.client_jobs.add(data['job_id'])
return True
def process_client_script_finished(self, data):
job_id = data['job_id']
rc = data['rc']
stdout = data.get('stdout', '')
stderr = data.get('stderr', '')
der = stdout + ('\n' if stdout and stderr else '') + stderr
if job_id in self.client_jobs:
self.client_jobs.remove(job_id)
logger.info(f"Client job {job_id} finished with return code {rc}. Sending webhook to ogcore.")
webhook_body = {
"nfn": "RESPUESTA_EjecutarScript",
"job_id": job_id,
"res": 1 if rc == 0 else 0,
"der": der
}
try:
self.REST.sendMessage('clients/status/webhook', webhook_body)
except Exception as e:
logger.error(f"Failed to send webhook for client job {job_id}: {e}")
return True
@execution_level('full')
@check_secret
def process_terminatescript(self, path, get_params, post_params, server):

View File

@ -151,6 +151,8 @@ def get_etc_path():
def build_popen_args(script):
## turn the script into utf16le, then to b64 again, and feed the blob to powershell
## wrap the script to redirect stderr and output as a simple string to avoid CLIXML wrappers in non-interactive errors
script = f"& {{ {script} }} 2>&1 | Out-String"
u16 = script.encode ('utf-16le') ## utf16
b64 = base64.b64encode (u16).decode ('utf-8') ## b64 (which returns bytes, so we need an additional decode(utf8))
return ['powershell', '-WindowStyle', 'Hidden', '-EncodedCommand', b64]