Compare commits
1 Commits
main
...
rconnect_o
| Author | SHA1 | Date |
|---|---|---|
|
|
70a9d4ad51 |
42
CHANGELOG.md
42
CHANGELOG.md
|
|
@ -5,48 +5,11 @@ 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).
|
||||
|
||||
## [8.5.10] - 2026-09-30
|
||||
|
||||
### Fixed
|
||||
- **ogLive**: Resuelta una condición de carrera que provocaba fallos de inicialización del servidor gráfico X al recibir peticiones concurrentes o ultra-rápidas de carga de navegador desde ogCore. Se ha implementado un semáforo/control de estado del proceso del navegador para ignorar peticiones duplicadas redundantes.
|
||||
|
||||
## [8.5.9] - 2026-07-23
|
||||
|
||||
### Fixed
|
||||
- Preserve original exit code in Windows PowerShell script wrapper when redirecting streams, ensuring ogcore receives failure status (e.g., CommandNotFoundException).
|
||||
- Prevent raw PowerShell CLIXML output from leaking into stderr on Windows when a command fails non-interactively.
|
||||
- Removed the `START_SCRIPT` webhook entirely from the script launching process for both admin (`client=false`) and user-session (`client=true`) paths. This eliminates the race condition that caused duplicate traces in `ogcore`, as `ogcore` now marks traces as `IN_PROGRESS` synchronously.
|
||||
|
||||
## [8.5.6] - 2026-07-21
|
||||
### Added
|
||||
- Notify `ogcore` via webhook when natively executed scripts finish, bringing `ogagent` in line with `ogLive` functionality (resolves stuck traces in "Enviada" state).
|
||||
|
||||
## [8.5.5] - 2026-05-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Promote all ogLive action handlers (`process_CrearImagen`, `process_RestaurarImagen`, `process_EjecutarScript`, etc.) from `DEBUG` to `INFO` so that the action trace is visible without enabling verbose logging. Polling operations (`process_status`, `process_Sondeo`) remain at `DEBUG`.
|
||||
- Promote job completion log in `oglive_worker.mon()` from `DEBUG` to `INFO`, now including the job result.
|
||||
- When `interfaceAdmin()` fails, log the script stdout at `WARNING` in addition to the existing `ERROR` for stderr, providing full diagnostic context without requiring a debug-level rerun.
|
||||
|
||||
## [8.5.5] - 2026-05-20
|
||||
|
||||
### Changed
|
||||
|
||||
- On machines with multiple network interfaces, the agent now selects the interface whose IP the OS routing table would use to reach ogcore, instead of always picking the first interface returned by the OS. This ensures the agent registers with the correct IP on multi-homed machines. If the routing query fails (network not ready yet) the detection loop keeps retrying rather than accepting a wrong interface. Falls back to the first available interface only when the routing query succeeds but no interface matches (e.g. NAT or VPN).
|
||||
|
||||
## [8.5.4] - 2026-05-11
|
||||
## [8.5.3] - 2026-04-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Network interface detection no longer blocks activation with a hard 5-minute timeout. `onActivation()` now returns immediately and launches a background thread (`_setup_network`) that retries indefinitely until a network interface is available, then registers with ogcore. This fixes a race condition on Windows machines where the network stack was not ready when the service started; the workaround of configuring a delayed service start is no longer needed. The same improvement applies to Linux and macOS where the service could also start before DHCP had assigned an address.
|
||||
- Guard against `AttributeError` in `onDeactivation()`, `onLogin()` and `onLogout()` if the service is stopped or a session event fires before the network interface has been detected.
|
||||
|
||||
## [8.5.3] - 2026-05-07
|
||||
|
||||
### Added
|
||||
|
||||
- Report `busy` and `st` fields in the periodic heartbeat ping to ogCore, so ogCore can update the client status without waiting for a command response. `busy` is `true` while a job is running; `st` indicates the base OS type (`OGL`, `LNX`, `WIN`, `OSX`)
|
||||
- Reconnect to ogcore after a connection loss in oglive: the monitoring thread now detects when contact is restored and re-sends `InclusionCliente` so ogcore marks the client as OG_LIVE and refreshes its token
|
||||
|
||||
## [8.5.2] - 2026-04-28
|
||||
|
||||
|
|
@ -590,4 +553,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
|
||||
- Initial release for OpenGnsys Agent
|
||||
|
||||
|
|
|
|||
|
|
@ -67,20 +67,6 @@ pipeline {
|
|||
}
|
||||
}
|
||||
}
|
||||
stage('Generate Changelog (Devel)') {
|
||||
when {
|
||||
branch 'devel'
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def devName = params.DEV_NAME ? params.DEV_NAME : env.DEFAULT_DEV_NAME
|
||||
def devEmail = params.DEV_EMAIL ? params.DEV_EMAIL : env.DEFAULT_DEV_EMAIL
|
||||
generateDebianChangelog(env.BUILD_DIR, devName, devEmail, "devel")
|
||||
sh "mkdir -p ${env.BUILD_DIR}/debian/"
|
||||
sh "cp ${env.BUILD_DIR}/debian/changelog ${env.BUILD_DIR}/linux/debian/"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
dir ("${env.BUILD_DIR}") {
|
||||
|
|
@ -126,32 +112,8 @@ pipeline {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Publish to Debian Repository (Devel)') {
|
||||
when {
|
||||
branch 'devel'
|
||||
}
|
||||
agent { label 'debian-repo' }
|
||||
steps {
|
||||
script {
|
||||
def versionPattern = "-${env.BUILD_NUMBER}~devel"
|
||||
publicarEnAptly('/var/tmp/opengnsys/debian-repo/ogagent', 'devel', versionPattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
success {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'devel') {
|
||||
build job: 'Aptly publish develop repository',
|
||||
wait: false,
|
||||
parameters: [
|
||||
string(name: 'TRIGGERED_BY', value: "${env.JOB_NAME}-${env.BUILD_NUMBER}")
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
always {
|
||||
notifyBuildStatus('narenas@qindel.com')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ install-ogagent:
|
|||
|
||||
cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR)
|
||||
# QT Dialogs & resources
|
||||
cd $(SOURCEDIR) && python3 update.py
|
||||
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
|
||||
# Version file
|
||||
cp $(SOURCEDIR)/VERSION $(LIBDIR)
|
||||
|
|
|
|||
|
|
@ -1,111 +1,3 @@
|
|||
ogagent (8.5.9-1) stable; urgency=medium
|
||||
|
||||
* Removed START_SCRIPT webhook to fix race conditions causing duplicate traces
|
||||
* Added script completion webhook to resolve stuck traces in Enviada state
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 29 Jul 2026 13:58:00 +0200
|
||||
|
||||
ogagent (8.5.5-1) stable; urgency=medium
|
||||
|
||||
* Remove obsolete dependencies (six, prctl, libxss1) to support modern Ubuntu releases (like 26.04)
|
||||
* Update policykit-1 to polkitd
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 09 Jul 2026 12:00:00 +0200
|
||||
|
||||
ogagent (8.5.4-1) stable; urgency=medium
|
||||
|
||||
* Fix network detection race condition on slow-booting machines: activation
|
||||
now launches a background thread that retries indefinitely until a network
|
||||
interface is available, removing the hard 5-minute timeout that caused the
|
||||
agent to fail permanently on Windows (and potentially Linux/macOS) when the
|
||||
network stack was not ready at service start time
|
||||
* Guard onDeactivation(), onLogin() and onLogout() against AttributeError
|
||||
when the service is stopped before the network interface has been detected
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 11 May 2026 00:00:00 +0200
|
||||
|
||||
ogagent (8.5.3-1) stable; urgency=medium
|
||||
|
||||
* Report busy state and OS type in the periodic heartbeat ping to ogcore
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 07 May 2026 00:00:00 +0200
|
||||
|
||||
ogagent (8.5.2-1) stable; urgency=medium
|
||||
|
||||
* Reconnect to ogcore after a connection loss and re-send ogagent/started
|
||||
* Fix crash in REST client when a network-level error occurs and e.response is None
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 28 Apr 2026 00:00:00 +0200
|
||||
|
||||
ogagent (8.5.1-1) stable; urgency=medium
|
||||
|
||||
* Start the HTTP server only after all modules complete activation
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 28 Apr 2026 00:00:00 +0200
|
||||
|
||||
ogagent (8.5.0-1) stable; urgency=medium
|
||||
|
||||
* Write the authentication token to /tmp/ogagent.token on oglive startup only
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 23 Mar 2026 00:00:00 +0100
|
||||
|
||||
ogagent (8.4.3-1) stable; urgency=medium
|
||||
|
||||
* Fix script execution on Windows when C:\tmp does not exist: use
|
||||
tempfile.gettempdir() instead of hardcoded /tmp for the EjecutarScript
|
||||
job_id handoff file
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 23 Mar 2026 00:00:00 +0100
|
||||
|
||||
ogagent (8.4.2-1) stable; urgency=medium
|
||||
|
||||
* Log the fail condition for network detection
|
||||
* Refresh the menu after calling getConfiguration
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 16 Oct 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.4.1-1) stable; urgency=medium
|
||||
|
||||
* Don't return HTTP 500 on POST requests with an empty body
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 15 Oct 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.4.0-1) stable; urgency=medium
|
||||
|
||||
* Show a user-friendly error if the agent fails to start
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 29 Sep 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.3.3-1) stable; urgency=medium
|
||||
|
||||
* Exit from the infinite loop
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 23 Sep 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.3.2-1) stable; urgency=medium
|
||||
|
||||
* Wait for children within a loop, not just once
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 18 Sep 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.3.1-1) stable; urgency=medium
|
||||
|
||||
* Fix EOF bug in interfaceAdm()
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 11 Sep 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.3.0-1) stable; urgency=medium
|
||||
|
||||
* Put InventarioHardware to work
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 29 Aug 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.2.0-1) stable; urgency=medium
|
||||
|
||||
* Git operations support
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 29 Aug 2025 00:00:00 +0200
|
||||
|
||||
ogagent (8.1.2-1) stable; urgency=medium
|
||||
|
||||
* Parameter fixes
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Source: ogagent
|
|||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: OpenGnsys developers <info@opengnsys.es>
|
||||
Build-Depends: debhelper (>= 7), po-debconf, pyqt6-dev-tools
|
||||
Build-Depends: debhelper (>= 7), po-debconf
|
||||
Standards-Version: 3.9.2
|
||||
Homepage: https://opengnsys.es/
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ Section: admin
|
|||
Priority: optional
|
||||
Architecture: all
|
||||
Depends:
|
||||
polkitd | policykit-1 (>= 0.100), python3 (>=3.4), python3-pyqt6, python3-requests,
|
||||
python3-distro, zenity, ${misc:Depends}
|
||||
policykit-1 (>= 0.100), python3 (>=3.4) | python (>= 3.4), python3-pyqt6, python3-requests,
|
||||
python3-six, python3-prctl, python3-distro, libxss1, zenity, ${misc:Depends}
|
||||
Description: OpenGnsys Agent for Operating Systems
|
||||
This package provides the required components to allow this machine to work on an environment managed by OpenGnsys.
|
||||
|
|
|
|||
238
ogagent.nsi
238
ogagent.nsi
|
|
@ -1,238 +0,0 @@
|
|||
# We need http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin
|
||||
# Copy inside the two x86_xxxxx folders inside nsis plugins folder
|
||||
Name "OpenGnsys Agent"
|
||||
|
||||
# OpenGnsys Agent version
|
||||
!define /file OGA_VERSION "src\VERSION"
|
||||
!define /file OGA_WINVERSION "windows\VERSION"
|
||||
|
||||
# General Symbol Definitions
|
||||
!define REGKEY "SOFTWARE\OGAgent"
|
||||
!if ${OGA_VERSION} == ${OGA_WINVERSION}
|
||||
!define VERSION ${OGA_WINVERSION}.0
|
||||
!else
|
||||
!define VERSION ${OGA_WINVERSION}.1
|
||||
!endif
|
||||
!define COMPANY "OpenGnsys Project"
|
||||
!define URL https://opengnsys.es
|
||||
!define CONFIGFILE "cfg\ogagent.cfg"
|
||||
|
||||
# MultiUser Symbol Definitions
|
||||
!define MULTIUSER_EXECUTIONLEVEL Admin
|
||||
!define MULTIUSER_INSTALLMODE_COMMANDLINE
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR OGAgent
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "${REGKEY}"
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUE "Path"
|
||||
|
||||
# MUI Symbol Definitions
|
||||
!define MUI_ICON "src\img\oga.ico"
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||
!define MUI_UNICON "src\img\oga.ico"
|
||||
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
|
||||
!define MUI_LANGDLL_REGISTRY_ROOT HKLM
|
||||
!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY}
|
||||
!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage
|
||||
|
||||
# Included files
|
||||
!include MultiUser.nsh
|
||||
!include Sections.nsh
|
||||
!include MUI2.nsh
|
||||
!include nsDialogs.nsh
|
||||
!include LogicLib.nsh
|
||||
!include Filefunc.nsh
|
||||
|
||||
# Reserved Files
|
||||
!insertmacro MUI_RESERVEFILE_LANGDLL
|
||||
|
||||
# Variables
|
||||
Var StartMenuGroup
|
||||
Var SERVERIP
|
||||
Var SERVERIP_VALUE
|
||||
|
||||
# Installer pages
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE src\license.txt
|
||||
Page custom "ParamsPage" "ParamsPageLeave"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
# Installer languages
|
||||
!insertmacro MUI_LANGUAGE English
|
||||
!insertmacro MUI_LANGUAGE Spanish
|
||||
!insertmacro MUI_LANGUAGE French
|
||||
!insertmacro MUI_LANGUAGE German
|
||||
|
||||
# Installer attributes
|
||||
BrandingText "OpenGnsys"
|
||||
OutFile OGAgentSetup-${OGA_VERSION}.exe
|
||||
InstallDir OGAgent
|
||||
CRCCheck on
|
||||
XPStyle on
|
||||
ShowInstDetails hide
|
||||
VIProductVersion "${VERSION}.0.0"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} ProductName "OGAgent"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} ProductVersion "${VERSION}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} CompanyName "${COMPANY}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} CompanyWebsite "${URL}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} FileVersion "${VERSION}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} FileDescription "OpenGnsys Agent installer"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} LegalCopyright "(c) 2015 Virtual Cable S.L.U."
|
||||
InstallDirRegKey HKLM "${REGKEY}" Path
|
||||
ShowUninstDetails show
|
||||
|
||||
# Installer sections
|
||||
Section -Main SEC0000
|
||||
SetShellVarContext all
|
||||
SetOutPath $INSTDIR
|
||||
SetOverwrite on
|
||||
File /r src\dist\OGAgent\*.*
|
||||
File windows\VC_redist.x64.exe
|
||||
File src\VERSION
|
||||
WriteRegStr HKLM "${REGKEY}\Components" Main 1
|
||||
SectionEnd
|
||||
|
||||
Section -post SEC0001
|
||||
WriteIniStr $INSTDIR\${CONFIGFILE} "opengnsys" "remote" "https://$SERVERIP_VALUE/opengnsys/rest"
|
||||
SetShellVarContext current
|
||||
WriteRegStr HKLM "${REGKEY}" Path $INSTDIR
|
||||
SetOutPath $INSTDIR
|
||||
WriteUninstaller $INSTDIR\OGAgentUninstaller.exe
|
||||
SetOutPath $SMPROGRAMS\$StartMenuGroup
|
||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^UninstallLink).lnk" $INSTDIR\OGAgentUninstaller.exe
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\OGAgentUninstaller.exe
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\OGAgentUninstaller.exe
|
||||
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" OGAgentTool $INSTDIR\OGAgentUser.exe
|
||||
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
|
||||
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
|
||||
ExecWait '"$INSTDIR\VC_redist.x64.exe" /passive /norestart'
|
||||
# Add the application to the firewall exception list - All Networks - All IP Version - Enabled
|
||||
# SimpleFC::AddApplication "OpenGnsys Agent Service" "$INSTDIR\OGAgentService.exe" 0 2 "" 1
|
||||
# SimpleFC::AdvAddRule [name] [description] [protocol] [direction]
|
||||
# [status] [profile] [action] [application] [service_name] [icmp_types_and_codes]
|
||||
# [group] [local_ports] [remote_ports] [local_address] [remote_address]
|
||||
#
|
||||
SimpleFC::AdvAddRule "OpenGnsys Agent Firewall rules" "Firewall rules for OpenGnsys Agent interaction with broker." "6" "1" \
|
||||
"1" "7" "1" "$INSTDIR\OGAgentService.exe" "" "" \
|
||||
"" "" "" "" ""
|
||||
Pop $0 ; return error(1)/success(0)
|
||||
# Disable fast boot on Windows 10, if registry key exists.
|
||||
ReadRegDWORD $0 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Power" HiberbootEnabled
|
||||
IfErrors 0 +2
|
||||
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Power" HiberbootEnabled 0
|
||||
# Install service
|
||||
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe --startup auto install" # Add service after installation
|
||||
Exec "net start ogagent"
|
||||
Exec "$INSTDIR\OGAgentUser.exe"
|
||||
SectionEnd
|
||||
|
||||
# Macro for selecting uninstaller sections
|
||||
!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
|
||||
Push $R0
|
||||
ReadRegStr $R0 HKLM "${REGKEY}\Components" "${SECTION_NAME}"
|
||||
StrCmp $R0 1 0 next${UNSECTION_ID}
|
||||
!insertmacro SelectSection "${UNSECTION_ID}"
|
||||
GoTo done${UNSECTION_ID}
|
||||
next${UNSECTION_ID}:
|
||||
!insertmacro UnselectSection "${UNSECTION_ID}"
|
||||
done${UNSECTION_ID}:
|
||||
Pop $R0
|
||||
!macroend
|
||||
|
||||
# Uninstaller sections
|
||||
Section /o -un.Main UNSEC0000
|
||||
nsExec::Exec "taskkill /F /IM OGAgentUser.exe /T"
|
||||
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe stop" # Stops the service prior uninstall
|
||||
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe remove" # Removes the service prior uninstall
|
||||
nsExec::Exec "taskkill /F /IM OGAgentService.exe /T"
|
||||
Delete /REBOOTOK "$INSTDIR\*.*"
|
||||
DeleteRegValue HKLM "${REGKEY}\Components" Main
|
||||
DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" OGAgentTool
|
||||
SectionEnd
|
||||
|
||||
Section -un.post UNSEC0001
|
||||
# Remove application from the firewall exception list
|
||||
# SimpleFC::RemoveApplication "$INSTDIR\OGAgentService.exe"
|
||||
SimpleFC::AdvRemoveRule "OpenGnsys Agent Firewall rules"
|
||||
Pop $0 ; return error(1)/success(0)
|
||||
|
||||
SetShellVarContext current
|
||||
StrCpy $StartMenuGroup "OpenGnsys Agent"
|
||||
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^UninstallLink).lnk"
|
||||
Delete /REBOOTOK $INSTDIR\OGAgentUninstaller.exe
|
||||
DeleteRegValue HKLM "${REGKEY}" Path
|
||||
DeleteRegKey /IfEmpty HKLM "${REGKEY}\Components"
|
||||
DeleteRegKey /IfEmpty HKLM "${REGKEY}"
|
||||
RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup
|
||||
SetShellVarContext all
|
||||
RmDir /r /REBOOTOK $INSTDIR
|
||||
SectionEnd
|
||||
|
||||
# Installer functions
|
||||
Function .onInit
|
||||
InitPluginsDir
|
||||
Call GetParameters
|
||||
StrCpy $StartMenuGroup "OpenGnsys Agent"
|
||||
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
!insertmacro MULTIUSER_INIT
|
||||
FunctionEnd
|
||||
|
||||
# Uninstaller functions
|
||||
Function un.onInit
|
||||
StrCpy $StartMenuGroup "OpenGnsys Agent"
|
||||
!insertmacro MUI_UNGETLANGUAGE
|
||||
!insertmacro MULTIUSER_UNINIT
|
||||
!insertmacro SELECT_UNSECTION Main ${UNSEC0000}
|
||||
FunctionEnd
|
||||
|
||||
# Parameters dialog box
|
||||
Function ParamsPage
|
||||
!insertmacro MUI_HEADER_TEXT "$(PARAMS_TITLE)" ""
|
||||
nsDialogs::Create /NOUNLOAD 1018
|
||||
Pop $0
|
||||
${If} $0 == error
|
||||
Abort
|
||||
${EndIf}
|
||||
${NSD_CreateLabel} 0 0 100% 12u "$(SERVER_LABEL):"
|
||||
Pop $0
|
||||
${NSD_CreateText} 10% 20u 80% 12u "$SERVERIP_VALUE"
|
||||
Pop $SERVERIP
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
# Get parameters from the dialog
|
||||
Function ParamsPageLeave
|
||||
${NSD_GetText} $SERVERIP $SERVERIP_VALUE
|
||||
FunctionEnd
|
||||
|
||||
# Assign input parameters or default values to variables
|
||||
Function GetParameters
|
||||
${GetOptions} $CMDLINE "/server" $SERVERIP_VALUE
|
||||
${If} $SERVERIP_VALUE == ""
|
||||
StrCpy $SERVERIP_VALUE "192.168.2.1:8443"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
# Installer Language Strings
|
||||
LangString PARAMS_TITLE ${LANG_ENGLISH} "Setup parameters"
|
||||
LangString PARAMS_TITLE ${LANG_SPANISH} "Parametros de configuracion"
|
||||
LangString PARAMS_TITLE ${LANG_FRENCH} "Parametres de configuration"
|
||||
LangString PARAMS_TITLE ${LANG_GERMAN} "Setup-Parameter"
|
||||
|
||||
LangString SERVER_LABEL ${LANG_ENGLISH} "OpenGnsys Server IP Address and port (eg. 192.168.98.99:8443)"
|
||||
LangString SERVER_LABEL ${LANG_SPANISH} "Direccion IP y puerto del Servidor OpenGnsys (p. ej. 192.168.98.99:8443)"
|
||||
LangString SERVER_LABEL ${LANG_FRENCH} "Adresse IP et port du Serveur OpenGnsys (ex. 192.168.98.99:8443)"
|
||||
LangString SERVER_LABEL ${LANG_GERMAN} "OpenGnsys Server IP-Adresse und Port (z. B. 192.168.98.99:8443)"
|
||||
|
||||
LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall $(^Name)"
|
||||
LangString ^UninstallLink ${LANG_SPANISH} "Desinstalar $(^Name)"
|
||||
LangString ^UninstallLink ${LANG_FRENCH} "D<>sinstaller $(^Name)"
|
||||
LangString ^UninstallLink ${LANG_GERMAN} "deinstallieren $(^Name)"
|
||||
|
|
@ -1 +1 @@
|
|||
8.5.9
|
||||
8.3.1
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
<item>
|
||||
<widget class="QLabel" name="VersionLabel">
|
||||
<property name="text">
|
||||
<string>Version 8.5.6</string>
|
||||
<string>Version 0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -31,10 +31,13 @@
|
|||
"""
|
||||
|
||||
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
from . import modules
|
||||
from .RESTApi import REST, RESTError
|
||||
|
||||
VERSION='8.5.6'
|
||||
VERSION='0'
|
||||
|
||||
__title__ = 'OpenGnsys Agent'
|
||||
__version__ = VERSION
|
||||
|
|
@ -43,4 +46,11 @@ __author__ = 'Adolfo Gómez'
|
|||
__license__ = "BSD 3-clause"
|
||||
__copyright__ = "Copyright VirtualCable S.L.U."
|
||||
|
||||
|
||||
if not hasattr(six, 'byte2int'):
|
||||
if six.PY3:
|
||||
import operator
|
||||
six.byte2int = operator.itemgetter(0)
|
||||
else:
|
||||
def _byte2int(bs):
|
||||
return ord(bs[0])
|
||||
six.byte2int = _byte2int
|
||||
|
|
|
|||
|
|
@ -34,9 +34,10 @@ import os
|
|||
import json
|
||||
import ssl
|
||||
import threading
|
||||
from socketserver import ThreadingMixIn
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from urllib.parse import unquote
|
||||
from six.moves.socketserver import ThreadingMixIn # @UnresolvedImport
|
||||
from six.moves.BaseHTTPServer import BaseHTTPRequestHandler # @UnresolvedImport
|
||||
from six.moves.BaseHTTPServer import HTTPServer # @UnresolvedImport
|
||||
from six.moves.urllib.parse import unquote # @UnresolvedImport
|
||||
|
||||
from .utils import exceptionToMessage
|
||||
from .certs import createSelfSignedCert
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class JobMgr():
|
|||
ts = now.strftime ('%Y-%m-%d %H:%M:%S.%f%z') ## '%s' doesn't work on windows
|
||||
job_id = hashlib.sha256 (now.isoformat().encode('UTF-8') + script.encode ('UTF-8')).hexdigest()[0:12]
|
||||
p = subprocess.Popen (args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
self.jobs[job_id] = { 'p': p, 'pid': p.pid, 'starttime': ts, 'script': script, 'client': is_client, 'status': 'running', 'stdout': '', 'stderr': '', 'notified': False }
|
||||
self.jobs[job_id] = { 'p': p, 'pid': p.pid, 'starttime': ts, 'script': script, 'client': is_client, 'status': 'running', 'stdout': '', 'stderr': '' }
|
||||
self.jobs[job_id]['t1'] = threading.Thread (target=job_readstdout, args=(self.jobs[job_id],))
|
||||
self.jobs[job_id]['t2'] = threading.Thread (target=job_readstderr, args=(self.jobs[job_id],))
|
||||
self.jobs[job_id]['t1'].start()
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import ctypes.util
|
|||
import subprocess
|
||||
import struct
|
||||
import array
|
||||
import six
|
||||
import distro
|
||||
from opengnsys import utils
|
||||
|
||||
|
|
@ -52,12 +53,12 @@ def _getMacAddr(ifname):
|
|||
'''
|
||||
if isinstance(ifname, list):
|
||||
return dict([(name, _getMacAddr(name)) for name in ifname])
|
||||
if isinstance(ifname, str):
|
||||
if isinstance(ifname, six.text_type):
|
||||
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15])))
|
||||
return str(''.join(['%02x:' % char for char in info[18:24]])[:-1])
|
||||
return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
|
@ -69,11 +70,11 @@ def _getIpAddr(ifname):
|
|||
'''
|
||||
if isinstance(ifname, list):
|
||||
return dict([(name, _getIpAddr(name)) for name in ifname])
|
||||
if isinstance(ifname, str):
|
||||
if isinstance(ifname, six.text_type):
|
||||
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
return str(socket.inet_ntoa(fcntl.ioctl(
|
||||
return six.text_type(socket.inet_ntoa(fcntl.ioctl(
|
||||
s.fileno(),
|
||||
0x8915, # SIOCGIFADDR
|
||||
struct.pack(str('256s'), ifname[:15])
|
||||
|
|
@ -137,6 +138,11 @@ def reboot(flags=0):
|
|||
'''
|
||||
Simple reboot using os command
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Check for OpenGnsys Client or GNU/Linux distribution.
|
||||
if os.path.exists('/scripts/oginit'):
|
||||
subprocess.call('source /opt/opengnsys/etc/preinit/loadenviron.sh; /opt/opengnsys/scripts/reboot', shell=True)
|
||||
|
|
@ -148,6 +154,11 @@ def poweroff(flags=0):
|
|||
'''
|
||||
Simple poweroff using os command
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Check for OpenGnsys Client or GNU/Linux distribution.
|
||||
if os.path.exists('/scripts/oginit'):
|
||||
subprocess.call('source /opt/opengnsys/etc/preinit/loadenviron.sh; /opt/opengnsys/scripts/poweroff', shell=True)
|
||||
|
|
@ -160,6 +171,11 @@ def logoff():
|
|||
Kills all curent user processes, which must send a logogof
|
||||
caveat: If the user has other sessions, will also disconnect from them
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import ctypes.util
|
|||
import subprocess
|
||||
import struct
|
||||
import array
|
||||
import six
|
||||
import json
|
||||
from opengnsys import utils
|
||||
|
||||
|
|
@ -133,6 +134,11 @@ def reboot(flags=0):
|
|||
'''
|
||||
Simple reboot command
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec reboot command
|
||||
subprocess.call('/sbin/shutdown -r now', shell=True)
|
||||
|
||||
|
|
@ -141,6 +147,11 @@ def poweroff(flags=0):
|
|||
'''
|
||||
Simple poweroff command
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec shutdown command
|
||||
subprocess.call('/sbin/shutdown -h now', shell=True)
|
||||
|
||||
|
|
@ -149,6 +160,11 @@ def logoff():
|
|||
'''
|
||||
Simple logout using AppleScript
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec logout using AppleScript
|
||||
subprocess.call('/usr/bin/osascript -e \'tell app "System Events" to «event aevtrlgo»\'', shell=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -53,35 +53,6 @@ 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))
|
||||
|
|
|
|||
|
|
@ -34,9 +34,7 @@ import base64
|
|||
import os
|
||||
import random
|
||||
import shutil
|
||||
import socket
|
||||
import string
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib.error
|
||||
|
|
@ -103,7 +101,6 @@ 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."""
|
||||
|
|
@ -127,40 +124,10 @@ class OpenGnSysWorker(ServerWorker):
|
|||
while True:
|
||||
time.sleep(1)
|
||||
n += 1
|
||||
|
||||
# Check for finished jobs to send webhook
|
||||
for k, elem in list(self.jobmgr.jobs.items()):
|
||||
if elem.get('p') and elem['p'].poll() is not None:
|
||||
if not elem.get('notified', False):
|
||||
if not elem.get('t1').is_alive() and not elem.get('t2').is_alive():
|
||||
elem['notified'] = True
|
||||
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": "RESPUESTA_EjecutarScript",
|
||||
"job_id": k,
|
||||
"res": 1 if elem['rc'] == 0 else 0,
|
||||
"der": der
|
||||
}
|
||||
logger.info(f"Job {k} finished with return code {elem['rc']}. Sending webhook to ogcore.")
|
||||
try:
|
||||
self.REST.sendMessage('clients/status/webhook', webhook_body)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send webhook for job {k}: {e}")
|
||||
|
||||
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": 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:
|
||||
|
|
@ -171,63 +138,57 @@ class OpenGnSysWorker(ServerWorker):
|
|||
logger.info('Connection to ogcore restored, re-registering')
|
||||
self._send_started()
|
||||
|
||||
def _best_interface(self, nets):
|
||||
def onActivation(self):
|
||||
"""
|
||||
Return the interface whose IP the OS routing table would use to reach
|
||||
ogcore, by opening a no-op UDP socket (no packets sent) and reading
|
||||
the local address assigned by the kernel.
|
||||
Sends OGAgent activation notification to OpenGnsys server
|
||||
"""
|
||||
if os.path.exists ('/scripts/oginit'):
|
||||
## estamos en oglive, este modulo no debe cargarse
|
||||
## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar
|
||||
raise Exception ('Refusing to load within an ogLive image')
|
||||
|
||||
Returns None if the routing query itself fails (network not ready yet),
|
||||
so the caller keeps retrying. Falls back to nets[0] only when the query
|
||||
succeeds but no interface matches the routing IP (e.g. NAT or VPN).
|
||||
"""
|
||||
e = None # Error info
|
||||
t = 0 # Count of time
|
||||
# Generate random secret to send on activation
|
||||
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
||||
# Ensure cfg has required configuration variables or an exception will be thrown
|
||||
try:
|
||||
host = urllib.parse.urlparse(self.REST.endpoint).hostname
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
s.connect((host, 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
except Exception as e:
|
||||
logger.debug('Routing query to ogcore failed (network not ready?): {}'.format(e))
|
||||
return None
|
||||
|
||||
for iface in nets:
|
||||
if iface.ip == local_ip:
|
||||
logger.debug('Selected interface {} ({}) for ogcore at {}'.format(
|
||||
iface.name, local_ip, host))
|
||||
return iface
|
||||
|
||||
# Routing succeeded but no interface matched (NAT/VPN): best effort
|
||||
logger.debug('No interface matched routing IP {}, falling back to {}'.format(
|
||||
local_ip, nets[0].ip))
|
||||
return nets[0]
|
||||
|
||||
def _setup_network(self, ca_file, crt_file, key_file):
|
||||
"""
|
||||
Background thread: waits indefinitely for a network interface, then
|
||||
registers with ogcore. This avoids blocking onActivation() and allows
|
||||
the agent to recover automatically when the network comes up late
|
||||
(e.g. on slow Windows boots without a delayed-start service setting).
|
||||
"""
|
||||
# COM must be initialised per-thread on Windows; WMI (used by
|
||||
# getNetworkInfo) raises CO_E_NOTINITIALIZED otherwise.
|
||||
if sys.platform == 'win32':
|
||||
import pythoncom
|
||||
pythoncom.CoInitialize()
|
||||
t = 0
|
||||
while not self.interface:
|
||||
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:
|
||||
logger.error("Configuration error: {}".format(e))
|
||||
raise e
|
||||
self.REST = REST (url, ca_file=ca_file, crt_file=crt_file, key_file=key_file)
|
||||
# Execution level ('full' by default)
|
||||
try:
|
||||
self.exec_level = self.service.config.get(self.name, 'level')
|
||||
except NoOptionError:
|
||||
self.exec_level = 'full'
|
||||
# Get network interfaces until they are active or timeout (5 minutes)
|
||||
for t in range(0, 300):
|
||||
try:
|
||||
nets = list(operations.getNetworkInfo())
|
||||
if nets:
|
||||
self.interface = self._best_interface(nets)
|
||||
if self.interface and t > 0:
|
||||
logger.debug("Network interface found after {} seconds".format(t))
|
||||
else:
|
||||
logger.error('No network interfaces found')
|
||||
# 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]
|
||||
except Exception as e:
|
||||
logger.warn(e)
|
||||
if not self.interface:
|
||||
# Wait 1 sec. and retry
|
||||
logger.warn (e)
|
||||
time.sleep(1)
|
||||
t += 1
|
||||
finally:
|
||||
# Exit loop if interface is active
|
||||
if self.interface:
|
||||
if t > 0:
|
||||
logger.debug("Fetch connection data after {} tries".format(t))
|
||||
break
|
||||
# Raise error after timeout
|
||||
if not self.interface:
|
||||
## UnboundLocalError: cannot access local variable 'e' where it is not associated with a value
|
||||
raise e
|
||||
|
||||
# Loop to send initialization message
|
||||
init_retries = 100
|
||||
|
|
@ -240,7 +201,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
'agent_version': VERSION})
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warn(str(e))
|
||||
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)
|
||||
|
|
@ -250,14 +211,13 @@ class OpenGnSysWorker(ServerWorker):
|
|||
'agent_version': VERSION})
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warn(str(e))
|
||||
logger.warn (str (e))
|
||||
time.sleep(3)
|
||||
|
||||
if t < init_retries - 1:
|
||||
# Raise error after timeout
|
||||
if t < init_retries-1:
|
||||
logger.debug('Successful connection after {} tries'.format(t))
|
||||
else:
|
||||
# ogcore unreachable after all retries; mon() will re-register when it comes back
|
||||
logger.error('Cannot connect to remote server after {} retries, will retry via monitoring'.format(init_retries))
|
||||
elif t == init_retries-1:
|
||||
raise Exception('Initialization error: Cannot connect to remote server')
|
||||
|
||||
# Delete marking files
|
||||
for f in ['ogboot.me', 'ogboot.firstboot', 'ogboot.secondboot']:
|
||||
|
|
@ -272,46 +232,10 @@ class OpenGnSysWorker(ServerWorker):
|
|||
if os.path.isfile(new_hosts_file):
|
||||
shutil.copyfile(new_hosts_file, hosts_file)
|
||||
|
||||
self._registered = t < init_retries - 1
|
||||
self._registered = True
|
||||
threading.Thread(name='monitoring_thread', target=self.mon, daemon=True).start()
|
||||
logger.debug('_setup_network complete')
|
||||
|
||||
def onActivation(self):
|
||||
"""
|
||||
Sends OGAgent activation notification to OpenGnsys server
|
||||
"""
|
||||
if os.path.exists('/scripts/oginit'):
|
||||
## estamos en oglive, este modulo no debe cargarse
|
||||
## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar
|
||||
raise Exception('Refusing to load within an ogLive image')
|
||||
|
||||
# Generate random secret to send on activation
|
||||
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
||||
# Ensure cfg has required configuration variables or an exception will be thrown
|
||||
try:
|
||||
url = self.service.config.get(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:
|
||||
logger.error("Configuration error: {}".format(e))
|
||||
raise e
|
||||
self.REST = REST(url, ca_file=ca_file, crt_file=crt_file, key_file=key_file)
|
||||
# Execution level ('full' by default)
|
||||
try:
|
||||
self.exec_level = self.service.config.get(self.name, 'level')
|
||||
except NoOptionError:
|
||||
self.exec_level = 'full'
|
||||
|
||||
# Network detection and ogcore registration run in a background thread so
|
||||
# that a slow network (boot race condition) does not prevent activation.
|
||||
threading.Thread(
|
||||
name='setup_thread',
|
||||
target=self._setup_network,
|
||||
args=(ca_file, crt_file, key_file),
|
||||
daemon=True,
|
||||
).start()
|
||||
logger.debug('onActivation launched setup thread')
|
||||
logger.debug('onActivation ok')
|
||||
|
||||
def onDeactivation(self):
|
||||
"""
|
||||
|
|
@ -322,9 +246,8 @@ class OpenGnSysWorker(ServerWorker):
|
|||
sess_len = now - elem['login_ts']
|
||||
logger.debug ('Session of logged in user {} took {} seconds'.format (elem['username'], int (sess_len)))
|
||||
logger.debug('onDeactivation')
|
||||
if self.interface:
|
||||
self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'ostype': operations.os_type, 'osversion': operations.os_version})
|
||||
self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'ostype': operations.os_type, 'osversion': operations.os_version})
|
||||
def onLogin(self, data):
|
||||
"""
|
||||
Sends session login notification to OpenGnsys server
|
||||
|
|
@ -332,10 +255,9 @@ 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() })
|
||||
if self.interface:
|
||||
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})
|
||||
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})
|
||||
|
||||
def onLogout(self, user):
|
||||
"""
|
||||
|
|
@ -350,8 +272,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
self.user.pop()
|
||||
except IndexError:
|
||||
pass
|
||||
if self.interface:
|
||||
self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, 'user': user})
|
||||
self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, 'user': user})
|
||||
|
||||
def process_ogclient(self, path, get_params, post_params, server):
|
||||
"""
|
||||
|
|
@ -500,38 +421,11 @@ class OpenGnSysWorker(ServerWorker):
|
|||
|
||||
def process_client_script_launched(self, data):
|
||||
import tempfile
|
||||
job_id = data['job_id']
|
||||
job_id_path = os.path.join(tempfile.gettempdir(), 'EjecutarScript-jobid')
|
||||
with open (job_id_path, 'w') as fd:
|
||||
fd.write (job_id)
|
||||
self.client_jobs.add(job_id)
|
||||
|
||||
fd.write (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):
|
||||
|
|
|
|||
|
|
@ -149,6 +149,13 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
break
|
||||
|
||||
def _on_reconnect(self):
|
||||
logger.info('Re-registering with ogcore after reconnection')
|
||||
if self.inclusionCliente():
|
||||
self._registered = True
|
||||
else:
|
||||
logger.warning('Re-registration failed, will retry on next ping')
|
||||
|
||||
def inclusionCliente (self):
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
|
|
@ -889,7 +896,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
|
||||
@check_secret
|
||||
def process_popup (self, path, get_params, post_params, server):
|
||||
logger.info ('in process_popup, path "{}" get_params "{}" post_params "{}" server "{}"'.format (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'>"
|
||||
|
|
@ -898,7 +905,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_Actualizar (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -907,7 +914,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_Purgar (self, path, get_params, post_params, server):
|
||||
logger.info ('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)
|
||||
return {}
|
||||
#exit (0) ## ogAdmClient.c:905
|
||||
|
|
@ -915,7 +922,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_Comando (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -931,7 +938,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_ConsolaRemota (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -940,7 +947,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_Arrancar (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -961,7 +968,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('halt')
|
||||
@check_secret
|
||||
def process_Apagar (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -970,7 +977,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('halt')
|
||||
@check_secret
|
||||
def process_Reiniciar (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -979,7 +986,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_IniciarSesion (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -988,7 +995,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_EjecutarScript (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -997,7 +1004,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_EjecutaComandosPendientes (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -1006,7 +1013,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_CrearImagen (self, path, get_params, post_params, server):
|
||||
logger.info ('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)))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
|
|
@ -1014,7 +1021,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
return self._long_running_job ('CrearImagen', self.do_CrearImagen, args=(post_params,))
|
||||
|
||||
def process_CrearImagenGit (self, path, get_params, post_params, server):
|
||||
logger.info ('in process_CrearImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (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)))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
|
|
@ -1022,7 +1029,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
return self._long_running_job ('CrearImagenGit', self.do_CrearImagenGit, args=(post_params,))
|
||||
|
||||
def process_ModificarImagenGit (self, path, get_params, post_params, server):
|
||||
logger.info ('in process_ModificarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.debug ('in process_ModificarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.debug ('type(post_params) "{}"'.format (type (post_params)))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
|
|
@ -1030,7 +1037,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
return self._long_running_job ('ModificarImagenGit', self.do_ModificarImagenGit, args=(post_params,))
|
||||
|
||||
def process_RestaurarImagenGit (self, path, get_params, post_params, server):
|
||||
logger.info ('in process_RestaurarImagenGit, path "{}" get_params "{}" post_params "{}" server "{}"'.format (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)))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
|
|
@ -1040,7 +1047,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_RestaurarImagen (self, path, get_params, post_params, server):
|
||||
logger.info ('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)))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
|
|
@ -1050,7 +1057,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_Configurar (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -1065,7 +1072,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_InventarioHardware (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -1074,7 +1081,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_InventarioSoftware (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -1083,7 +1090,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_KillJob (self, path, get_params, post_params, server):
|
||||
logger.info ('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))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
@ -1097,7 +1104,7 @@ class ogAdmClientWorker (ogLiveWorker):
|
|||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_GetGitData (self, path, get_params, post_params, server):
|
||||
logger.info ('in process_GetGitData, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
logger.debug ('in process_GetGitData, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
if post_params is None:
|
||||
logger.warning ('POST body is empty--setting it to an empty dictionary')
|
||||
post_params = {}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
|
||||
import sys
|
||||
import six
|
||||
|
||||
if sys.platform == 'win32':
|
||||
_fromEncoding = 'windows-1250'
|
||||
|
|
@ -41,15 +42,15 @@ else:
|
|||
|
||||
def toUnicode(msg):
|
||||
try:
|
||||
if not isinstance(msg, str):
|
||||
if isinstance(msg, bytes):
|
||||
if not isinstance(msg, six.text_type):
|
||||
if isinstance(msg, six.binary_type):
|
||||
return msg.decode(_fromEncoding, 'ignore')
|
||||
return str(msg)
|
||||
return six.text_type(msg)
|
||||
else:
|
||||
return msg
|
||||
except Exception:
|
||||
try:
|
||||
return str(msg)
|
||||
return six.text_type(msg)
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
|
|
|
|||
|
|
@ -151,8 +151,6 @@ 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"$ProgressPreference = 'SilentlyContinue'; try {{ $errBefore = $Error.Count; $output = & {{ {script} }} 2>&1; $errAfter = $Error.Count; $exitcode = $LASTEXITCODE; $output | Out-String; if ($exitcode) {{ exit $exitcode }} elseif ($errAfter -gt $errBefore) {{ exit 1 }} }} catch {{ $_ | Out-String; exit 1 }}"
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -67,85 +67,90 @@ class ThreadWithResult (threading.Thread):
|
|||
class ogLiveWorker(ServerWorker):
|
||||
thread_list = {}
|
||||
thread_lock = threading.Lock()
|
||||
_registered = False # Whether ogcore has acknowledged our registration
|
||||
|
||||
tbErroresScripts = [
|
||||
"Se han generado errores desconocidos. No se puede continuar la ejecución de este módulo", ## 0
|
||||
"001-Formato de ejecución incorrecto.",
|
||||
"002-Fichero o dispositivo no encontrado",
|
||||
"003-Error en partición de disco",
|
||||
"004-Partición o fichero bloqueado",
|
||||
"005-Error al crear o restaurar una imagen",
|
||||
"006-Sin sistema operativo",
|
||||
"007-Programa o función BOOLEAN no ejecutable",
|
||||
"008-Error en la creación del archivo de eco para consola remota",
|
||||
"009-Error en la lectura del archivo temporal de intercambio",
|
||||
"010-Error al ejecutar la llamada a la interface de administración",
|
||||
"011-La información retornada por la interface de administración excede de la longitud permitida",
|
||||
"012-Error en el envío de fichero por la red",
|
||||
"013-Error en la creación del proceso hijo",
|
||||
"014-Error de escritura en destino",
|
||||
"015-Sin Cache en el Cliente",
|
||||
"016-No hay espacio en la cache para almacenar fichero-imagen",
|
||||
"017-Error al Reducir el Sistema Archivos",
|
||||
"018-Error al Expandir el Sistema Archivos",
|
||||
"019-Valor fuera de rango o no válido.",
|
||||
"020-Sistema de archivos desconocido o no se puede montar",
|
||||
"021-Error en partición de caché local",
|
||||
"022-El disco indicado no contiene una particion GPT",
|
||||
"023-Error no definido",
|
||||
"024-Error no definido",
|
||||
"025-Error no definido",
|
||||
"026-Error no definido",
|
||||
"027-Error no definido",
|
||||
"028-Error no definido",
|
||||
"029-Error no definido",
|
||||
"030-Error al restaurar imagen - Imagen mas grande que particion",
|
||||
"031-Error al realizar el comando updateCache",
|
||||
"032-Error al formatear",
|
||||
"033-Archivo de imagen corrupto o de otra versión de partclone",
|
||||
"034-Error no definido",
|
||||
"035-Error no definido",
|
||||
"036-Error no definido",
|
||||
"037-Error no definido",
|
||||
"038-Error no definido",
|
||||
"039-Error no definido",
|
||||
"040-Error imprevisto no definido",
|
||||
"041-Error no definido",
|
||||
"042-Error no definido",
|
||||
"043-Error no definido",
|
||||
"044-Error no definido",
|
||||
"045-Error no definido",
|
||||
"046-Error no definido",
|
||||
"047-Error no definido",
|
||||
"048-Error no definido",
|
||||
"049-Error no definido",
|
||||
"050-Error en la generación de sintaxis de transferenica unicast",
|
||||
"051-Error en envio UNICAST de una particion",
|
||||
"052-Error en envio UNICAST de un fichero",
|
||||
"053-Error en la recepcion UNICAST de una particion",
|
||||
"054-Error en la recepcion UNICAST de un fichero",
|
||||
"055-Error en la generacion de sintaxis de transferenica Multicast",
|
||||
"056-Error en envio MULTICAST de un fichero",
|
||||
"057-Error en la recepcion MULTICAST de un fichero",
|
||||
"058-Error en envio MULTICAST de una particion",
|
||||
"059-Error en la recepcion MULTICAST de una particion",
|
||||
"060-Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER",
|
||||
"061-Error no definido",
|
||||
"062-Error no definido",
|
||||
"063-Error no definido",
|
||||
"064-Error no definido",
|
||||
"065-Error no definido",
|
||||
"066-Error no definido",
|
||||
"067-Error no definido",
|
||||
"068-Error no definido",
|
||||
"069-Error no definido",
|
||||
"070-Error al montar una imagen sincronizada.",
|
||||
"071-Imagen no sincronizable (es monolitica).",
|
||||
"072-Error al desmontar la imagen.",
|
||||
"073-No se detectan diferencias entre la imagen basica y la particion.",
|
||||
"074-Error al sincronizar, puede afectar la creacion/restauracion de la imagen.",
|
||||
"Error desconocido",
|
||||
]
|
||||
"001-Formato de ejecución incorrecto.",
|
||||
"002-Fichero o dispositivo no encontrado",
|
||||
"003-Error en partición de disco",
|
||||
"004-Partición o fichero bloqueado",
|
||||
"005-Error al crear o restaurar una imagen",
|
||||
"006-Sin sistema operativo",
|
||||
"007-Programa o función BOOLEAN no ejecutable",
|
||||
"008-Error en la creación del archivo de eco para consola remota",
|
||||
"009-Error en la lectura del archivo temporal de intercambio",
|
||||
"010-Error al ejecutar la llamada a la interface de administración",
|
||||
"011-La información retornada por la interface de administración excede de la longitud permitida",
|
||||
"012-Error en el envío de fichero por la red",
|
||||
"013-Error en la creación del proceso hijo",
|
||||
"014-Error de escritura en destino",
|
||||
"015-Sin Cache en el Cliente",
|
||||
"016-No hay espacio en la cache para almacenar fichero-imagen",
|
||||
"017-Error al Reducir el Sistema Archivos",
|
||||
"018-Error al Expandir el Sistema Archivos",
|
||||
"019-Valor fuera de rango o no válido.",
|
||||
"020-Sistema de archivos desconocido o no se puede montar",
|
||||
"021-Error en partición de caché local",
|
||||
"022-El disco indicado no contiene una particion GPT",
|
||||
"023-Error no definido",
|
||||
"024-Error no definido",
|
||||
"025-Error no definido",
|
||||
"026-Error no definido",
|
||||
"027-Error no definido",
|
||||
"028-Error no definido",
|
||||
"029-Error no definido",
|
||||
"030-Error al restaurar imagen - Imagen mas grande que particion",
|
||||
"031-Error al realizar el comando updateCache",
|
||||
"032-Error al formatear",
|
||||
"033-Archivo de imagen corrupto o de otra versión de partclone",
|
||||
"034-Error no definido",
|
||||
"035-Error no definido",
|
||||
"036-Error no definido",
|
||||
"037-Error no definido",
|
||||
"038-Error no definido",
|
||||
"039-Error no definido",
|
||||
"040-Error imprevisto no definido",
|
||||
"041-Error no definido",
|
||||
"042-Error no definido",
|
||||
"043-Error no definido",
|
||||
"044-Error no definido",
|
||||
"045-Error no definido",
|
||||
"046-Error no definido",
|
||||
"047-Error no definido",
|
||||
"048-Error no definido",
|
||||
"049-Error no definido",
|
||||
"050-Error en la generación de sintaxis de transferenica unicast",
|
||||
"051-Error en envio UNICAST de una particion",
|
||||
"052-Error en envio UNICAST de un fichero",
|
||||
"053-Error en la recepcion UNICAST de una particion",
|
||||
"054-Error en la recepcion UNICAST de un fichero",
|
||||
"055-Error en la generacion de sintaxis de transferenica Multicast",
|
||||
"056-Error en envio MULTICAST de un fichero",
|
||||
"057-Error en la recepcion MULTICAST de un fichero",
|
||||
"058-Error en envio MULTICAST de una particion",
|
||||
"059-Error en la recepcion MULTICAST de una particion",
|
||||
"060-Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER",
|
||||
"061-Error no definido",
|
||||
"062-Error no definido",
|
||||
"063-Error no definido",
|
||||
"064-Error no definido",
|
||||
"065-Error no definido",
|
||||
"066-Error no definido",
|
||||
"067-Error no definido",
|
||||
"068-Error no definido",
|
||||
"069-Error no definido",
|
||||
"070-Error al montar una imagen sincronizada.",
|
||||
"071-Imagen no sincronizable (es monolitica).",
|
||||
"072-Error al desmontar la imagen.",
|
||||
"073-No se detectan diferencias entre la imagen basica y la particion.",
|
||||
"074-Error al sincronizar, puede afectar la creacion/restauracion de la imagen.",
|
||||
"Error desconocido",
|
||||
]
|
||||
|
||||
def _on_reconnect(self):
|
||||
"""Hook called by mon() when connection to ogcore is restored. Override in subclasses."""
|
||||
pass
|
||||
|
||||
def notifier (self, job_id, result):
|
||||
result['job_id'] = job_id
|
||||
|
|
@ -239,10 +244,10 @@ class ogLiveWorker(ServerWorker):
|
|||
|
||||
elem['thread'].join (0.05)
|
||||
if not elem['thread'].is_alive():
|
||||
logger.debug (f'is no longer alive, k ({k}) thread ({elem["thread"]})')
|
||||
elem['running'] = False
|
||||
elem['result'] = elem['thread'].result
|
||||
del elem['thread']
|
||||
logger.info (f'job finished, job_id ({k}) result ({elem["result"]})')
|
||||
self.notifier (k, elem['result'])
|
||||
|
||||
time.sleep (1)
|
||||
|
|
@ -260,11 +265,15 @@ class ogLiveWorker(ServerWorker):
|
|||
body = {
|
||||
'iph': self.IPlocal,
|
||||
'timestamp': int (time.time()),
|
||||
'busy': any(elem['running'] for elem in self.thread_list.values()),
|
||||
'st': 'OGL',
|
||||
}
|
||||
#logger.debug (f'about to send ping ({body})')
|
||||
self.REST.sendMessage ('clients/status/webhook', body)
|
||||
result = self.REST.sendMessage('clients/status/webhook', body)
|
||||
if result is None:
|
||||
if self._registered:
|
||||
logger.debug('Lost connection to ogcore')
|
||||
self._registered = False
|
||||
elif not self._registered:
|
||||
logger.info('Connection to ogcore restored, re-registering')
|
||||
self._on_reconnect()
|
||||
|
||||
def interfaceAdmin (self, method, parametros=[]):
|
||||
if method not in ['ConsolaRemota', 'procesaCache']:
|
||||
|
|
@ -363,9 +372,6 @@ class ogLiveWorker(ServerWorker):
|
|||
## /DEBUG
|
||||
if 0 != p.returncode:
|
||||
cmd_txt = ' '.join (proc)
|
||||
logger.warning (f'command ({cmd_txt}) failed, stdout follows:')
|
||||
for l in sout.splitlines():
|
||||
logger.warning (f' {l}')
|
||||
logger.error (f'command ({cmd_txt}) failed, stderr follows:')
|
||||
for l in serr.splitlines():
|
||||
logger.error (f' {l}')
|
||||
|
|
@ -427,33 +433,23 @@ class ogLiveWorker(ServerWorker):
|
|||
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'
|
||||
try:
|
||||
b = dbus.SystemBus()
|
||||
dest = 'es.opengnsys.OGBrowser.browser'
|
||||
path = '/'
|
||||
interface = None
|
||||
method = 'setURL'
|
||||
b.call_blocking (dest, path, interface, method, 's', [url])
|
||||
except Exception as e:
|
||||
# Capturamos ServiceUnknown y también NameHasNoOwner que salta a veces por condiciones de carrera
|
||||
if 'ServiceUnknown' in str(e) or 'NameHasNoOwner' in str(e):
|
||||
if getattr(self, 'browser_process', None) is None or self.browser_process.poll() is not None:
|
||||
logger.warning (f'browser is not running ({e}), launching a new one')
|
||||
browser_log_fd = open ('/var/log/launch_browser.log', 'a')
|
||||
self.browser_process = subprocess.Popen (['/usr/bin/launch_browser', url], stdout=browser_log_fd, stderr=subprocess.STDOUT)
|
||||
browser_log_fd.close()
|
||||
else:
|
||||
logger.warning ('browser is already launching, ignoring duplicate launch request')
|
||||
if 'ServiceUnknown' in str(e):
|
||||
logger.warning ('browser is not running, launching a new one')
|
||||
browser_log_fd = open ('/var/log/launch_browser.log', 'a')
|
||||
self.browser_process = subprocess.Popen (['/usr/bin/launch_browser', url], stdout=browser_log_fd, stderr=subprocess.STDOUT)
|
||||
browser_log_fd.close()
|
||||
else:
|
||||
logger.error (f'Error al cambiar URL: ({e})')
|
||||
# Como medida de contingencia, si falla por cualquier otro error de DBus y no hay proceso, lo lanzamos
|
||||
if getattr(self, 'browser_process', None) is None or self.browser_process.poll() is not None:
|
||||
logger.warning ('Fallback: launching browser despite unknown DBus error')
|
||||
browser_log_fd = open ('/var/log/launch_browser.log', 'a')
|
||||
self.browser_process = subprocess.Popen (['/usr/bin/launch_browser', url], stdout=browser_log_fd, stderr=subprocess.STDOUT)
|
||||
browser_log_fd.close()
|
||||
else:
|
||||
return False
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -552,7 +548,8 @@ class ogLiveWorker(ServerWorker):
|
|||
logger.warning ('onActivation failed')
|
||||
return
|
||||
|
||||
threading.Thread (name='monitoring_thread', target=self.mon, daemon=True).start()
|
||||
self._registered = True
|
||||
threading.Thread(name='monitoring_thread', target=self.mon, daemon=True).start()
|
||||
|
||||
def _long_running_job (self, name, f, args):
|
||||
any_job_running = False
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
pywin32
|
||||
pyqt6
|
||||
requests
|
||||
|
||||
six
|
||||
pycryptodome
|
||||
pyinstaller
|
||||
|
|
|
|||
Loading…
Reference in New Issue