Compare commits
257 Commits
Author | SHA1 | Date |
---|---|---|
|
e20c671c1e | |
|
15d4f2cf6b | |
|
e920a3c681 | |
|
921706e9f0 | |
|
63944cef0e | |
|
3ae4471d5d | |
|
d1ce3b5cc9 | |
|
1fa2a4f0bb | |
|
ac9ab7e8f5 | |
|
05e0b83b91 | |
|
9d0589547f | |
|
ccb6cdfa54 | |
|
0d375e9aae | |
|
60cc47dd30 | |
|
acc9c2ed07 | |
|
dd445cee65 | |
|
d231816c0f | |
|
0099a05921 | |
|
db9f4992be | |
|
a13765a46e | |
|
8431c60a78 | |
|
130e1139c8 | |
|
a13c3e8a44 | |
|
747815c50d | |
|
27a6f2b470 | |
|
cc65ece84a | |
|
fdd4e6d2ca | |
|
16d24f3d96 | |
|
c39b2530f9 | |
|
15e11e6ea3 | |
|
ff042ca0d5 | |
|
4cf3d505de | |
|
52297f2815 | |
|
4fe87fc926 | |
|
a7c5ae4d27 | |
|
5c2fbc36e3 | |
|
61e2061cba | |
|
584abfa44c | |
|
6e39e473d2 | |
|
16554e5d93 | |
|
0e113ecd49 | |
|
70517e7ada | |
|
886df695dc | |
|
08dce3f2d9 | |
|
b9ce7b7f25 | |
|
4d34c5d274 | |
|
9af7469fc7 | |
|
f1c3cd2460 | |
|
e9a0b47e86 | |
|
db8f96d9d8 | |
|
a57d148922 | |
|
c9d6a54e54 | |
|
5212678dd6 | |
|
074efd42dc | |
|
e32a5f01c2 | |
|
c218eff895 | |
|
5e4ef1851f | |
|
a02029f0e2 | |
|
5c52377acf | |
|
684cdddab0 | |
|
5ce090133e | |
|
d5275c6dbe | |
|
1de04f3a7a | |
|
f8d6706897 | |
|
b5b09ec132 | |
|
f8563ec1a6 | |
|
658c72ad51 | |
|
14e893a21e | |
|
173379f99a | |
|
69be238f9f | |
|
be1fd7d624 | |
|
7f45c3083d | |
|
74a6937501 | |
|
a00fbcb76e | |
|
7293aee3ea | |
|
831da3a053 | |
|
2f4ade71dd | |
|
9ac107dde4 | |
|
029d3a778d | |
|
87a5258de5 | |
|
e2fcf02222 | |
|
dd82e4db50 | |
|
ef0920079b | |
|
6163c0b435 | |
|
b8733fea49 | |
|
66b6ea4fc4 | |
|
b7b2a58186 | |
|
4c789b6f43 | |
|
3929421cf5 | |
|
85dd23f957 | |
|
1fdeb2adeb | |
|
324ffdaffc | |
|
96bb0a7198 | |
|
1d89f6d50c | |
|
ca12de8351 | |
|
bf099333b5 | |
|
fcf9c5b2ba | |
|
c7b48a6c70 | |
|
9245bff31d | |
|
0f5cf07aa0 | |
|
a2df6afda7 | |
|
376dec466f | |
|
58b7f0d406 | |
|
2d6cd923a0 | |
|
3a9c293c33 | |
|
c19128619f | |
|
def6750cd1 | |
|
e1bd063bde | |
|
a0fb19ddbd | |
|
6757ab5697 | |
|
4fdcbe620f | |
|
3bfaf3c838 | |
|
2d7d023e99 | |
|
e39bb7401e | |
|
9e3d8be629 | |
|
e2f161ae97 | |
|
a3f4eafffb | |
|
08dba6d99a | |
|
239bfc21f7 | |
|
7efb0fdcc8 | |
|
1ee279afd5 | |
|
8d9a9ef5c3 | |
|
f21a75a23d | |
|
d3829cd46f | |
|
1e1974432e | |
|
068e0cf633 | |
|
72e4198762 | |
|
647489d507 | |
|
3191a171a1 | |
|
e28094ec1b | |
|
62a2514569 | |
|
aa0f62edcc | |
|
5cb2ef6cfc | |
|
1cf3c6bf2c | |
|
74ef2b7e15 | |
|
9b91eedf1b | |
|
39a367be79 | |
|
1864995066 | |
|
4b46827d17 | |
|
ba681a8116 | |
|
258df2af7c | |
|
22f83d8dea | |
|
0a6edd8cfe | |
|
82bf3a15f6 | |
|
38815028a6 | |
|
3682ac2b1d | |
|
274d8d448c | |
|
18f1314521 | |
|
ebf822aad6 | |
|
f5f58ce796 | |
|
3b9cab338d | |
|
60d7561afd | |
|
040ca6612f | |
|
8686b09d0e | |
|
b58c2c1f7f | |
|
e9f0e44010 | |
|
2dc264a187 | |
|
178d724ec0 | |
|
07a8a5b4af | |
|
a97755e368 | |
|
cc3146c15f | |
|
79dcefc0b4 | |
|
a67669b99f | |
|
fa328348f2 | |
|
2ba25ffa7b | |
|
35fbb59444 | |
|
b7b7351783 | |
|
dd3703ce63 | |
|
e5d2904cb9 | |
|
a5d0da2403 | |
|
8a369923ec | |
|
8c9fc6be3f | |
|
c92093ca8c | |
|
f497cfaf4d | |
|
ca3f9257ae | |
|
9e279dca35 | |
|
00dc9804dd | |
|
7defe5cc63 | |
|
5b058a5e33 | |
|
ffa80a9af2 | |
|
71e34b3d40 | |
|
1334a87c4d | |
|
5cf45c580e | |
|
ecee987dff | |
|
9fe674f30e | |
|
32d3621923 | |
|
9d3a320f36 | |
|
9d6596668f | |
|
1d93de1b59 | |
|
0cadbf3381 | |
|
53f44ad3ab | |
|
023886cea3 | |
|
25cfb31725 | |
|
2c3bbe82a8 | |
|
4b5193105c | |
|
08f7e44870 | |
|
67d5e81838 | |
|
b7788d9c1d | |
|
367e6dc782 | |
|
f7fd5d5570 | |
|
b6945279bc | |
|
d4e21dae13 | |
|
6740704919 | |
|
da7dd418c0 | |
|
8c6a6523d8 | |
|
f25252fcf9 | |
|
bfe563d902 | |
|
94eaba7688 | |
|
1d0057fd38 | |
|
e92d8855a1 | |
|
97246759c1 | |
|
5482d25116 | |
|
5f7ca5be15 | |
|
886bf5e616 | |
|
bf061b13db | |
|
360d0f8fb8 | |
|
64089e07af | |
|
dc7c6af6e8 | |
|
10a4c28ea6 | |
|
eab72819f2 | |
|
f69d3ab31d | |
|
64dea9f846 | |
|
833a0dc412 | |
|
10fab7839e | |
|
d7a7a1f0bf | |
|
5294919c98 | |
|
b84ab338f5 | |
|
6a01818ef7 | |
|
e1dbd89fc4 | |
|
3910a842d4 | |
|
2b257183d9 | |
|
be263c6e2f | |
|
64c933fbb8 | |
|
b21ea0708b | |
|
db05f930b5 | |
|
e7774214a1 | |
|
9525724449 | |
|
bb685d9700 | |
|
68c4c91495 | |
|
6900215e9c | |
|
9424789f69 | |
|
683d8d4427 | |
|
23503892b2 | |
|
7521269654 | |
|
de4289ae2b | |
|
e274dc0ee9 | |
|
12f0b1d654 | |
|
1b0abe2f61 | |
|
909a626001 | |
|
53e7d458c5 | |
|
1528428d8b | |
|
0440c7c37a | |
|
3a3b642556 | |
|
a850bd1236 | |
|
e298c499c9 | |
|
4aa86dedef | |
|
aa9ea5dec9 |
|
@ -0,0 +1,30 @@
|
|||
.DS_Store
|
||||
.editorconfig
|
||||
.idea
|
||||
**/*.swp
|
||||
__pycache__/
|
||||
linux/debian/.debhelper/
|
||||
linux/debian/files
|
||||
linux/debian/ogagent/
|
||||
linux/debian/ogagent.debhelper.log
|
||||
linux/debian/ogagent.postinst.debhelper
|
||||
linux/debian/ogagent.postrm.debhelper
|
||||
linux/configure-stamp
|
||||
linux/build-stamp
|
||||
macos/build
|
||||
windows/vc_redist.x64.exe
|
||||
ogagent_*_all.deb
|
||||
ogagent_*_amd64.buildinfo
|
||||
ogagent_*_amd64.changes
|
||||
ogagent_*_amd64.build
|
||||
OGAgentInstaller-*.pkg
|
||||
OGAgentSetup-*.exe
|
||||
bin
|
||||
src/build
|
||||
src/dist
|
||||
src/about_dialog_ui.py
|
||||
src/message_dialog_ui.py
|
||||
src/dist
|
||||
src/build
|
||||
windows/VERSION
|
||||
windows/VC_redist.x64.exe
|
|
@ -0,0 +1,344 @@
|
|||
|
||||
# Changelog
|
||||
|
||||
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.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
|
||||
|
||||
- EjecutarScript/ConsolaRemota: expect "scp" parameter encoded in base64
|
||||
|
||||
## [1.7.1] - 2025-03-25
|
||||
|
||||
### Fixed
|
||||
|
||||
- Make cfg2obj more robust
|
||||
|
||||
## [1.7.0] - 2025-03-21
|
||||
|
||||
### Removed
|
||||
|
||||
- Delete the new "ptt" parameter. It's not needed.
|
||||
|
||||
## [1.6.0] - 2025-03-12
|
||||
|
||||
### Changed
|
||||
|
||||
- Don't invoke bash code for some functionalities
|
||||
|
||||
## [1.5.0] - 2025-03-12
|
||||
|
||||
### Changed
|
||||
|
||||
- Accept new "ptt" parameter in /ogAdmCli/Configurar
|
||||
|
||||
### Removed
|
||||
|
||||
- No longer recognise the unused "che" parameter in /ogAdmCli/Configurar
|
||||
|
||||
## [1.4.9] - 2025-02-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Notify ogcore when agent shuts down within oglive
|
||||
|
||||
## [1.4.8] - 2025-02-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Optionally return disk config in /status
|
||||
|
||||
## [1.4.7] - 2025-02-04
|
||||
|
||||
### Changed
|
||||
|
||||
- Merge server modules
|
||||
|
||||
### Added
|
||||
|
||||
- Track the progress of children
|
||||
|
||||
## [1.4.6] - 2025-01-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Point to the new menu browser
|
||||
|
||||
## [1.4.5] - 2024-11-29
|
||||
|
||||
### Added
|
||||
|
||||
- Kill long running jobs in oglive
|
||||
|
||||
## [1.4.5~pre8] - 2024-11-27
|
||||
|
||||
### Added
|
||||
|
||||
- Add Configurar() to the CloningEngine module
|
||||
|
||||
## [1.4.5~pre7] - 2024-11-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Use old browser again
|
||||
|
||||
## [1.4.5~pre6] - 2024-11-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not use envvars for the operating-system module
|
||||
|
||||
## [1.4.5~pre5] - 2024-11-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Avoid some KeyErrors
|
||||
|
||||
## [1.4.5~pre4] - 2024-11-15
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't die when ogcore returns HTTP 4xx or 5xx
|
||||
|
||||
### Changed
|
||||
|
||||
- Get ogcore IP and port from the environment
|
||||
|
||||
## [1.4.5~pre3] - 2024-11-06
|
||||
- Kill long running jobs in oglive (not-yet-working draft)
|
||||
|
||||
## [1.4.5~pre2] - 2024-11-06
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove race condition due to several monitoring threads
|
||||
|
||||
### Changed
|
||||
|
||||
- Include job_id in asynchronous responses
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove vim swapfiles from the package contents
|
||||
|
||||
## [1.4.5~pre1] - 2024-11-06
|
||||
|
||||
### Changed
|
||||
|
||||
- CrearImagen: return inventory inline
|
||||
|
||||
## [1.4.4] - 2024-10-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use logger.debug() to prevent the windows agent from dying
|
||||
|
||||
### Changed
|
||||
|
||||
- Make status() call synchronous
|
||||
|
||||
## [1.4.3] - 2024-10-17
|
||||
|
||||
### Changed
|
||||
|
||||
- Use new OGBrowser
|
||||
|
||||
## [1.4.2] - 2024-10-15
|
||||
|
||||
### Added
|
||||
|
||||
- Have ogAdmClient/status return information about network and disks
|
||||
|
||||
## [1.4.1] - 2024-10-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bugfix: move data structure to the right class
|
||||
|
||||
## [1.4.0] - 2024-10-11
|
||||
- Add more functionality
|
||||
|
||||
### Changed
|
||||
|
||||
- Begin using semantic versioning
|
||||
|
||||
## [1.3.8] - 2024-10-01
|
||||
|
||||
### Added
|
||||
|
||||
- Add more functionality to the ogAdmClient module
|
||||
|
||||
## [1.3.7] - 2024-09-27
|
||||
|
||||
### Added
|
||||
|
||||
- CloningEngine: RESTfully keep a list of long-running jobs
|
||||
|
||||
## [1.3.6] - 2024-09-19
|
||||
|
||||
### Added
|
||||
|
||||
- Add more functionality to the ogAdmClient module
|
||||
- Add CloningEngine module
|
||||
|
||||
## [1.3.5] - 2024-08-29
|
||||
|
||||
### Changed
|
||||
|
||||
- Don't unconditionally load modules--dynamically load everything
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove old, unused code
|
||||
|
||||
## [1.3.4] - 2024-07-30
|
||||
|
||||
### Added
|
||||
|
||||
- Implement JobMgr
|
||||
|
||||
## [1.3.1] - 2024-06-26
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrate the update script from shell to python
|
||||
- pyinstaller: include the 'img' subdir
|
||||
- take icons from 'img'
|
||||
|
||||
## [1.3.0-2] - 2024-04-25
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add missing dependency on zenity
|
||||
|
||||
## [1.3.0] - 2024-04-25
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to Qt 6
|
||||
|
||||
## [1.2.0] - 2020-05-4
|
||||
|
||||
### Changed
|
||||
|
||||
- Python 3 and Qt 5 compatibility
|
||||
|
||||
## [1.1.1b] - 2020-02-7
|
||||
|
||||
### Changed
|
||||
|
||||
- Use python-distro to detect the distribution version
|
||||
|
||||
## [1.1.1] - 2019-05-23
|
||||
|
||||
### Changed
|
||||
|
||||
- Set connection timeout
|
||||
- Compatibility with "Exam Mode" from the University of Seville
|
||||
|
||||
## [1.1.0a] - 2019-05-22
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix a bug when activating the agent with some network devices
|
||||
|
||||
## [1.1.0] - 2016-10-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Functional OpenGnsys Agent interacting with OpenGnsys Server 1.1.0
|
||||
|
||||
## [1.0.0] - 2015-07-18
|
||||
|
||||
### Added
|
||||
|
||||
- Initial release for OpenGnsys Agent
|
|
@ -1,25 +1,22 @@
|
|||
OGAgent: agente OpenGnsys para sistemas operativos INSTALL.es.txt
|
||||
====================================================================
|
||||
|
||||
|
||||
Requisitos de creación
|
||||
----------------------
|
||||
Sisitema operativo Linux con los siguientes paquetes instalados:
|
||||
- Subversion
|
||||
- GNU C++, Python, librerías PyQt4
|
||||
Sistema operativo Linux con los siguientes paquetes instalados:
|
||||
- GNU C++, Python, librerías PyQt6
|
||||
- Creación de instalador Exe (Wine 32 bits, Wine Gecko, Wine Mono, Samba Winbind, Cabextrct)
|
||||
- Creación de paquetes Deb (debhelper, dpkg-dev)
|
||||
- Creación de paquetes RPM (rpm-build)
|
||||
- Creación de paquetes Pkg (xar, bomutils)
|
||||
|
||||
|
||||
Crear instaladores de OGAgent
|
||||
-----------------------------
|
||||
- Paso previo: actaulizar componentes gráficos de PyQt para OGAgnet:
|
||||
- Paso previo: actualizar componentes gráficos de PyQt para OGAgent:
|
||||
src/update.sh
|
||||
|
||||
- Crear paquetes Deb y RPM para distribuciones Linux (requiere permisos de "root"):
|
||||
sudo linux/build-packages.sh
|
||||
- Crear paquetes Deb distribuciones debian/ubuntu
|
||||
linux/build-packages.sh
|
||||
|
||||
- Crear paquete Pkg para sistemas operativos macOS X:
|
||||
sudo macos/build-pkg.sh
|
||||
|
@ -27,8 +24,8 @@ Crear instaladores de OGAgent
|
|||
- Crear el programa instalador para sistemas operativos Windows:
|
||||
windows/build-windows.sh
|
||||
|
||||
- Subir los nuevos ficheros .deb, .rpm, .pkg y .exe generados al directorio
|
||||
/opt/opengnsys/www/descargas del servidor OpenGnsys.
|
||||
- Subir los nuevos ficheros .deb, .pkg y .exe generados al directorio
|
||||
/opt/opengnsys/www/descargas del servidor OpenGnsys.
|
||||
|
||||
|
||||
Instalar OGAgent en cliente modelo
|
||||
|
@ -43,20 +40,6 @@ Instalar OGAgent en cliente modelo
|
|||
- Iniciar el servicio (se iniciará automáticamente en el proceso de arranque):
|
||||
sudo service ogagent start
|
||||
|
||||
- Red Hat, Fedora y derivados (como root):
|
||||
- Descargar e instalar el agente:
|
||||
yum install ogagent-Version.noarch.rpm (Red Hat/CentOS)
|
||||
dnf install ogagent-Version.noarch.rpm (Fedora)
|
||||
- Configurar el agente:
|
||||
sed -i "0,/remote=/ s,remote=.*,remote=https://IPServidorOpenGnsys/opengnsys/rest/," /usr/share/OGAgent/cfg/ogagent.cfg
|
||||
- Puede ser necesario corregir permisos antes de iniciar el servicio:
|
||||
chmod +x /etc/init.d/ogagent
|
||||
- Iniciar el servicio (se iniciará automáticamente en el proceso de arranque):
|
||||
service ogagent start
|
||||
|
||||
- OpenSuSE:
|
||||
(en preparación)
|
||||
|
||||
- Windows (como usuario administrador):
|
||||
- Descargar e instalar el agente ejecutando:
|
||||
OGAgentSetup-Version.exe
|
||||
|
@ -83,5 +66,3 @@ Postconfiguración para clientes clonados
|
|||
- Ejecutar manualmente o configurar automáticamente OGAgent en los clientes clonados
|
||||
en el script de postconfiguración tras restuarar imagen:
|
||||
ogConfigureOgagent NDisco Npart
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
pipeline {
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
echo 'Jenkinsfile for ogagent'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 43 KiB |
|
@ -0,0 +1,47 @@
|
|||
## Crear tarea programada para matar el agente de Windows al cerrar sesión
|
||||
|
||||
1. Abrir el task scheduler y pinchar en Create task:
|
||||
|
||||

|
||||
|
||||
2. Rellenar el nombre y luego pinchar en Change user or group:
|
||||
|
||||

|
||||
|
||||
3. Pinchar en Advanced:
|
||||
|
||||

|
||||
|
||||
4. Pinchar en Find now:
|
||||
|
||||

|
||||
|
||||
5. Seleccionar Administrator y luego Ok, y luego Ok:
|
||||
|
||||

|
||||
|
||||
6. De vuelta en la pantalla de crear tarea, ir a la pestaña Triggers y pinchar en New:
|
||||
|
||||

|
||||
|
||||
7. Seleccionar "On disconnect from user session", seleccionar "Connection from local computer", y luego Ok:
|
||||
|
||||

|
||||
|
||||
8. Ir a la pestaña "Actions" y pinchar en New:
|
||||
|
||||

|
||||
|
||||
9. Pinchar en Browse, seleccionar C:\Program Files (x86)\OGAgent\stop-agent.ps1, y luego Ok:
|
||||
|
||||

|
||||
|
||||
10. Ir a la pestaña Conditions y desmarcar las condiciones relativas a Power:
|
||||
|
||||

|
||||
|
||||
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".
|
|
@ -39,7 +39,6 @@ install-ogagent:
|
|||
cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR)
|
||||
# QT Dialogs & resources
|
||||
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
|
||||
cp $(SOURCEDIR)/OGAgent_rc.py $(LIBDIR)
|
||||
# Version file
|
||||
cp $(SOURCEDIR)/VERSION $(LIBDIR)
|
||||
|
||||
|
|
|
@ -1,37 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd $(dirname "$0")
|
||||
top=$(pwd)
|
||||
|
||||
VERSION="$(cat ../src/VERSION 2>/dev/null)" || VERSION="1.1.1"
|
||||
RELEASE="1"
|
||||
|
||||
# Debian based
|
||||
dpkg-buildpackage -b -d
|
||||
|
||||
# Fix version number.
|
||||
sed -e "s/version 0.0.0/version ${VERSION}/g" \
|
||||
-e "s/release 1/release ${RELEASE}/g" ogagent-template.spec > ogagent-$VERSION.spec
|
||||
|
||||
# Now fix dependencies for opensuse
|
||||
sed -e "s/name ogagent/name ogagent-opensuse/g" \
|
||||
-e "s/version 0.0.0/version ${VERSION}/g" \
|
||||
-e "s/release 1/release ${RELEASE}/g" \
|
||||
-e "s/chkconfig//g" \
|
||||
-e "s/initscripts/insserv/g" \
|
||||
-e "s/PyQt4/python-qt4/g" \
|
||||
-e "s/libXScrnSaver/libXss1/g" ogagent-template.spec > ogagent-opensuse-$VERSION.spec
|
||||
|
||||
|
||||
# Right now, ogagent-xrdp-1.7.0.spec is not needed
|
||||
for pkg in ogagent-$VERSION.spec ogagent-opensuse-$VERSION.spec; do
|
||||
|
||||
rm -rf rpm
|
||||
for folder in SOURCES BUILD RPMS SPECS SRPMS; do
|
||||
mkdir -p rpm/$folder
|
||||
done
|
||||
|
||||
rpmbuild -v -bb --clean --buildroot=$top/rpm/BUILD/$pkg-root --target noarch $pkg 2>&1
|
||||
done
|
||||
|
||||
#rm ogagent-$VERSION
|
||||
debuild -D --build=binary --post-clean --lintian-opts --profile debian
|
||||
|
|
|
@ -1,3 +1,291 @@
|
|||
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
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 31 Mar 2025 10:16:07 +0200
|
||||
|
||||
ogagent (2.0.0-1) stable; urgency=medium
|
||||
|
||||
* EjecutarScript/ConsolaRemota: expect "scp" parameter encoded in base64
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 26 Mar 2025 10:40:14 +0100
|
||||
|
||||
ogagent (1.7.1-1) stable; urgency=medium
|
||||
|
||||
* Make cfg2obj more robust
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 25 Mar 2025 13:31:33 +0100
|
||||
|
||||
ogagent (1.7.0-1) stable; urgency=medium
|
||||
|
||||
* Delete the new "ptt" parameter. It's not needed.
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 21 Mar 2025 14:19:56 +0100
|
||||
|
||||
ogagent (1.6.0-1) stable; urgency=medium
|
||||
|
||||
* Don't invoke bash code for some functionalities
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 12 Mar 2025 11:59:36 +0100
|
||||
|
||||
ogagent (1.5.0-1) stable; urgency=medium
|
||||
|
||||
* Accept new "ptt" parameter in /ogAdmCli/Configurar
|
||||
* No longer recognise the unused "che" parameter in /ogAdmCli/Configurar
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 12 Mar 2025 11:45:37 +0100
|
||||
|
||||
ogagent (1.4.9-1) stable; urgency=medium
|
||||
|
||||
* Notify ogcore when agent shuts down within oglive
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 20 Feb 2025 11:58:29 +0100
|
||||
|
||||
ogagent (1.4.8-1) stable; urgency=medium
|
||||
|
||||
* Optionally return disk config in /status
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 18 Feb 2025 13:48:54 +0100
|
||||
|
||||
ogagent (1.4.7-1) stable; urgency=medium
|
||||
|
||||
* Merge server modules
|
||||
* Track the progress of children
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 04 Feb 2025 14:12:19 +0100
|
||||
|
||||
ogagent (1.4.6-1) stable; urgency=medium
|
||||
|
||||
* Point to the new menu browser
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 14 Jan 2025 12:00:24 +0100
|
||||
|
||||
ogagent (1.4.5-1) stable; urgency=medium
|
||||
|
||||
* Kill long running jobs in oglive
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 29 Nov 2024 10:22:36 +0100
|
||||
|
||||
ogagent (1.4.5~pre8-1) stable; urgency=medium
|
||||
|
||||
* Add Configurar() to the CloningEngine module
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 27 Nov 2024 20:02:42 +0100
|
||||
|
||||
ogagent (1.4.5~pre7-1) stable; urgency=medium
|
||||
|
||||
* Use old browser again
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 20 Nov 2024 14:24:44 +0100
|
||||
|
||||
ogagent (1.4.5~pre6-1) stable; urgency=medium
|
||||
|
||||
* Do not use envvars for the operating-system module
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 20 Nov 2024 13:45:21 +0100
|
||||
|
||||
ogagent (1.4.5~pre5-1) stable; urgency=medium
|
||||
|
||||
* Avoid some KeyErrors
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 18 Nov 2024 12:14:27 +0100
|
||||
|
||||
ogagent (1.4.5~pre4-1) stable; urgency=medium
|
||||
|
||||
* Don't die when ogcore returns HTTP 4xx or 5xx
|
||||
* Get ogcore IP and port from the environment
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 15 Nov 2024 11:43:01 +0100
|
||||
|
||||
ogagent (1.4.5~pre3-1) stable; urgency=medium
|
||||
|
||||
* Kill long running jobs in oglive (not-yet-working draft)
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 06 Nov 2024 14:11:32 +0100
|
||||
|
||||
ogagent (1.4.5~pre2-1) stable; urgency=medium
|
||||
|
||||
* Remove race condition due to several monitoring threads
|
||||
* Include job_id in asynchronous responses
|
||||
* Remove vim swapfiles from the package contents
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 06 Nov 2024 13:24:03 +0100
|
||||
|
||||
ogagent (1.4.5~pre1-1) stable; urgency=medium
|
||||
|
||||
* CrearImagen: return inventory inline
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 06 Nov 2024 12:41:14 +0100
|
||||
|
||||
ogagent (1.4.4-1) stable; urgency=medium
|
||||
|
||||
* Use logger.debug() to prevent the windows agent from dying
|
||||
* Make status() call synchronous
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 17 Oct 2024 19:13:58 +0200
|
||||
|
||||
ogagent (1.4.3-1) stable; urgency=medium
|
||||
|
||||
* Use new OGBrowser
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 17 Oct 2024 09:43:08 +0200
|
||||
|
||||
ogagent (1.4.2-1) stable; urgency=medium
|
||||
|
||||
* Have ogAdmClient/status return information about network and disks
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 15 Oct 2024 10:35:16 +0200
|
||||
|
||||
ogagent (1.4.1-1) stable; urgency=medium
|
||||
|
||||
* Bugfix: move data structure to the right class
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 11 Oct 2024 13:51:55 +0200
|
||||
|
||||
ogagent (1.4.0-1) stable; urgency=medium
|
||||
|
||||
* Add more functionality
|
||||
* Begin using semantic versioning
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 11 Oct 2024 13:06:51 +0200
|
||||
|
||||
ogagent (1.3.8-1) stable; urgency=medium
|
||||
|
||||
* Add more functionality to the ogAdmClient module
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 01 Oct 2024 13:41:48 +0200
|
||||
|
||||
ogagent (1.3.7-1) stable; urgency=medium
|
||||
|
||||
* CloningEngine: RESTfully keep a list of long-running jobs
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Fri, 27 Sep 2024 18:03:16 +0200
|
||||
|
||||
ogagent (1.3.6-1) stable; urgency=medium
|
||||
|
||||
* Add more functionality to the ogAdmClient module
|
||||
* Add CloningEngine module
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 19 Sep 2024 13:28:17 +0200
|
||||
|
||||
ogagent (1.3.5-1) stable; urgency=medium
|
||||
|
||||
* Don't unconditionally load modules--dynamically load everything
|
||||
* Remove old, unused code
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 29 Aug 2024 10:47:12 +0200
|
||||
|
||||
ogagent (1.3.4-1) stable; urgency=medium
|
||||
|
||||
* Implement JobMgr
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Tue, 30 Jul 2024 13:39:55 +0200
|
||||
|
||||
ogagent (1.3.1-1) stable; urgency=medium
|
||||
|
||||
* Migrate the update script from shell to python
|
||||
* pyinstaller: include the 'img' subdir
|
||||
* take icons from 'img'
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Wed, 26 Jun 2024 15:16:57 +0200
|
||||
|
||||
ogagent (1.3.0-2) stable; urgency=medium
|
||||
|
||||
* Add missing dependency on zenity
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 25 Apr 2024 15:53:16 +0200
|
||||
|
||||
ogagent (1.3.0-1) stable; urgency=medium
|
||||
|
||||
* Upgrade to Qt 6
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Thu, 25 Apr 2024 12:50:20 +0200
|
||||
|
||||
ogagent (1.2.0) unstable; urgency=medium
|
||||
|
||||
* Python 3 and Qt 5 compatibility
|
||||
|
||||
-- OpenGnsys developers <info@opengnsys.es> Mon, 4 May 2020 18:00:00 +0100
|
||||
|
||||
ogagent (1.1.1b) stable; urgency=medium
|
||||
|
||||
* Use python-distro to detect the distribution version
|
||||
|
|
|
@ -1 +1 @@
|
|||
9
|
||||
10
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Source: ogagent
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: Ramón M. Gómez <ramongomez@us.es>
|
||||
Maintainer: OpenGnsys developers <info@opengnsys.es>
|
||||
Build-Depends: debhelper (>= 7), po-debconf
|
||||
Standards-Version: 3.9.2
|
||||
Homepage: https://opengnsys.es/
|
||||
|
@ -11,8 +11,7 @@ Section: admin
|
|||
Priority: optional
|
||||
Architecture: all
|
||||
Depends:
|
||||
policykit-1 (>= 0.100), python2 (>=2.7) | python (>= 2.7), python-qt4 (>= 4.9),, python-requests (>= 0.8.2),
|
||||
python-six (>= 1.1), python-prctl (>= 1.1.1), python-distro, libxss1, ${misc:Depends}
|
||||
Suggests: gnome-shell-extension-top-icons-plus
|
||||
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.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
|
||||
Name: ogagent
|
||||
Maintainer: Ramón M. Gómez
|
||||
Maintainer: OpenGnsys developers
|
||||
Source: https://opengnsys.es
|
||||
|
||||
Copyright: 2014 Virtual Cable S.L.U.
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# Automatically added by dh_installinit
|
||||
if [ -x "/etc/init.d/ogagent" ]; then
|
||||
update-rc.d ogagent defaults >/dev/null || exit $?
|
||||
fi
|
||||
# End automatically added section
|
|
@ -1,12 +0,0 @@
|
|||
# Automatically added by dh_installinit
|
||||
if [ "$1" = "purge" ] ; then
|
||||
update-rc.d ogagent remove >/dev/null
|
||||
fi
|
||||
|
||||
|
||||
# In case this system is running systemd, we make systemd reload the unit files
|
||||
# to pick up changes.
|
||||
if [ -d /run/systemd/system ] ; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
fi
|
||||
# End automatically added section
|
|
@ -1,2 +0,0 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
|
@ -22,6 +22,7 @@ install: build
|
|||
dh_prep
|
||||
dh_installdirs
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/ogagent install-ogagent
|
||||
find $(CURDIR) -name '*.swp' -exec rm -f '{}' ';'
|
||||
binary-arch: build install
|
||||
# emptyness
|
||||
binary-indep: build install
|
||||
|
@ -31,7 +32,7 @@ binary-indep: build install
|
|||
dh_installdocs
|
||||
dh_installdebconf
|
||||
dh_installinit --no-start
|
||||
dh_python2=python
|
||||
dh_python3=python
|
||||
dh_compress
|
||||
dh_link
|
||||
dh_fixperms
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
%define _topdir %(echo $PWD)/rpm
|
||||
%define name ogagent
|
||||
%define version 0.0.0
|
||||
%define release 1
|
||||
%define buildroot %{_topdir}/%{name}-%{version}-%{release}-root
|
||||
|
||||
BuildRoot: %{buildroot}
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
Release: %{release}
|
||||
Summary: OpenGnsys Agent for Operating Systems
|
||||
License: BSD3
|
||||
Group: Admin
|
||||
Requires: chkconfig initscripts python-six python-requests python-distro PyQt4 libXScrnSaver
|
||||
Vendor: OpenGnsys Project
|
||||
URL: https://opengnsys.es/
|
||||
Provides: ogagent
|
||||
|
||||
%define _rpmdir ../
|
||||
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
|
||||
|
||||
|
||||
%install
|
||||
curdir=`pwd`
|
||||
cd ../..
|
||||
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install-ogagent
|
||||
cd $curdir
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
curdir=`pwd`
|
||||
cd ../..
|
||||
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh clean
|
||||
cd $curdir
|
||||
|
||||
|
||||
%post
|
||||
systemctl enable ogagent.service > /dev/null 2>&1
|
||||
|
||||
%preun
|
||||
systemctl disable ogagent.service > /dev/null 2>&1
|
||||
systemctl stop ogagent.service > /dev/null 2>&1
|
||||
|
||||
%postun
|
||||
# $1 == 0 on uninstall, == 1 on upgrade for preun and postun (just a reminder for me... :) )
|
||||
if [ $1 -eq 0 ]; then
|
||||
rm -rf /etc/ogagent
|
||||
rm /var/log/ogagent.log
|
||||
fi
|
||||
# And, posibly, the .pyc leaved behind on /usr/share/OGAgent
|
||||
rm -rf /usr/share/OGAgent > /dev/null 2>&1
|
||||
|
||||
%description
|
||||
This package provides the required components to allow this machine to work on an environment managed by OpenGnsys.
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
/etc/ogagent
|
||||
/etc/xdg/autostart/OGAgentTool.desktop
|
||||
/etc/init.d/ogagent
|
||||
/usr/bin/OGAgentTool-startup
|
||||
/usr/bin/ogagent
|
||||
/usr/bin/OGAgentTool
|
||||
/usr/share/OGAgent/*
|
||||
/usr/share/autostart/OGAgentTool.desktop
|
||||
|
||||
%changelog
|
||||
* Fri Feb 07 2020 Ramón M. Gómez <ramongomez@us.es> - 1.1.1b-1
|
||||
- Use python-distro to detect the distribution version
|
||||
|
||||
* Thu May 23 2019 Ramón M. Gómez <ramongomez@us.es> - 1.1.1-1
|
||||
- Set connection timeout
|
||||
- Compatibility with "Exam Mode" from the University of Seville
|
||||
|
||||
* Wed May 22 2019 Ramón M. Gómez <ramongomez@us.es> - 1.1.0a-1
|
||||
- Fix a bug when activating the agent with some network devices
|
||||
|
||||
* Tue Oct 13 2016 Ramón M. Gómez <ramongomez@us.es> - 1.1.0-1
|
||||
- Functional OpenGnsys Agent interacting with OpenGnsys Server 1.1.0
|
||||
|
||||
* Tue Jul 18 2015 Adolfo Gómez García <agomez@virtualcable.es> - 1.0.0-1
|
||||
- Initial release for OpenGnsys Agent
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
for p in python python2; do
|
||||
[ -z "$PYTHON" ] && [ $($p -c 'import sys; print(sys.version_info[0])') -eq 2 ] && PYTHON=$p
|
||||
for p in python python3; do
|
||||
[ "$(command -v $p)" ] && [ -z "$PYTHON" ] && [ $($p -c 'import sys; print(sys.version_info[0])') -eq 3 ] && PYTHON=$p
|
||||
done
|
||||
if [ -z "$PYTHON" ]; then
|
||||
echo "ERROR: OGAgent needs Python 2" &>2
|
||||
echo "ERROR: OGAgent needs Python 3" &>2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
for p in python python2; do
|
||||
[ -z "$PYTHON" ] && [ $($p -c 'import sys; print(sys.version_info[0])') -eq 2 ] && PYTHON=$p
|
||||
for p in python python3; do
|
||||
[ "$(command -v $p)" ] && [ -z "$PYTHON" ] && [ $($p -c 'import sys; print(sys.version_info[0])') -eq 3 ] && PYTHON=$p
|
||||
done
|
||||
if [ -z "$PYTHON" ]; then
|
||||
echo "ERROR: OGAgent needs Python 2" &>2
|
||||
echo "ERROR: OGAgent needs Python 3" &>2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/bin/bash
|
||||
# Create macOS installation packages.
|
||||
# Based on bomutils tutorail: http://bomutils.dyndns.org/tutorial.html
|
||||
# Based on bomutils tutorial: http://bomutils.dyndns.org/tutorial.html
|
||||
|
||||
cd $(dirname $0)
|
||||
[ -r ../src/VERSION ] && VERSION="$(cat ../src/VERSION)" || VERSION="1.1.0"
|
||||
[ -r ../src/VERSION ] && VERSION="$(cat ../src/VERSION)" || (echo "Can't get version from ../src/VERSION" 1>&2; exit 1)
|
||||
AUTHOR="OpenGnsys Project"
|
||||
|
||||
# Create empty directories.
|
||||
|
@ -12,7 +12,7 @@ mkdir -p build && cd build
|
|||
mkdir -p flat/base.pkg flat/Resources/en.lproj
|
||||
mkdir -p root/Applications
|
||||
|
||||
# Copy application and script files.
|
||||
# Copy application and script files
|
||||
cp -a ../../src root/Applications/OGAgent.app
|
||||
cp -a ../scripts .
|
||||
|
||||
|
@ -84,4 +84,3 @@ EOT
|
|||
# Create new Xar application archive.
|
||||
rm -f ../../../OGAgentInstaller-$VERSION.pkg
|
||||
( cd flat && xar --compression none -cf "../../../OGAgentInstaller-$VERSION.pkg" * )
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>es.opengnsys.ogagent</string>
|
||||
<string>es.opengnsys.agent.system</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/bin/ogagent</string>
|
||||
<string>/usr/local/bin/ogagent</string>
|
||||
<string>start</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>es.opengnsys.agent.user</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/usr/local/bin:/usr/bin:/bin</string>
|
||||
</dict>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/Applications/OGAgent.app</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Applications/OGAgent.app/OGAgentUser.py</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,832 @@
|
|||
#!/usr/bin/env python3
|
||||
# encoding: utf8
|
||||
|
||||
"""
|
||||
This program is taken from https://github.com/brona/iproute2mac
|
||||
When doing 'brew install iproute2mac', we get an old version (1.4.2) that doesn't support 'ip -json'
|
||||
The alternative installation method recomended in the project's README file is 'curl; chmod; mv'
|
||||
Therefore we make the decision of shipping this ip.py (version 1.5.0) along opengnsys, which is pretty much the same as curling it
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
iproute2mac
|
||||
CLI wrapper for basic network utilites on Mac OS X.
|
||||
Homepage: https://github.com/brona/iproute2mac
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2015 Bronislav Robenek <brona@robenek.me>
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import socket
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import types
|
||||
|
||||
# Version
|
||||
VERSION = "1.5.0"
|
||||
|
||||
# Utilities
|
||||
SUDO = "/usr/bin/sudo"
|
||||
IFCONFIG = "/sbin/ifconfig"
|
||||
ROUTE = "/sbin/route"
|
||||
NETSTAT = "/usr/sbin/netstat"
|
||||
NDP = "/usr/sbin/ndp"
|
||||
ARP = "/usr/sbin/arp"
|
||||
NETWORKSETUP = "/usr/sbin/networksetup"
|
||||
|
||||
|
||||
# Helper functions
|
||||
def perror(*args):
|
||||
sys.stderr.write(*args)
|
||||
sys.stderr.write("\n")
|
||||
|
||||
|
||||
def execute_cmd(cmd):
|
||||
print("Executing: %s" % cmd)
|
||||
status, output = subprocess.getstatusoutput(cmd)
|
||||
if status == 0: # unix/linux commands 0 true, 1 false
|
||||
print(output)
|
||||
return True
|
||||
else:
|
||||
perror(output)
|
||||
return False
|
||||
|
||||
|
||||
def json_dump(data, pretty):
|
||||
if pretty:
|
||||
print(json.dumps(data, indent=4))
|
||||
else:
|
||||
print(json.dumps(data, separators=(",", ":")))
|
||||
return True
|
||||
|
||||
# Classful to CIDR conversion with "default" being passed through
|
||||
def cidr_from_netstat_dst(target):
|
||||
if target == "default":
|
||||
return target
|
||||
|
||||
dots = target.count(".")
|
||||
if target.find("/") == -1:
|
||||
addr = target
|
||||
netmask = (dots + 1) * 8
|
||||
else:
|
||||
[addr, netmask] = target.split("/")
|
||||
|
||||
addr = addr + ".0" * (3 - dots)
|
||||
return addr + "/" + str(netmask)
|
||||
|
||||
|
||||
# Convert hexadecimal netmask in prefix length
|
||||
def netmask_to_length(mask):
|
||||
return int(mask, 16).bit_count()
|
||||
|
||||
|
||||
def any_startswith(words, test):
|
||||
for word in words:
|
||||
if word.startswith(test):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Handles passsing return value, error messages and program exit on error
|
||||
def help_msg(help_func):
|
||||
def wrapper(func):
|
||||
def inner(*args, **kwargs):
|
||||
if not func(*args, **kwargs):
|
||||
specific = eval(help_func)
|
||||
if specific:
|
||||
if isinstance(specific, types.FunctionType):
|
||||
if args and kwargs:
|
||||
specific(*args, **kwargs)
|
||||
else:
|
||||
specific()
|
||||
return False
|
||||
else:
|
||||
raise Exception("Function expected for: " + help_func)
|
||||
else:
|
||||
raise Exception(
|
||||
"Function variant not defined: " + help_func
|
||||
)
|
||||
return True
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# Generate random MAC address with XenSource Inc. OUI
|
||||
# http://www.linux-kvm.com/sites/default/files/macgen.py
|
||||
def randomMAC():
|
||||
mac = [
|
||||
0x00,
|
||||
0x16,
|
||||
0x3E,
|
||||
random.randint(0x00, 0x7F),
|
||||
random.randint(0x00, 0xFF),
|
||||
random.randint(0x00, 0xFF),
|
||||
]
|
||||
return ":".join(["%02x" % x for x in mac])
|
||||
|
||||
|
||||
# Decode ifconfig output
|
||||
def parse_ifconfig(res, af, address):
|
||||
links = []
|
||||
count = 1
|
||||
|
||||
for r in res.split("\n"):
|
||||
if re.match(r"^\w+:", r):
|
||||
if count > 1:
|
||||
links.append(link)
|
||||
(ifname, flags, mtu, ifindex) = re.findall(r"^(\w+): flags=\d+<(.*)> mtu (\d+) index (\d+)", r)[0]
|
||||
flags = flags.split(",")
|
||||
link = {
|
||||
"ifindex": int(ifindex),
|
||||
"ifname": ifname,
|
||||
"flags": flags,
|
||||
"mtu": int(mtu),
|
||||
"operstate": "UNKNOWN",
|
||||
"link_type": "unknown"
|
||||
}
|
||||
if "LOOPBACK" in flags:
|
||||
link["link_type"] = "loopback"
|
||||
link["address"] = "00:00:00:00:00:00"
|
||||
link["broadcast"] = "00:00:00:00:00:00"
|
||||
elif "POINTOPOINT" in flags:
|
||||
link["link_type"] = "none"
|
||||
count = count + 1
|
||||
else:
|
||||
if re.match(r"^\s+ether ", r):
|
||||
link["link_type"] = "ether"
|
||||
link["address"] = re.findall(r"(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)", r)[0]
|
||||
link["broadcast"] = "ff:ff:ff:ff:ff:ff"
|
||||
elif address and re.match(r"^\s+inet ", r) and af != 6:
|
||||
(local, netmask) = re.findall(r"inet (\d+\.\d+\.\d+\.\d+) netmask (0x[0-9a-f]+)", r)[0]
|
||||
addr = {
|
||||
"family": "inet",
|
||||
"local": local,
|
||||
"prefixlen": netmask_to_length(netmask),
|
||||
}
|
||||
if re.match(r"^.*broadcast", r):
|
||||
addr["broadcast"] = re.findall(r"broadcast (\d+\.\d+\.\d+\.\d+)", r)[0]
|
||||
link["addr_info"] = link.get("addr_info", []) + [addr]
|
||||
elif address and re.match(r"^\s+inet6 ", r) and af != 4:
|
||||
(local, prefixlen) = re.findall(r"inet6 ((?:[a-f0-9:]+:+)+[a-f0-9]+)%*\w* +prefixlen (\d+)", r)[0]
|
||||
link["addr_info"] = link.get("addr_info", []) + [{
|
||||
"family": "inet6",
|
||||
"local": local,
|
||||
"prefixlen": int(prefixlen)
|
||||
}]
|
||||
elif re.match(r"^\s+status: ", r):
|
||||
match re.findall(r"status: (\w+)", r)[0]:
|
||||
case "active":
|
||||
link["operstate"] = "UP"
|
||||
case "inactive":
|
||||
link["operstate"] = "DOWN"
|
||||
|
||||
if count > 1:
|
||||
links.append(link)
|
||||
|
||||
return links
|
||||
|
||||
|
||||
def link_addr_show(argv, af, json_print, pretty_json, address):
|
||||
if len(argv) > 0 and argv[0] == "dev":
|
||||
argv.pop(0)
|
||||
if len(argv) > 0:
|
||||
param = argv[0]
|
||||
else:
|
||||
param = "-a"
|
||||
|
||||
status, res = subprocess.getstatusoutput(
|
||||
IFCONFIG + " -v " + param + " 2>/dev/null"
|
||||
)
|
||||
if status: # unix status
|
||||
if res == "":
|
||||
perror(param + " not found")
|
||||
else:
|
||||
perror(res)
|
||||
return False
|
||||
|
||||
links = parse_ifconfig(res, af, address)
|
||||
|
||||
if json_print:
|
||||
return json_dump(links, pretty_json)
|
||||
|
||||
for l in links:
|
||||
print("%d: %s: <%s> mtu %d status %s" % (
|
||||
l["ifindex"], l["ifname"], ",".join(l["flags"]), l["mtu"], l["operstate"]
|
||||
))
|
||||
print(
|
||||
" link/" + l["link_type"] +
|
||||
((" " + l["address"]) if "address" in l else "") +
|
||||
((" brd " + l["broadcast"]) if "broadcast" in l else "")
|
||||
)
|
||||
for a in l.get("addr_info", []):
|
||||
print(
|
||||
" %s %s/%d" % (a["family"], a["local"], a["prefixlen"]) +
|
||||
((" brd " + a["broadcast"]) if "broadcast" in a else "")
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# Help
|
||||
def do_help(argv=None, af=None, json_print=None, pretty_json=None):
|
||||
perror("Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }")
|
||||
perror("where OBJECT := { link | addr | route | neigh }")
|
||||
perror(" OPTIONS := { -V[ersion] | -j[son] | -p[retty] |")
|
||||
perror(" -4 | -6 }")
|
||||
perror("iproute2mac")
|
||||
perror("Homepage: https://github.com/brona/iproute2mac")
|
||||
perror(
|
||||
"This is CLI wrapper for basic network utilities on Mac OS X"
|
||||
" inspired with iproute2 on Linux systems."
|
||||
)
|
||||
perror(
|
||||
"Provided functionality is limited and command output is not"
|
||||
" fully compatible with iproute2."
|
||||
)
|
||||
perror(
|
||||
"For advanced usage use netstat, ifconfig, ndp, arp, route "
|
||||
" and networksetup directly."
|
||||
)
|
||||
exit(255)
|
||||
|
||||
|
||||
def do_help_route():
|
||||
perror("Usage: ip route list")
|
||||
perror(" ip route get ADDRESS")
|
||||
perror(" ip route { add | del | replace } ROUTE")
|
||||
perror(" ip route flush cache")
|
||||
perror(" ip route flush table main")
|
||||
perror("ROUTE := NODE_SPEC [ INFO_SPEC ]")
|
||||
perror("NODE_SPEC := [ TYPE ] PREFIX")
|
||||
perror("INFO_SPEC := NH")
|
||||
perror("TYPE := { blackhole }")
|
||||
perror("NH := { via ADDRESS | gw ADDRESS | nexthop ADDRESS | dev STRING }")
|
||||
exit(255)
|
||||
|
||||
|
||||
def do_help_addr():
|
||||
perror("Usage: ip addr show [ dev STRING ]")
|
||||
perror(" ip addr { add | del } PREFIX dev STRING")
|
||||
exit(255)
|
||||
|
||||
|
||||
def do_help_link():
|
||||
perror("Usage: ip link show [ DEVICE ]")
|
||||
perror(" ip link set dev DEVICE")
|
||||
perror(" [ { up | down } ]")
|
||||
perror(" [ address { LLADDR | factory | random } ]")
|
||||
perror(" [ mtu MTU ]")
|
||||
exit(255)
|
||||
|
||||
|
||||
def do_help_neigh():
|
||||
perror("Usage: ip neighbour show [ [ to ] PREFIX ] [ dev DEV ]")
|
||||
perror(" ip neighbour flush [ dev DEV ]")
|
||||
exit(255)
|
||||
|
||||
|
||||
# Route Module
|
||||
@help_msg("do_help_route")
|
||||
def do_route(argv, af, json_print, pretty_json):
|
||||
if not argv or (
|
||||
any_startswith(["show", "lst", "list"], argv[0]) and len(argv) == 1
|
||||
):
|
||||
return do_route_list(af, json_print, pretty_json)
|
||||
elif "get".startswith(argv[0]) and len(argv) == 2:
|
||||
argv.pop(0)
|
||||
return do_route_get(argv, af, json_print, pretty_json)
|
||||
elif "add".startswith(argv[0]) and len(argv) >= 3:
|
||||
argv.pop(0)
|
||||
return do_route_add(argv, af)
|
||||
elif "delete".startswith(argv[0]) and len(argv) >= 2:
|
||||
argv.pop(0)
|
||||
return do_route_del(argv, af)
|
||||
elif "replace".startswith(argv[0]) and len(argv) >= 3:
|
||||
argv.pop(0)
|
||||
return do_route_del(argv, af) and do_route_add(argv, af)
|
||||
elif "flush".startswith(argv[0]) and len(argv) >= 1:
|
||||
argv.pop(0)
|
||||
return do_route_flush(argv, af)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def do_route_list(af, json_print, pretty_json):
|
||||
# ip route prints IPv6 or IPv4, never both
|
||||
inet = "inet6" if af == 6 else "inet"
|
||||
status, res = subprocess.getstatusoutput(
|
||||
NETSTAT + " -nr -f " + inet + " 2>/dev/null"
|
||||
)
|
||||
if status:
|
||||
perror(res)
|
||||
return False
|
||||
res = res.split("\n")
|
||||
res = res[4:] # Removes first 4 lines
|
||||
|
||||
routes = []
|
||||
|
||||
for r in res:
|
||||
ra = r.split()
|
||||
target = ra[0]
|
||||
gw = ra[1]
|
||||
flags = ra[2]
|
||||
# macOS Mojave and earlier vs Catalina
|
||||
dev = ra[5] if len(ra) >= 6 else ra[3]
|
||||
if flags.find("W") != -1:
|
||||
continue
|
||||
if af == 6:
|
||||
target = re.sub(r"%[^ ]+/", "/", target)
|
||||
else:
|
||||
target = cidr_from_netstat_dst(target)
|
||||
if flags.find("B") != -1:
|
||||
routes.append({"type": "blackhole", "dst": target, "flags": []})
|
||||
continue
|
||||
if re.match(r"link.+", gw):
|
||||
routes.append({"dst": target, "dev": dev, "scope": "link", "flags": []})
|
||||
else:
|
||||
routes.append({"dst": target, "gateway": gw, "dev": dev, "flags": []})
|
||||
|
||||
if json_print:
|
||||
return json_dump(routes, pretty_json)
|
||||
|
||||
for route in routes:
|
||||
if "type" in route:
|
||||
print("%s %s" % (route["type"], route["dst"]))
|
||||
elif "scope" in route:
|
||||
print("%s dev %s scope %s" % (route["dst"], route["dev"], route["scope"]))
|
||||
elif "gateway" in route:
|
||||
print("%s via %s dev %s" % (route["dst"], route["gateway"], route["dev"]))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def do_route_add(argv, af):
|
||||
options = ""
|
||||
if argv[0] == "blackhole":
|
||||
argv.pop(0)
|
||||
if len(argv) != 1:
|
||||
return False
|
||||
argv.append("via")
|
||||
argv.append("::1" if ":" in argv[0] or af == 6 else "127.0.0.1")
|
||||
options = "-blackhole"
|
||||
|
||||
if len(argv) not in (3, 5):
|
||||
return False
|
||||
|
||||
if len(argv) == 5:
|
||||
perror(
|
||||
"iproute2mac: Ignoring last 2 arguments, not implemented: {} {}".format(
|
||||
argv[3], argv[4]
|
||||
)
|
||||
)
|
||||
|
||||
if argv[1] in ["via", "nexthop", "gw"]:
|
||||
gw = argv[2]
|
||||
elif argv[1] in ["dev"]:
|
||||
gw = "-interface " + argv[2]
|
||||
else:
|
||||
do_help_route()
|
||||
|
||||
prefix = argv[0]
|
||||
inet = "-inet6 " if ":" in prefix or af == 6 else ""
|
||||
|
||||
return execute_cmd(
|
||||
SUDO + " " + ROUTE + " add " + inet + prefix + " " + gw + " " + options
|
||||
)
|
||||
|
||||
|
||||
def do_route_del(argv, af):
|
||||
options = ""
|
||||
if argv[0] == "blackhole":
|
||||
argv.pop(0)
|
||||
if len(argv) != 1:
|
||||
return False
|
||||
if ":" in argv[0] or af == 6:
|
||||
options = " ::1 -blackhole"
|
||||
else:
|
||||
options = " 127.0.0.1 -blackhole"
|
||||
|
||||
prefix = argv[0]
|
||||
inet = "-inet6 " if ":" in prefix or af == 6 else ""
|
||||
return execute_cmd(
|
||||
SUDO + " " + ROUTE + " delete " + inet + prefix + options
|
||||
)
|
||||
|
||||
|
||||
def do_route_flush(argv, af):
|
||||
if not argv:
|
||||
perror('"ip route flush" requires arguments.')
|
||||
perror("")
|
||||
return False
|
||||
|
||||
# https://github.com/brona/iproute2mac/issues/38
|
||||
# http://linux-ip.net/html/tools-ip-route.html
|
||||
if argv[0] == "cache":
|
||||
print("iproute2mac: There is no route cache to flush in MacOS,")
|
||||
print(" returning 0 status code for compatibility.")
|
||||
return True
|
||||
elif len(argv) == 2 and argv[0] == "table" and argv[1] == "main":
|
||||
family = "-inet6" if af == 6 else "-inet"
|
||||
print("iproute2mac: Flushing all routes")
|
||||
return execute_cmd(SUDO + " " + ROUTE + " -n flush " + family)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def do_route_get(argv, af, json_print, pretty_json):
|
||||
target = argv[0]
|
||||
|
||||
inet = ""
|
||||
if ":" in target or af == 6:
|
||||
inet = "-inet6 "
|
||||
family = socket.AF_INET6
|
||||
else:
|
||||
family = socket.AF_INET
|
||||
|
||||
status, res = subprocess.getstatusoutput(
|
||||
ROUTE + " -n get " + inet + target
|
||||
)
|
||||
if status: # unix status or not in table
|
||||
perror(res)
|
||||
return False
|
||||
if res.find("not in table") >= 0:
|
||||
perror(res)
|
||||
exit(1)
|
||||
|
||||
res = dict(
|
||||
re.findall(
|
||||
r"^\W*((?:route to|destination|gateway|interface)): (.+)$",
|
||||
res,
|
||||
re.MULTILINE,
|
||||
)
|
||||
)
|
||||
|
||||
route = {"dst": res["route to"], "dev": res["interface"]}
|
||||
|
||||
if "gateway" in res:
|
||||
route["gateway"] = res["gateway"]
|
||||
|
||||
try:
|
||||
s = socket.socket(family, socket.SOCK_DGRAM)
|
||||
s.connect((route["dst"], 7))
|
||||
route["prefsrc"] = src_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
route["flags"] = []
|
||||
route["uid"] = os.getuid()
|
||||
route["cache"] = []
|
||||
|
||||
if json_print:
|
||||
return json_dump([route], pretty_json)
|
||||
|
||||
print(
|
||||
route["dst"] +
|
||||
((" via " + route["gateway"]) if "gateway" in route else "") +
|
||||
" dev " + route["dev"] +
|
||||
((" src " + route["prefsrc"]) if "prefsrc" in route else "") +
|
||||
" uid " + str(route["uid"])
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# Addr Module
|
||||
@help_msg("do_help_addr")
|
||||
def do_addr(argv, af, json_print, pretty_json):
|
||||
if not argv:
|
||||
argv.append("show")
|
||||
|
||||
if any_startswith(["show", "lst", "list"], argv[0]):
|
||||
argv.pop(0)
|
||||
return do_addr_show(argv, af, json_print, pretty_json)
|
||||
elif "add".startswith(argv[0]) and len(argv) >= 3:
|
||||
argv.pop(0)
|
||||
return do_addr_add(argv, af)
|
||||
elif "delete".startswith(argv[0]) and len(argv) >= 3:
|
||||
argv.pop(0)
|
||||
return do_addr_del(argv, af)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def do_addr_show(argv, af, json_print, pretty_json):
|
||||
return link_addr_show(argv, af, json_print, pretty_json, True)
|
||||
|
||||
|
||||
def do_addr_add(argv, af):
|
||||
if len(argv) < 2:
|
||||
return False
|
||||
|
||||
dst = ""
|
||||
if argv[1] == "peer":
|
||||
argv.pop(1)
|
||||
dst = argv.pop(1)
|
||||
|
||||
if argv[1] == "dev":
|
||||
argv.pop(1)
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
addr = argv[0]
|
||||
dev = argv[1]
|
||||
except IndexError:
|
||||
perror("dev not found")
|
||||
exit(1)
|
||||
inet = ""
|
||||
if ":" in addr or af == 6:
|
||||
af = 6
|
||||
inet = " inet6"
|
||||
return execute_cmd(
|
||||
SUDO + " " + IFCONFIG + " " + dev + inet + " add " + addr + " " + dst
|
||||
)
|
||||
|
||||
|
||||
def do_addr_del(argv, af):
|
||||
if len(argv) < 2:
|
||||
return False
|
||||
if argv[1] == "dev":
|
||||
argv.pop(1)
|
||||
try:
|
||||
addr = argv[0]
|
||||
dev = argv[1]
|
||||
except IndexError:
|
||||
perror("dev not found")
|
||||
exit(1)
|
||||
inet = "inet"
|
||||
if ":" in addr or af == 6:
|
||||
af = 6
|
||||
inet = "inet6"
|
||||
return execute_cmd(
|
||||
SUDO + " " + IFCONFIG + " " + dev + " " + inet + " " + addr + " remove"
|
||||
)
|
||||
|
||||
|
||||
# Link module
|
||||
@help_msg("do_help_link")
|
||||
def do_link(argv, af, json_print, pretty_json):
|
||||
if not argv:
|
||||
argv.append("show")
|
||||
|
||||
if any_startswith(["show", "lst", "list"], argv[0]):
|
||||
argv.pop(0)
|
||||
return do_link_show(argv, af, json_print, pretty_json)
|
||||
elif "set".startswith(argv[0]):
|
||||
argv.pop(0)
|
||||
return do_link_set(argv, af)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def do_link_show(argv, af, json_print, pretty_json):
|
||||
return link_addr_show(argv, af, json_print, pretty_json, False)
|
||||
|
||||
|
||||
def do_link_set(argv, af):
|
||||
if not argv:
|
||||
return False
|
||||
elif argv[0] == "dev":
|
||||
argv.pop(0)
|
||||
|
||||
if len(argv) < 2:
|
||||
return False
|
||||
|
||||
dev = argv[0]
|
||||
|
||||
IFCONFIG_DEV_CMD = SUDO + " " + IFCONFIG + " " + dev
|
||||
try:
|
||||
args = iter(argv)
|
||||
for arg in args:
|
||||
if arg == "up":
|
||||
if not execute_cmd(IFCONFIG_DEV_CMD + " up"):
|
||||
return False
|
||||
elif arg == "down":
|
||||
if not execute_cmd(IFCONFIG_DEV_CMD + " down"):
|
||||
return False
|
||||
elif arg in ["address", "addr", "lladdr"]:
|
||||
addr = next(args)
|
||||
if addr in ["random", "rand"]:
|
||||
addr = randomMAC()
|
||||
elif addr == "factory":
|
||||
(status, res) = subprocess.getstatusoutput(
|
||||
NETWORKSETUP + " -listallhardwareports"
|
||||
)
|
||||
if status != 0:
|
||||
return False
|
||||
details = re.findall(
|
||||
r"^(?:Device|Ethernet Address): (.+)$",
|
||||
res,
|
||||
re.MULTILINE,
|
||||
)
|
||||
addr = details[details.index(dev) + 1]
|
||||
if not execute_cmd(IFCONFIG_DEV_CMD + " lladdr " + addr):
|
||||
return False
|
||||
elif arg == "mtu":
|
||||
mtu = int(next(args))
|
||||
if not execute_cmd(IFCONFIG_DEV_CMD + " mtu " + str(mtu)):
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Neigh module
|
||||
@help_msg("do_help_neigh")
|
||||
def do_neigh(argv, af, json_print, pretty_json):
|
||||
if not argv:
|
||||
argv.append("show")
|
||||
|
||||
if any_startswith(["show", "list", "lst"], argv[0]) and len(argv) <= 5:
|
||||
argv.pop(0)
|
||||
return do_neigh_show(argv, af, json_print, pretty_json)
|
||||
elif "flush".startswith(argv[0]):
|
||||
argv.pop(0)
|
||||
return do_neigh_flush(argv, af)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def do_neigh_show(argv, af, json_print, pretty_json):
|
||||
prefix = None
|
||||
dev = None
|
||||
try:
|
||||
while argv:
|
||||
arg = argv.pop(0)
|
||||
if arg == "to":
|
||||
prefix = argv.pop(0)
|
||||
elif arg == "dev":
|
||||
dev = argv.pop(0)
|
||||
elif prefix is None:
|
||||
prefix = arg
|
||||
else:
|
||||
return False
|
||||
if prefix:
|
||||
prefix = ipaddress.ip_network(prefix, strict=False)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
nd_ll_states = {
|
||||
"R": "REACHABLE",
|
||||
"S": "STALE",
|
||||
"D": "DELAY",
|
||||
"P": "PROBE",
|
||||
"I": "INCOMPLETE",
|
||||
"N": "INCOMPLETE",
|
||||
"W": "INCOMPLETE",
|
||||
}
|
||||
|
||||
neighs = []
|
||||
|
||||
if af != 4:
|
||||
res = subprocess.run(
|
||||
[NDP, "-an"], capture_output=True, text=True, check=True
|
||||
)
|
||||
for row in res.stdout.splitlines()[1:]:
|
||||
cols = row.split()
|
||||
entry = {"dst": re.sub(r"%.+$", "", cols[0])}
|
||||
if cols[1] != "(incomplete)":
|
||||
entry["lladdr"] = cols[1]
|
||||
entry["dev"] = cols[2]
|
||||
if dev and entry["dev"] != dev:
|
||||
continue
|
||||
if prefix and ipaddress.ip_address(entry["dst"]) not in prefix:
|
||||
continue
|
||||
if cols[1] == "(incomplete)" and cols[4] != "R":
|
||||
entry["status"] = ["INCOMPLETE"]
|
||||
else:
|
||||
entry["status"] = [nd_ll_states[cols[4]]]
|
||||
entry["router"] = len(cols) >= 6 and cols[5] == "R"
|
||||
neighs.append(entry)
|
||||
|
||||
if af != 6:
|
||||
args = [ARP, "-anl"]
|
||||
if dev:
|
||||
args += ["-i", dev]
|
||||
|
||||
res = subprocess.run(args, capture_output=True, text=True, check=True)
|
||||
for row in res.stdout.splitlines()[1:]:
|
||||
cols = row.split()
|
||||
entry = {"dst": cols[0]}
|
||||
if cols[1] != "(incomplete)":
|
||||
entry["lladdr"] = cols[1]
|
||||
entry["dev"] = cols[4]
|
||||
if dev and entry["dev"] != dev:
|
||||
continue
|
||||
if prefix and ipaddress.ip_address(entry["dst"]) not in prefix:
|
||||
continue
|
||||
if cols[1] == "(incomplete)":
|
||||
entry["status"] = ["INCOMPLETE"]
|
||||
else:
|
||||
entry["status"] = ["REACHABLE"]
|
||||
entry["router"] = False
|
||||
neighs.append(entry)
|
||||
|
||||
if json_print:
|
||||
return json_dump(neighs, pretty_json)
|
||||
|
||||
for nb in neighs:
|
||||
print(
|
||||
nb["dst"] +
|
||||
("", " dev " + nb["dev"], "")[dev == None] +
|
||||
("", " router")[nb["router"]] +
|
||||
" %s" % (nb["status"][0])
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def do_neigh_flush(argv, af):
|
||||
if len(argv) != 2:
|
||||
perror("Flush requires arguments.")
|
||||
exit(1)
|
||||
|
||||
if argv[0] != "dev":
|
||||
return False
|
||||
dev = argv[1]
|
||||
|
||||
if af != 4:
|
||||
print(
|
||||
"iproute2mac: NDP doesn't support filtering by interface,"
|
||||
"flushing all IPv6 entries."
|
||||
)
|
||||
execute_cmd(SUDO + " " + NDP + " -cn")
|
||||
if af != 6:
|
||||
execute_cmd(SUDO + " " + ARP + " -a -d -i " + dev)
|
||||
return True
|
||||
|
||||
|
||||
# Match iproute2 commands
|
||||
# https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ip.c#n86
|
||||
cmds = [
|
||||
("address", do_addr),
|
||||
("route", do_route),
|
||||
("neighbor", do_neigh),
|
||||
("neighbour", do_neigh),
|
||||
("link", do_link),
|
||||
("help", do_help),
|
||||
]
|
||||
|
||||
|
||||
@help_msg("do_help")
|
||||
def main(argv):
|
||||
af = -1 # default / both
|
||||
json_print = False
|
||||
pretty_json = False
|
||||
|
||||
while argv and argv[0].startswith("-"):
|
||||
# Turn --opt into -opt
|
||||
argv[0] = argv[0][1:] if argv[0][1] == "-" else argv[0]
|
||||
# Process options
|
||||
if argv[0] == "-6":
|
||||
af = 6
|
||||
argv.pop(0)
|
||||
elif argv[0] == "-4":
|
||||
af = 4
|
||||
argv.pop(0)
|
||||
elif argv[0].startswith("-color"):
|
||||
perror("iproute2mac: Color option is not implemented")
|
||||
argv.pop(0)
|
||||
elif "-json".startswith(argv[0]):
|
||||
json_print = True
|
||||
argv.pop(0)
|
||||
elif "-pretty".startswith(argv[0]):
|
||||
pretty_json = True
|
||||
argv.pop(0)
|
||||
elif "-Version".startswith(argv[0]):
|
||||
print("iproute2mac, v" + VERSION)
|
||||
exit(0)
|
||||
elif "-help".startswith(argv[0]):
|
||||
return False
|
||||
else:
|
||||
perror('Option "{}" is unknown, try "ip help".'.format(argv[0]))
|
||||
exit(255)
|
||||
|
||||
if not argv:
|
||||
return False
|
||||
|
||||
for cmd, cmd_func in cmds:
|
||||
if cmd.startswith(argv[0]):
|
||||
argv.pop(0)
|
||||
# Functions return true or terminate with exit(255)
|
||||
# See help_msg and do_help*
|
||||
return cmd_func(argv, af, json_print, pretty_json)
|
||||
|
||||
perror('Object "{}" is unknown, try "ip help".'.format(argv[0]))
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
|
@ -3,4 +3,4 @@
|
|||
FOLDER=/Applications/OGAgent.app
|
||||
|
||||
cd $FOLDER
|
||||
python -m opengnsys.linux.OGAgentService $@
|
||||
/usr/local/bin/python3 -m opengnsys.linux.OGAgentService $@
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Directories
|
||||
SRCDIR=$(dirname "$0")
|
||||
BINDIR=/usr/bin
|
||||
INITDIR=/Library/LaunchDaemons
|
||||
BINDIR=/usr/local/bin
|
||||
LAUNCH_AGENTS_DIR=/Library/LaunchAgents
|
||||
LAUNCH_DAEMONS_DIR=/Library/LaunchDaemons
|
||||
|
||||
# Check if it needs to install Python dependencies:
|
||||
if ! which pip &>/dev/null; then
|
||||
easy_install pip
|
||||
pip install netifaces requests six
|
||||
fi
|
||||
|
||||
# Copying files.
|
||||
cp $SRCDIR/ogagent $BINDIR
|
||||
cp $SRCDIR/es.opengnsys.ogagent.plist $INITDIR
|
||||
|
||||
cp $SRCDIR/ip.py $BINDIR/ip ## override 'ip' from iproute2mac-1.4.2 with a more recent one
|
||||
cp $SRCDIR/es.opengnsys.agent.system.plist $LAUNCH_DAEMONS_DIR
|
||||
cp $SRCDIR/es.opengnsys.agent.user.plist $LAUNCH_AGENTS_DIR
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
from flask import Flask, request, jsonify, render_template_string, abort
|
||||
import os
|
||||
import logging
|
||||
import json
|
||||
import subprocess
|
||||
import base64
|
||||
|
||||
## FLASK_APP=/path/to/ogcore-mock.py FLASK_ENV=development FLASK_RUN_CERT=adhoc sudo --preserve-env flask run --host 192.168.1.249 --port 443
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
## agente lin/win/mac
|
||||
|
||||
@app.route('/opengnsys/rest/ogagent/<cucu>', methods=['POST'])
|
||||
def og_agent(cucu):
|
||||
logging.info(f'{request.get_json()}')
|
||||
return jsonify({})
|
||||
|
||||
|
||||
|
||||
## agente oglive: modulo ogAdmClient
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/InclusionCliente', methods=['POST'])
|
||||
def inclusion_cliente():
|
||||
logging.info(f'{request.get_json()}')
|
||||
#procesoInclusionCliente() or { return (jsonify { 'res': 0 }) }
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph'] ## Toma ip
|
||||
cfg = j['cfg'] ## Toma configuracion
|
||||
logging.info(f'iph ({iph}) cfg ({cfg})')
|
||||
|
||||
# dbi->query (sprintf "SELECT ordenadores.*,aulas.idaula,centros.idcentro FROM ordenadores INNER JOIN aulas ON aulas.idaula=ordenadores.idaula INNER JOIN centros ON centros.idcentro=aulas.idcentro WHERE ordenadores.ip = '%s'", iph);
|
||||
# if (!dbi_result_next_row(result)) { log_error ('client does not exist in database') }
|
||||
# log_debug (sprintf 'Client %s requesting inclusion', iph);
|
||||
|
||||
idordenador = 42 #dbi_result_get_uint(result, "idordenador")
|
||||
nombreordenador = 'hal9000' #dbi_result_get_string(result, "nombreordenador");
|
||||
cache = 42 #dbi_result_get_uint(result, "cache");
|
||||
idproautoexec = 42 #dbi_result_get_uint(result, "idproautoexec");
|
||||
idaula = 42 #dbi_result_get_uint(result, "idaula");
|
||||
idcentro = 42 #dbi_result_get_uint(result, "idcentro");
|
||||
|
||||
# resul = actualizaConfiguracion(dbi, cfg, idordenador); ## Actualiza la configuración del ordenador
|
||||
# if (!resul) { log_error ('Cannot add client to database') }
|
||||
# if (!registraCliente(iph)) { log_error ('client table is full') } ## Incluyendo al cliente en la tabla de sokets
|
||||
|
||||
return jsonify({
|
||||
'res': 1, ## int, Confirmación proceso correcto
|
||||
'ido': idordenador, ## int
|
||||
'npc': nombreordenador, ## string
|
||||
'che': cache, ## int
|
||||
'exe': idproautoexec, ## int
|
||||
'ida': idaula, ## int
|
||||
'idc': idcentro ## int
|
||||
})
|
||||
|
||||
def _recorreProcedimientos(parametros, fileexe, idp):
|
||||
#char idprocedimiento[LONPRM];
|
||||
#int procedimientoid, lsize;
|
||||
#const char *msglog, *param;
|
||||
#dbi_result result;
|
||||
|
||||
#dbi->query (sprintf 'SELECT procedimientoid,parametros FROM procedimientos_acciones WHERE idprocedimiento=%s ORDER BY orden', $idp);
|
||||
#if (!result) { log_error ('failed to query database'); return 0; }
|
||||
if 1: #while (dbi_result_next_row(result)) {
|
||||
procedimientoid = 0 ## dbi_result_get_uint(result, "procedimientoid");
|
||||
if (procedimientoid > 0): ## Procedimiento recursivo
|
||||
if (not _recorreProcedimientos (parametros, fileexe, procedimientoid)):
|
||||
return 0
|
||||
else:
|
||||
#param = '@'.join (["nfn=EjecutarScript\rscp=uptime", "nfn=EjecutarScript\rscp=cat /proc/uptime"]) ## dbi_result_get_string(result, "parametros");
|
||||
param = '@'.join (["nfn=popup\rtitle=my title\rmessage=my message"])
|
||||
parametros = '{}@'.format (param)
|
||||
fileexe.write (parametros)
|
||||
#}
|
||||
|
||||
return 1
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/AutoexecCliente', methods=['POST'])
|
||||
def autoexec_client():
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph'] ## Toma dirección IP del cliente
|
||||
exe = j['exe'] ## Toma identificador del procedimiento inicial
|
||||
logging.info(f'iph ({iph}) exe ({exe})')
|
||||
|
||||
fileautoexec = '/tmp/Sautoexec-{}'.format(iph)
|
||||
logging.info ('fileautoexec ({})'.format (fileautoexec));
|
||||
try:
|
||||
fileexe = open (fileautoexec, 'w')
|
||||
except Exception as e:
|
||||
logging.error ('cannot create temporary file: {}'.format (e))
|
||||
return jsonify({})
|
||||
|
||||
if (_recorreProcedimientos ('', fileexe, exe)):
|
||||
res = jsonify ({ 'res': 1, 'nfl': fileautoexec })
|
||||
else:
|
||||
res = jsonify ({ 'res': 0 })
|
||||
|
||||
fileexe.close()
|
||||
return res
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/enviaArchivo', methods=['POST'])
|
||||
def envia_archivo():
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
nfl = j['nfl'] ## Toma nombre completo del archivo
|
||||
logging.info(f'nfl ({nfl})')
|
||||
|
||||
contents = subprocess.run (['cat', nfl], capture_output=True).stdout
|
||||
b64 = base64.b64encode (contents).decode ('utf-8')
|
||||
return jsonify({'contents': b64})
|
||||
|
||||
def clienteExistente(iph):
|
||||
## esto queda totalmente del lado del servidor, no lo implemento en python
|
||||
return 42
|
||||
|
||||
def buscaComandos(ido):
|
||||
#dbi->query (sprintf "SELECT sesion, parametros FROM acciones WHERE idordenador=%s AND estado='%d' ORDER BY idaccion", ido, ACCION_INICIADA);
|
||||
#dbi_result_next_row(result) ## cogemos solo una fila
|
||||
|
||||
#if not row { return; }
|
||||
return
|
||||
|
||||
#ids = 42 #dbi_result_get_uint(result, "sesion");
|
||||
#param = "nfn=popup\rtitle=my title\rmessage=my message" #dbi_result_get_string(result, "parametros");
|
||||
## convertirlo a json, aqui lo pongo a capon
|
||||
#return jsonify ({ 'nfn': 'popup', 'title': 'my title', 'message': 'my message', 'ids': ids })
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/ComandosPendientes', methods=['POST'])
|
||||
def comandos_pendientes():
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph'] ## Toma dirección IP
|
||||
ido = j['ido'] ## Toma identificador del ordenador
|
||||
logging.info(f'iph ({iph}) ido ({ido})')
|
||||
|
||||
idx = clienteExistente(iph) ## Busca índice del cliente
|
||||
if not idx:
|
||||
## que devuelvo?? pongamos un 404...
|
||||
abort(404, 'Client does not exist')
|
||||
|
||||
param = buscaComandos(ido) ## Existen comandos pendientes, buscamos solo uno
|
||||
if param is None:
|
||||
return jsonify({'nfn': 'NoComandosPtes'})
|
||||
|
||||
#strcpy(tbsockets[idx].estado, CLIENTE_OCUPADO); ## esto queda totalmente del lado del servidor, no lo implemento en python
|
||||
|
||||
return jsonify(param)
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/DisponibilidadComandos', methods=['POST'])
|
||||
def disponibilidad_comandos():
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
iph = j['iph']
|
||||
tpc = j['tpc']
|
||||
logging.info(f'iph ({iph}) tpc ({tpc})')
|
||||
|
||||
idx = clienteExistente(iph) ## Busca índice del cliente
|
||||
if not idx:
|
||||
## que devuelvo?? pongamos un 404...
|
||||
abort(404, 'Client does not exist')
|
||||
|
||||
#strcpy(tbsockets[idx].estado, tpc); ## esto queda totalmente del lado del servidor, no lo implemento en python
|
||||
|
||||
return jsonify({})
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/recibeArchivo', methods=['POST'])
|
||||
def oac_recibe_archivo():
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
nfl = j['nfl']
|
||||
contents = j['contents']
|
||||
logging.info(f'nfl ({nfl}) contents ({contents})')
|
||||
dec = base64.b64decode (contents).decode ('utf-8')
|
||||
logging.info(f'dec ({dec})')
|
||||
return jsonify({'anything':'anything'}) ## if we return {}, then we trigger "if not {}" which happens to be true
|
||||
|
||||
@app.route('/opengnsys/rest/clients/status/webhook', methods=['POST'])
|
||||
def oac_callback():
|
||||
logging.info(f'{request.get_json()}')
|
||||
return jsonify({'anything':'anything'})
|
||||
|
||||
@app.route('/opengnsys/rest/ogAdmClient/<cucu>', methods=['GET', 'POST'])
|
||||
def oac_cucu(cucu):
|
||||
#j = request.get_json(force=True)
|
||||
#logging.info(f'{request.get_json()} {j}')
|
||||
#if 'cucu' not in j:
|
||||
# abort(400, 'missing parameter 'cucu'')
|
||||
#return jsonify({'cucu': j['cucu']})
|
||||
abort (404)
|
||||
|
||||
|
||||
|
||||
## agente oglive: modulo CloningEngine
|
||||
|
||||
@app.route('/opengnsys/rest/CloningEngine/recibeArchivo', methods=['POST'])
|
||||
def ce_recibe_archivo():
|
||||
logging.info(f'{request.get_json()}')
|
||||
j = request.get_json(force=True)
|
||||
nfl = j['nfl']
|
||||
contents = j['contents']
|
||||
logging.info(f'nfl ({nfl}) contents ({contents})')
|
||||
dec = base64.b64decode (contents).decode ('utf-8')
|
||||
logging.info(f'dec ({dec})')
|
||||
return jsonify({'anything':'anything'}) ## if we return {}, then we trigger "if not {}" which happens to be true
|
||||
|
||||
@app.route('/opengnsys/rest/CloningEngine/callback', methods=['POST'])
|
||||
def ce_callback():
|
||||
logging.info(f'{request.get_json()}')
|
||||
return jsonify({'anything':'anything'})
|
||||
|
||||
@app.route('/opengnsys/rest/CloningEngine/<cucu>', methods=['GET', 'POST'])
|
||||
def ce_cucu(cucu):
|
||||
abort (404)
|
||||
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
def _page_not_found(e):
|
||||
if type(e.description) is dict:
|
||||
return jsonify (e.description), e.code
|
||||
else:
|
||||
return render_template_string('''<!DOCTYPE html><html>not found</html>'''), e.code
|
||||
|
||||
@app.errorhandler(500)
|
||||
def _internal_server_error(e):
|
||||
return render_template_string('''<!DOCTYPE html><html>err</html>'''), e.code
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def _exception(e):
|
||||
print(e)
|
||||
return render_template_string('''<!DOCTYPE html><html>exception</html>'''), e.code
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host = '192.168.1.249', port = 443, debug=True)
|
|
@ -0,0 +1,297 @@
|
|||
openapi: 3.0.3
|
||||
|
||||
info:
|
||||
title: OgAgent API
|
||||
description: OgAgent API
|
||||
version: 0.0.1
|
||||
|
||||
paths:
|
||||
/opengnsys/status:
|
||||
post:
|
||||
summary: Get status of the agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/StatusReq'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/StatusRes'
|
||||
|
||||
/opengnsys/poweroff:
|
||||
post:
|
||||
summary: Power agent off
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LaunchedRes'
|
||||
|
||||
/opengnsys/reboot:
|
||||
post:
|
||||
summary: Reboot agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LaunchedRes'
|
||||
|
||||
/opengnsys/script:
|
||||
post:
|
||||
summary: Run script on agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ScriptReq'
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ScriptRes'
|
||||
|
||||
/opengnsys/terminatescript:
|
||||
post:
|
||||
summary: Terminate running script on agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TerminateScriptReq'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
|
||||
/opengnsys/preparescripts:
|
||||
post:
|
||||
summary: Prepare list of scripts running on agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
|
||||
/opengnsys/getscripts:
|
||||
post:
|
||||
summary: Get the list of scripts running on agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GetScriptsRes'
|
||||
|
||||
/opengnsys/logoff:
|
||||
post:
|
||||
summary: Log remote user off
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmptyObj'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LogoffRes'
|
||||
|
||||
/opengnsys/popup:
|
||||
post:
|
||||
summary: Show message on agent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Popup'
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LaunchedRes'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
EmptyObj:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
Jobid:
|
||||
type: string
|
||||
example:
|
||||
- "deadbeef"
|
||||
|
||||
StatusReq:
|
||||
type: object
|
||||
properties:
|
||||
detail:
|
||||
type: boolean
|
||||
|
||||
StatusRes:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- "LNX"
|
||||
- "OSX"
|
||||
- "WIN"
|
||||
loggedin:
|
||||
type: boolean
|
||||
session:
|
||||
type: string
|
||||
example:
|
||||
- "x11"
|
||||
agent_version:
|
||||
type: string
|
||||
os_version:
|
||||
type: string
|
||||
sys_load:
|
||||
type: number
|
||||
|
||||
LaunchedRes:
|
||||
type: object
|
||||
required:
|
||||
- op
|
||||
properties:
|
||||
op:
|
||||
type: string
|
||||
enum:
|
||||
- "launched"
|
||||
|
||||
ScriptReq:
|
||||
type: object
|
||||
required:
|
||||
- script
|
||||
properties:
|
||||
script:
|
||||
type: string
|
||||
example:
|
||||
- "uptime\nwho"
|
||||
- "Start-Process notepad.exe"
|
||||
client:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
ScriptRes:
|
||||
type: object
|
||||
required:
|
||||
- op
|
||||
properties:
|
||||
op:
|
||||
type: string
|
||||
enum:
|
||||
- "launched"
|
||||
jobid:
|
||||
$ref: '#/components/schemas/Jobid'
|
||||
|
||||
TerminateScriptReq:
|
||||
type: object
|
||||
required:
|
||||
- jobid
|
||||
properties:
|
||||
jobid:
|
||||
$ref: '#/components/schemas/Jobid'
|
||||
|
||||
|
||||
RunningScript:
|
||||
type: object
|
||||
required:
|
||||
- jobid
|
||||
- pid
|
||||
- starttime
|
||||
- script
|
||||
- client
|
||||
- status
|
||||
- stdout
|
||||
- stderr
|
||||
properties:
|
||||
jobid:
|
||||
$ref: '#/components/schemas/Jobid'
|
||||
pid:
|
||||
type: integer
|
||||
starttime:
|
||||
type: string
|
||||
example: "2024-12-31 23:59:59.123456+0000"
|
||||
script:
|
||||
type: string
|
||||
client:
|
||||
type: boolean
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- "running"
|
||||
- "finished"
|
||||
stdout:
|
||||
type: string
|
||||
stderr:
|
||||
type: string
|
||||
rc:
|
||||
type: integer
|
||||
|
||||
GetScriptsRes:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/RunningScript'
|
||||
|
||||
LogoffRes:
|
||||
type: object
|
||||
required:
|
||||
- op
|
||||
properties:
|
||||
op:
|
||||
type: string
|
||||
enum:
|
||||
- "sent to client"
|
||||
|
||||
Popup:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
|
@ -1,3 +0,0 @@
|
|||
six
|
||||
requests
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import win32service
|
||||
import win32serviceutil
|
||||
|
||||
svc_name = "UDSActor"
|
||||
|
||||
try:
|
||||
hscm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_ALL_ACCESS)
|
||||
|
||||
try:
|
||||
hs = win32serviceutil.SmartOpenService(hscm, svc_name, win32service.SERVICE_ALL_ACCESS)
|
||||
service_failure_actions = {
|
||||
'ResetPeriod': 864000, # Time in ms after which to reset the failure count to zero.
|
||||
'RebootMsg': u'', # Not using reboot option
|
||||
'Command': u'', # Not using run-command option
|
||||
'Actions': [
|
||||
(win32service.SC_ACTION_RESTART, 5000), # action, delay in ms
|
||||
(win32service.SC_ACTION_RESTART, 5000)
|
||||
]
|
||||
}
|
||||
win32service.ChangeServiceConfig2(hs, win32service.SERVICE_CONFIG_FAILURE_ACTIONS, service_failure_actions)
|
||||
finally:
|
||||
win32service.CloseServiceHandle(hs)
|
||||
finally:
|
||||
win32service.CloseServiceHandle(hscm)
|
|
@ -1,5 +0,0 @@
|
|||
<RCC>
|
||||
<qresource prefix="images">
|
||||
<file>img/oga.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,102 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
## generated on windows using:
|
||||
## pyi-makespec.exe --windowed --icon img\oga.ico --manifest OGAgent.manifest OGAgentUser.py opengnsys\windows\OGAgentService.py
|
||||
## move OGAgentUser.spec OGAgent.spec
|
||||
|
||||
|
||||
ogausr_a = Analysis(
|
||||
['OGAgentUser.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[
|
||||
# ('cfg', 'cfg'), ## add the entire directory
|
||||
('img', 'img'), ## add the entire directory
|
||||
],
|
||||
hiddenimports=['win32timezone', 'socketserver', 'http.server', 'urllib', 'opengnsys.modules.client.OpenGnSys', 'opengnsys.modules.server.ogAdmClient', 'opengnsys.modules.server.OpenGnSys'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
ogasvc_a = Analysis(
|
||||
['opengnsys\\windows\\OGAgentService.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=['win32timezone', 'socketserver', 'http.server', 'urllib', 'opengnsys.modules.client.OpenGnSys', 'opengnsys.modules.server.ogAdmClient', 'opengnsys.modules.server.OpenGnSys'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
|
||||
MERGE(
|
||||
(ogausr_a, 'OGAgentUser', 'OGAgentUser'), ## class, py name, exe name
|
||||
(ogasvc_a, 'OGAgentService', 'OGAgentService')
|
||||
)
|
||||
|
||||
ogausr_pyz = PYZ(ogausr_a.pure)
|
||||
ogasvc_pyz = PYZ(ogasvc_a.pure)
|
||||
|
||||
ogausr_exe = EXE(
|
||||
ogausr_pyz,
|
||||
ogausr_a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='OGAgentUser',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['img\\oga.ico'],
|
||||
)
|
||||
ogasvc_exe = EXE(
|
||||
ogasvc_pyz,
|
||||
ogasvc_a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='OGAgentService',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['img\\oga.ico'],
|
||||
manifest='OGAgent.manifest',
|
||||
)
|
||||
|
||||
dist_name = 'OGAgent'
|
||||
coll = COLLECT(
|
||||
ogausr_exe,
|
||||
ogausr_a.binaries,
|
||||
ogausr_a.datas,
|
||||
|
||||
ogasvc_exe,
|
||||
ogasvc_a.binaries,
|
||||
ogasvc_a.datas,
|
||||
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name=dist_name,
|
||||
)
|
||||
|
||||
import shutil
|
||||
shutil.copytree ('cfg', '{}/{}/cfg'. format(DISTPATH, dist_name))
|
||||
shutil.copy ('stop-agent.ps1', '{}/{}/stop-agent.ps1'.format(DISTPATH, dist_name))
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
|
@ -29,28 +29,22 @@
|
|||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import atexit
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import six
|
||||
import atexit
|
||||
from PyQt4 import QtCore, QtGui # @UnresolvedImport
|
||||
import os
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from opengnsys import VERSION, ipc, operations, utils
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.service import IPC_PORT
|
||||
from about_dialog_ui import Ui_OGAAboutDialog
|
||||
from message_dialog_ui import Ui_OGAMessageDialog
|
||||
from opengnsys.scriptThread import ScriptExecutorThread
|
||||
from opengnsys import VERSION, ipc, operations, utils
|
||||
from opengnsys.config import readConfig
|
||||
from opengnsys.loader import loadModules
|
||||
|
||||
# Set default characters encoding to UTF-8
|
||||
reload(sys)
|
||||
if hasattr(sys, 'setdefaultencoding'):
|
||||
sys.setdefaultencoding('utf-8')
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.jobmgr import JobMgr
|
||||
from opengnsys.service import IPC_PORT
|
||||
|
||||
trayIcon = None
|
||||
|
||||
|
@ -61,9 +55,9 @@ def sigAtExit():
|
|||
|
||||
|
||||
# About dialog
|
||||
class OGAAboutDialog(QtGui.QDialog):
|
||||
class OGAAboutDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
self.ui = Ui_OGAAboutDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.VersionLabel.setText("Version " + VERSION)
|
||||
|
@ -72,9 +66,9 @@ class OGAAboutDialog(QtGui.QDialog):
|
|||
self.hide()
|
||||
|
||||
|
||||
class OGAMessageDialog(QtGui.QDialog):
|
||||
class OGAMessageDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
self.ui = Ui_OGAMessageDialog()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
|
@ -89,7 +83,7 @@ class OGAMessageDialog(QtGui.QDialog):
|
|||
class MessagesProcessor(QtCore.QThread):
|
||||
logoff = QtCore.pyqtSignal(name='logoff')
|
||||
message = QtCore.pyqtSignal(tuple, name='message')
|
||||
script = QtCore.pyqtSignal(QtCore.QString, name='script')
|
||||
script = QtCore.pyqtSignal(str, name='script')
|
||||
exit = QtCore.pyqtSignal(name='exit')
|
||||
|
||||
def __init__(self, port):
|
||||
|
@ -119,9 +113,13 @@ class MessagesProcessor(QtCore.QThread):
|
|||
if self.ipc:
|
||||
self.ipc.sendLogin(user_data)
|
||||
|
||||
def sendLogout(self, userName):
|
||||
def sendLogout(self, username):
|
||||
if self.ipc:
|
||||
self.ipc.sendLogout(userName)
|
||||
self.ipc.sendLogout(username)
|
||||
|
||||
def sendMessage(self, module, message, data):
|
||||
if self.ipc:
|
||||
self.ipc.sendMessage(module, message, data)
|
||||
|
||||
def run(self):
|
||||
if self.ipc is None:
|
||||
|
@ -136,20 +134,17 @@ class MessagesProcessor(QtCore.QThread):
|
|||
msg = self.ipc.getMessage()
|
||||
if msg is None:
|
||||
break
|
||||
msgId, data = msg
|
||||
logger.debug('Got Message on User Space: {}:{}'.format(msgId, data))
|
||||
if msgId == ipc.MSG_MESSAGE:
|
||||
module, message, data = data.split('\0')
|
||||
msg_id, data = msg
|
||||
logger.debug('Got Message on User Space: {}:{}'.format(msg_id, data))
|
||||
if msg_id == ipc.MSG_MESSAGE:
|
||||
module, message, data = data.decode('utf-8').split('\0')
|
||||
self.message.emit((module, message, data))
|
||||
elif msgId == ipc.MSG_LOGOFF:
|
||||
elif msg_id == ipc.MSG_LOGOFF:
|
||||
self.logoff.emit()
|
||||
elif msgId == ipc.MSG_SCRIPT:
|
||||
self.script.emit(QtCore.QString.fromUtf8(data))
|
||||
elif msg_id == ipc.MSG_SCRIPT:
|
||||
self.script.emit(data.decode('utf-8'))
|
||||
except Exception as e:
|
||||
try:
|
||||
logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
|
||||
except:
|
||||
logger.error('Got error on IPC thread (an unicode error??)')
|
||||
logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
|
||||
|
||||
if self.ipc.running is False and self.running is True:
|
||||
logger.warn('Lost connection with Service, closing program')
|
||||
|
@ -157,27 +152,26 @@ class MessagesProcessor(QtCore.QThread):
|
|||
self.exit.emit()
|
||||
|
||||
|
||||
class OGASystemTray(QtGui.QSystemTrayIcon):
|
||||
class OGASystemTray(QtWidgets.QSystemTrayIcon):
|
||||
jobmgr = JobMgr()
|
||||
def __init__(self, app_, parent=None):
|
||||
self.app = app_
|
||||
self.config = readConfig(client=True)
|
||||
|
||||
self.modules = None
|
||||
# Get opengnsys section as dict
|
||||
cfg = dict(self.config.items('opengnsys'))
|
||||
|
||||
# Set up log level
|
||||
logger.setLevel(cfg.get('log', 'INFO'))
|
||||
|
||||
self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
|
||||
|
||||
# style = app.style()
|
||||
# icon = QtGui.QIcon(style.standardPixmap(QtGui.QStyle.SP_ComputerIcon))
|
||||
icon = QtGui.QIcon(':/images/img/oga.png')
|
||||
QtCore.QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), 'img'))
|
||||
icon = QtGui.QIcon('images:oga.png')
|
||||
|
||||
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
|
||||
self.menu = QtGui.QMenu(parent)
|
||||
exitAction = self.menu.addAction("About")
|
||||
exitAction.triggered.connect(self.about)
|
||||
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
|
||||
self.menu = QtWidgets.QMenu(parent)
|
||||
exit_action = self.menu.addAction("About")
|
||||
exit_action.triggered.connect(self.about)
|
||||
self.setContextMenu(self.menu)
|
||||
self.ipc = MessagesProcessor(self.ipcport)
|
||||
|
||||
|
@ -208,18 +202,16 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
|
||||
|
||||
# Send init to all modules
|
||||
validMods = []
|
||||
valid_mods = []
|
||||
for mod in self.modules:
|
||||
try:
|
||||
logger.debug('Activating module {}'.format(mod.name))
|
||||
mod.activate()
|
||||
validMods.append(mod)
|
||||
valid_mods.append(mod)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error("Activation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
|
||||
|
||||
self.modules[:] = validMods # copy instead of assignment
|
||||
|
||||
logger.debug ("Activation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
|
||||
self.modules[:] = valid_mods # copy instead of assignment
|
||||
# If this is running, it's because he have logged in, inform service of this fact
|
||||
self.ipc.sendLogin((operations.getCurrentUser(), operations.getSessionLanguage(),
|
||||
operations.get_session_type()))
|
||||
|
@ -231,7 +223,7 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
mod.deactivate()
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
|
||||
logger.debug ("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
|
||||
|
||||
def timerFnc(self):
|
||||
pass
|
||||
|
@ -244,7 +236,7 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
logger.debug('msg: {}, {}'.format(type(msg), msg))
|
||||
module, message, data = msg
|
||||
except Exception as e:
|
||||
logger.error('Got exception {} processing message {}'.format(e, msg))
|
||||
logger.debug ('Got exception {} processing message {}'.format(e, msg))
|
||||
return
|
||||
|
||||
for v in self.modules:
|
||||
|
@ -254,22 +246,22 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
v.processMessage(message, json.loads(data))
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
|
||||
logger.debug ('Got exception {} processing generic message on {}'.format(e, v.name))
|
||||
|
||||
logger.error('Module {} not found, messsage {} not sent'.format(module, message))
|
||||
logger.debug ('Module {} not found, messsage {} not sent'.format(module, message))
|
||||
|
||||
## when is this run??
|
||||
def executeScript(self, script):
|
||||
logger.debug('Executing script')
|
||||
script = six.text_type(script.toUtf8()).decode('base64')
|
||||
th = ScriptExecutorThread(script)
|
||||
th.start()
|
||||
script = base64.b64decode(script.encode('ascii'))
|
||||
logger.debug('Executing received script "{}"'.format(script))
|
||||
self.jobmgr.launch_job (script, True)
|
||||
|
||||
def logoff(self):
|
||||
logger.debug('Logoff invoked')
|
||||
operations.logoff() # Invoke log off
|
||||
|
||||
def about(self):
|
||||
self.aboutDlg.exec_()
|
||||
self.aboutDlg.exec()
|
||||
|
||||
def cleanup(self):
|
||||
logger.debug('Quit invoked')
|
||||
|
@ -279,7 +271,7 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
self.deinitialize()
|
||||
except Exception:
|
||||
logger.exception()
|
||||
logger.error('Got exception deinitializing modules')
|
||||
logger.debug ('Got exception deinitializing modules')
|
||||
|
||||
try:
|
||||
# If we close Client, send Logoff to Broker
|
||||
|
@ -290,7 +282,7 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
except Exception:
|
||||
# May we have lost connection with server, simply log and exit in that case
|
||||
logger.exception()
|
||||
logger.exception("Got an exception, processing quit")
|
||||
logger.debug ('Got an exception, processing quit')
|
||||
|
||||
try:
|
||||
# operations.logoff() # Uncomment this after testing to logoff user
|
||||
|
@ -311,20 +303,20 @@ class OGASystemTray(QtGui.QSystemTrayIcon):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
|
||||
if not QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
|
||||
# QtGui.QMessageBox.critical(None, "Systray", "I couldn't detect any system tray on this system.")
|
||||
sys.exit(1)
|
||||
|
||||
# This is important so our app won't close on messages windows (alerts, etc...)
|
||||
QtGui.QApplication.setQuitOnLastWindowClosed(False)
|
||||
QtWidgets.QApplication.setQuitOnLastWindowClosed(False)
|
||||
|
||||
try:
|
||||
trayIcon = OGASystemTray(app)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error('OGA Service is not running, or it can\'t contact with OGA Server. User Tools stopped: {}'.format(
|
||||
logger.debug ('OGA Service is not running, or it can\'t contact with OGA Server. User Tools stopped: {}'.format(
|
||||
utils.exceptionToMessage(e)))
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -332,7 +324,7 @@ if __name__ == '__main__':
|
|||
trayIcon.initialize() # Initialize modules, etc..
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
|
||||
logger.debug ('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
|
||||
trayIcon.quit()
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -342,7 +334,7 @@ if __name__ == '__main__':
|
|||
# Catch kill and logout user :)
|
||||
atexit.register(sigAtExit)
|
||||
|
||||
res = app.exec_()
|
||||
res = app.exec()
|
||||
|
||||
logger.debug('Exiting')
|
||||
trayIcon.quit()
|
||||
|
|
|
@ -1,292 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Resource object code
|
||||
#
|
||||
# Created by: The Resource Compiler for PyQt4 (Qt v4.8.6)
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
qt_resource_data = b"\
|
||||
\x00\x00\x0f\x42\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x01\x20\x05\xc9\x11\
|
||||
\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc7\x00\x00\x0e\xc7\
|
||||
\x01\x38\x92\x2f\x76\x00\x00\x0e\xf4\x49\x44\x41\x54\x78\xda\xc5\
|
||||
\x59\x07\x58\x53\xd9\x12\xbe\x29\xd4\x84\x12\x21\xf4\x22\x08\x08\
|
||||
\x02\x22\xa2\x22\x22\x22\x8a\xa0\xa0\x14\x1b\xae\x62\x05\xac\xb8\
|
||||
\x76\xd7\xde\xd6\xde\x15\xf5\xd9\x7b\x17\x51\x41\x50\x14\x10\x04\
|
||||
\x44\x94\x8e\xd2\x25\x80\x80\x48\xef\x24\xb4\xbc\x39\x59\x6e\x4c\
|
||||
\x42\xe2\x0a\xea\x7b\xf3\x7d\xe1\xde\x7b\xca\xcc\x9c\x73\xe6\xcc\
|
||||
\xfc\x33\x90\xd9\x6c\x36\x86\xe8\xe8\xe9\x9b\xe9\xab\x97\x79\x9a\
|
||||
\x62\x5d\x44\x46\x7f\xd6\x6f\x3f\xc1\xb6\xb7\x1d\xf6\xf7\xc3\xc0\
|
||||
\xf0\xf3\x53\x5c\xc6\x2e\xe4\x74\x9c\xbe\x78\xff\xcd\xc1\x9d\x2b\
|
||||
\x08\xf8\x48\x34\x48\x55\x85\x9e\x4a\x5e\xe6\x3d\x7d\x04\xfa\x40\
|
||||
\x8d\xd3\xdd\xc6\x6d\x73\x1c\x6b\x55\xd8\xd9\xd1\xf9\x88\xc3\x0a\
|
||||
\x9f\x11\x19\x93\xc8\x4e\x4e\xcb\xce\x5e\xeb\x3b\x7b\x07\x19\xe3\
|
||||
\xa1\xd1\x23\x2d\x08\xd0\x91\xcc\x91\x81\x6b\x15\x15\x9b\xb4\xd6\
|
||||
\xd6\x7a\xf0\x61\xae\x56\x1b\x77\x9d\x62\xed\xdb\xe6\x2b\x11\xfc\
|
||||
\x22\xfa\x10\x5f\x47\x47\x47\x87\x38\xc6\x4f\x6c\xee\x3a\x0e\x9f\
|
||||
\xba\x91\xd1\xa5\x00\x6a\x24\x30\x99\x2c\x39\x32\xef\x1a\x10\xed\
|
||||
\x3e\x7c\x91\xb5\x65\xad\xb7\x38\x9f\x56\x57\x6f\x3f\x79\xda\xd9\
|
||||
\xc9\x76\xae\xaa\xae\xd5\xe7\x6a\x85\x2f\x12\x71\x40\xef\x82\x9c\
|
||||
\xba\xed\xe1\x9c\x19\x13\x27\x9b\x18\xf5\x7b\x84\x77\xf8\x07\x86\
|
||||
\x5f\x98\xea\x32\xd6\x07\xbd\x57\xd7\xd4\xe9\xef\x3f\x7e\x35\x47\
|
||||
\x4d\x85\xde\x40\x3e\x70\xe2\x6a\x1e\x6a\xec\xec\xec\xe4\x4c\x6e\
|
||||
\x6f\xef\x90\x44\x4f\x34\xf8\xaf\x1d\x27\xd8\x07\x76\xac\xc0\x52\
|
||||
\xd3\x73\x36\x4d\x18\x67\x1d\x6a\x37\x72\x88\x23\xf9\xaf\x15\xf3\
|
||||
\xf4\xf8\x44\x92\x49\x4c\xa4\xce\xd5\xdb\x81\x9c\xc1\x70\x58\x71\
|
||||
\x70\x2e\xfb\x22\x5e\xbf\xdb\xcb\x55\x49\x18\x95\x55\xd4\xb4\xc0\
|
||||
\x43\x0a\x06\x5b\x9d\xba\x70\xaf\xc8\xd7\xc7\x03\xdb\xb0\xd3\xaf\
|
||||
\x43\xe4\x84\x0d\x2b\xe6\x4a\x27\xa5\x64\xb2\x07\x0f\x32\xc2\x5c\
|
||||
\x27\xd8\x6a\xc2\xc9\xb4\xed\xdf\xbe\x5c\x9c\xbb\x4b\x88\x92\xd3\
|
||||
\xb2\x66\xdd\x79\x18\x7a\x53\xd4\x0e\x71\x55\x82\x3d\x26\x6d\xd8\
|
||||
\x79\xb2\x7d\xa9\xd7\x34\x9b\x95\x4b\x66\x9a\xff\xeb\xb6\xa2\xc1\
|
||||
\x5b\xd7\xf9\xa8\xca\x50\xa5\xcb\xf0\xf3\x10\x29\x01\x1f\x80\x06\
|
||||
\xa3\xe7\xd0\xc1\xc6\x97\xdf\x27\x7d\x5c\xc0\x3b\x68\xdf\xb1\xcb\
|
||||
\x0d\x34\x79\x39\xaa\x89\xa1\xee\xca\x6e\x8b\xd6\xd3\xd1\x88\xc0\
|
||||
\x27\x7c\x29\xab\x1c\x25\x2f\x27\x13\x45\xa5\x50\x72\x86\x0e\x32\
|
||||
\x32\xa0\x50\xa4\x8f\x77\x9b\x80\x16\xbd\x69\xf5\x02\xed\xd6\xd6\
|
||||
\x36\x8a\xaa\x8a\x62\x14\xd8\x6b\xd6\xf2\x85\x1e\x46\x99\xd9\x05\
|
||||
\xec\x80\xa7\xaf\x0a\xc9\x72\xb2\xd4\xe2\xba\xfa\x46\x0d\x5c\x35\
|
||||
\xdd\xbe\xea\x51\xc0\xb5\x28\xe8\xf9\x6b\xf6\xa4\xf1\xa3\x30\x65\
|
||||
\xba\xc2\x01\xd4\x2e\x21\x21\x6e\xeb\x3e\xd1\x4e\x86\xbc\x79\x8d\
|
||||
\x97\xa6\xb0\xc5\xa1\xc1\x88\x86\x98\x0f\xb8\xfa\x22\x22\x2e\xc8\
|
||||
\x61\x8c\xd5\xc4\x80\xa0\x08\x86\xd0\x83\xcb\xc9\x2b\x74\x34\xd0\
|
||||
\xd3\xe6\x7e\x9b\x9b\x19\x4d\x44\xcf\xf8\xc4\xf4\xbe\x42\x27\xc0\
|
||||
\xe0\x50\xde\x2b\x95\x5f\x50\x82\xd1\x15\xe4\x31\xb0\x2d\xa2\x48\
|
||||
\xd3\x68\x6a\x6e\xa1\x1f\x3e\x75\x93\xb1\x75\xad\x17\xcd\xd2\xc2\
|
||||
\xb8\xcd\xef\xfc\xbd\x42\x58\xbc\x68\xe3\xcb\x67\x14\x8f\xd9\xbe\
|
||||
\xde\x87\xfa\x24\x24\xaa\xcd\xd5\xc9\x16\xf3\x99\xe3\x66\xc6\xe7\
|
||||
\x07\x78\xe9\xdc\xd5\x87\x11\x9f\x18\xc5\x76\xf8\xb7\xa6\xba\xf2\
|
||||
\xfb\xe5\x0b\x67\x0c\xc3\x7a\x41\x7c\x1a\xf1\x9a\x11\x5a\x1f\x81\
|
||||
\x80\xb1\x13\x53\x32\xe7\xdc\x7b\xf4\xe2\x1a\xde\xb7\x78\xfe\xd4\
|
||||
\xd1\xe8\xa8\x7a\x24\x20\x2a\x36\x71\x5d\xf0\x8b\x98\x83\xe8\x5d\
|
||||
\x52\x52\xa2\x6e\xd7\xc6\xc5\xf2\xf8\x00\x8b\x41\x46\xd7\x91\x00\
|
||||
\xfc\x1b\x8d\xed\x91\x80\xbc\xfc\xcf\x63\x71\xe6\x88\x78\x99\xe3\
|
||||
\xb4\x68\xfe\x14\xbb\x73\x57\x1e\xbe\xfa\xc7\xf6\x07\x5c\xe6\xf3\
|
||||
\x92\x6c\x36\x71\xcb\x9e\x33\x6d\xfa\xba\x5a\xc4\xd9\x1e\xce\x18\
|
||||
\x89\x44\xe4\xf1\x7e\x81\x35\xe4\xf3\xd7\x02\xc2\x04\x27\x10\x08\
|
||||
\x84\x4e\x3e\x73\xf7\x7f\x7e\x0b\x3d\x89\x44\x62\xbb\x89\x91\x5e\
|
||||
\x00\x7a\x2f\x2a\x2e\x73\x3a\x7b\xc5\x3f\x78\xef\x56\x5f\x6c\xcf\
|
||||
\x96\x65\xd8\xf6\xfd\x67\xeb\xd3\x3e\xe6\x2c\x95\x92\x92\x6c\xaf\
|
||||
\xab\x6f\xba\x0b\x96\x81\xa9\xa9\x2a\x7d\x21\x7b\xcf\x76\x1b\x7f\
|
||||
\xf1\xc6\xe3\xe7\x5c\xcf\xb2\xd3\xaf\xed\xc0\x8e\x3f\x49\x82\xe7\
|
||||
\x62\xa8\xdf\x37\x64\x81\xa7\xab\x33\xde\x06\xde\x06\x43\xcc\xbb\
|
||||
\xbe\x91\x3b\x96\xe3\x84\xb2\x33\x37\xd9\x43\x06\x0d\xa8\xdd\xba\
|
||||
\xe7\x0c\x79\xc3\xca\x79\xa3\xc9\xc8\x88\x91\x33\x89\x8e\x4b\x5e\
|
||||
\x05\xf7\xeb\x28\x5a\x01\xce\x14\x1d\xf2\x8a\xc5\x33\x07\xab\xab\
|
||||
\xd2\x93\x79\x05\xc2\x78\x5e\xc3\xc0\x78\x9d\x51\x7f\x3d\xed\xa0\
|
||||
\xa7\xa1\xd1\x93\xf4\x74\x34\xb1\xf8\x84\x8f\xe5\x5c\x2b\xb2\xb1\
|
||||
\x32\x3f\x86\x7e\xdf\x3b\xb0\x82\xa2\x52\x6b\xfb\xd1\x96\x22\xfb\
|
||||
\x71\x37\xef\xec\x60\xc3\x15\x4e\xee\x89\x4d\xab\x2a\xd3\xd3\xc2\
|
||||
\xa3\xde\xf3\xb5\x59\x5a\x98\x60\x17\xae\x3d\xea\xd4\xd6\x54\x21\
|
||||
\xcc\xfd\x63\x12\x8f\x7f\xce\xae\x9a\x60\x3f\xe2\x48\x8f\x04\x48\
|
||||
\x48\x88\x35\x8c\xb2\x1e\x3c\xee\xef\x43\x17\x43\x60\x7f\xc5\xc4\
|
||||
\xc4\xc8\x10\xbd\x87\x74\x1c\xf2\xbb\xce\xae\xad\xab\xaf\xb6\xb6\
|
||||
\x1c\xa4\xd4\x15\xc4\xc0\xb1\x7c\x48\x5f\x3c\x7f\xca\x3e\x72\x4f\
|
||||
\x6f\xa6\xbe\xae\x66\x18\xc4\x22\x83\x0b\xd7\x1f\x31\x1c\xc7\x58\
|
||||
\x61\xe9\x19\xb9\x24\x63\xc3\x7e\xfe\x39\x9f\x0a\xa7\x52\x28\x52\
|
||||
\x1c\xe6\x7b\x8f\x5e\x29\xdd\xb2\xd6\xcb\xee\xbb\x81\xed\x7b\xd4\
|
||||
\x87\x26\x5b\x00\x91\xfd\x6b\x42\x4a\x46\x2b\xf8\x4f\xcd\xe6\x66\
|
||||
\xe6\x54\x3b\x1b\x8b\x57\x87\xfd\xae\xab\x8d\xb7\xb7\xde\x08\xcc\
|
||||
\xb9\x31\x5b\xa8\x2f\x42\x04\x93\x14\x82\x42\x5f\x1f\x49\xfb\x98\
|
||||
\x3b\x8d\x4c\x22\xb5\xee\xdc\xb8\x98\xf6\xd3\xbe\x88\x13\x92\xbf\
|
||||
\x56\x9a\x1e\x3d\x73\x2b\x8d\xb7\x0d\xc5\x47\xac\x97\xc4\x27\xe0\
|
||||
\xc1\x93\xb0\x4b\x78\x44\x43\xb6\x8f\xee\x00\x7a\x87\xd8\x1f\x07\
|
||||
\x37\x77\xb8\xcb\x04\xdb\x15\x23\x87\x0f\x3a\xd9\x2b\x01\x8f\x9e\
|
||||
\xbe\x3a\x83\x33\x47\x0e\xce\xc3\xdd\x61\xae\xa0\x87\x0d\x7c\x16\
|
||||
\x75\xa2\x57\x02\x8a\x4b\xcb\x2d\xe2\xde\xa7\x2d\x41\xef\x52\x92\
|
||||
\x12\xb5\x38\xf3\x5f\x41\x1c\x01\x27\xcf\xdd\x49\xc0\x1b\x04\xa3\
|
||||
\xae\x9b\xd3\xe8\xe5\x8f\x43\x22\xfd\x7a\x2d\x20\x3c\xea\xdd\x16\
|
||||
\xde\x06\x71\x71\xb1\x46\xde\xef\x11\x96\x66\xa7\x7e\x44\x40\xc8\
|
||||
\xcb\xd8\x07\x1f\x33\x3f\xb9\x56\x54\xd5\x88\x71\x6e\xbd\x0a\x3d\
|
||||
\x6f\xd5\x92\x99\xfa\xe4\xd0\x88\xb8\xbf\xf1\x41\x70\x33\x5b\x7a\
|
||||
\xaa\xe1\xeb\x37\x49\x67\x82\x43\xa3\x97\x78\x7a\x38\x61\x4e\xe3\
|
||||
\xac\xb9\xed\x8d\x8d\x2d\x7a\x27\xcf\xde\x49\xe0\xb3\x22\x8a\xb4\
|
||||
\x54\xc5\xf7\x98\xa9\x28\x2b\xa6\xf3\x7e\xef\x39\x72\x89\xa9\xa3\
|
||||
\xad\x2e\x71\x00\xbc\x6b\x65\x55\x6d\x33\x18\x84\xb4\x9a\x32\xbd\
|
||||
\x73\xe5\xd2\x99\x44\x2a\x55\x0a\x6b\x6d\x6f\x97\xe1\x13\xd0\xd0\
|
||||
\xd8\xa4\x2a\xc8\x14\x07\xc7\x88\x16\xcc\x72\x71\xc6\xdf\xc1\xc1\
|
||||
\x35\xb9\x39\xdb\x49\x18\x1b\xea\x62\xe5\x15\xd5\x0d\xc7\xfe\x73\
|
||||
\x5b\x02\xb9\xed\x93\xe7\xef\x72\xad\x8e\x2a\x2d\x55\xc6\x27\xa0\
|
||||
\xa3\xa3\x53\x4c\x50\x00\x4a\x17\xba\xb6\xaf\x19\x30\xd9\x67\xf4\
|
||||
\x7e\xe4\xf4\xcd\xba\x31\x36\x43\xa4\x11\x73\x8e\xb0\xeb\x01\xf5\
|
||||
\x90\x88\x68\x7c\xcb\x34\x30\xec\xfe\xe3\x97\x59\x66\x03\x0d\xee\
|
||||
\x74\xbb\xc9\x9f\x4b\xbe\x0e\x45\x30\xa5\x6b\x7f\x57\xc3\xd2\xf5\
|
||||
\x39\x29\xc7\xe6\x65\xd4\x2e\x58\xe6\x29\x21\x2e\x26\x6b\x3e\xd0\
|
||||
\x90\x33\x1e\x00\x41\xb6\xfb\xc4\x31\xeb\xd1\x7b\x62\x6a\xe6\x5e\
|
||||
\x2a\x45\x1a\xbb\x7c\x2b\x30\xbd\xae\xbe\xa1\x13\x32\xab\xb3\x1c\
|
||||
\x30\x08\x7b\xf9\x19\x17\xe0\x77\xfe\xee\x3b\x04\x6e\x11\xf2\x42\
|
||||
\x50\x1a\xb5\x21\x3f\x84\xa2\x1b\x7a\x87\xf0\x7a\x83\x37\xa2\x01\
|
||||
\xac\xe9\x0f\xf7\x26\x90\x83\x3c\xb5\xd5\xcf\x41\x40\xcf\x30\x32\
|
||||
\xd0\x09\x2a\xfd\x5a\x69\xd6\xd8\xd8\xac\xc4\x81\xa7\x28\xcf\x82\
|
||||
\xcc\x24\x1f\x60\xaa\xfa\x3f\x67\xd1\xac\xc2\xc1\x84\xfd\xb4\x5e\
|
||||
\x78\xcf\x71\x77\xc4\x99\x65\xe5\x16\x4c\x1b\x31\xcc\x8c\xcb\xbc\
|
||||
\xbc\xb2\x06\x1b\x64\xda\xff\x0e\xfe\x2d\x29\x21\x51\xff\x30\x28\
|
||||
\xe2\x9a\x96\x9a\x32\xb1\xbf\x7e\x5f\x2c\x3c\x22\x21\x9a\xb3\x45\
|
||||
\x24\xf0\x96\xb0\x12\x8d\x7f\x33\xc9\x98\xf8\x94\x63\xde\x9e\x6e\
|
||||
\xdc\xef\xac\x1c\x46\xae\xf5\x70\x33\xee\x1d\xb9\xf7\xf8\x65\xf5\
|
||||
\xce\x0d\x8b\x10\x3f\xce\x77\xf0\xcb\x18\x9b\x1e\xc5\x83\x9c\xdc\
|
||||
\x42\x75\xde\x6f\x70\x2b\xc5\xb5\xb5\x0d\x5a\xda\x1a\xaa\x71\x57\
|
||||
\x6e\x05\x66\x8f\xb2\xb6\xe0\x32\xaf\xad\x6b\xc0\x10\xc4\x21\xff\
|
||||
\x8c\x9f\xa9\x6f\x68\x1a\x06\xfb\xbd\xa6\xa9\xa9\x45\xad\xb0\xf8\
|
||||
\x8b\x41\xbf\xbe\xdf\xe4\x83\xf3\xcc\x07\xc4\xbf\xb2\x47\x02\xe8\
|
||||
\x8a\xb4\x6a\x14\xd0\xbe\x21\x91\xc1\x94\x4b\x37\x1f\x27\x12\xe1\
|
||||
\x64\xb7\xad\x5b\xc8\x37\x36\x33\x87\xa1\x3b\x5f\xce\xe5\x73\x8f\
|
||||
\x04\x8c\xb2\x1a\xbc\xf1\x55\x74\xc2\x39\x3b\x9b\x21\x5d\x7e\x8b\
|
||||
\x8c\x2d\x59\x30\x95\xc0\x62\xb5\xb2\xf7\x1f\xbf\x4c\xd8\xb4\xda\
|
||||
\xab\xeb\xc2\x36\x63\xda\x9a\xaa\x6f\x7a\x1c\x93\x2d\x87\x98\x9c\
|
||||
\xdf\xb4\xeb\x94\x9f\xa4\xb8\xb8\xb8\x95\xe5\x40\x4e\x5b\x58\x64\
|
||||
\x7c\xea\xcb\xc8\x78\x53\x49\x09\x71\x2e\xf8\xba\x1b\x10\x9a\xe6\
|
||||
\x33\xc7\xdd\xa1\x57\x41\x7f\x2f\x98\x34\x30\xdd\xb6\x69\xf7\xa9\
|
||||
\x8d\x04\x36\x81\x34\xc5\x65\xec\x11\x40\x19\x0c\xd8\x92\x68\x7c\
|
||||
\x0c\xe4\x16\x03\xc0\x2b\x37\xf5\x1a\x55\xa4\x7e\xcc\x5d\xa4\xad\
|
||||
\xa1\x26\xe9\x3d\xdb\x1d\xdb\xb8\xeb\xe4\x75\x45\x05\x5a\xdd\xea\
|
||||
\xa5\xb3\x38\x7d\x37\xef\x3f\x7b\xb7\x7a\x99\xe7\x5c\x91\x41\xff\
|
||||
\x47\x48\xa1\x8f\x9c\xfc\xbc\x2e\x14\xb7\x6f\xdb\x72\xec\xdc\xb5\
|
||||
\x00\x39\x48\xfc\x31\x46\x61\x49\x45\x51\xf1\x17\x55\x25\x45\x5a\
|
||||
\xd6\x4f\x09\xb0\x1e\x66\xe6\x76\xe6\xd2\xfd\x13\x0e\x76\x23\x34\
|
||||
\x5a\x98\x4c\x19\x27\xfb\x11\xf5\x00\x9e\x1f\x46\xc6\x24\x4c\x80\
|
||||
\xa4\x5b\x8b\x2f\xa2\x89\xc2\x45\xc2\xe8\x53\x41\xf1\xe8\x98\xb8\
|
||||
\x94\x15\x59\xb9\x0c\x67\x61\x9e\x17\x05\x2c\x2f\x4f\xb7\x09\x3d\
|
||||
\xc9\x80\x7e\x49\xcc\xff\x1e\xa1\xb4\xe1\x59\x58\xec\xbe\xf6\xf6\
|
||||
\x0e\x89\x6e\x01\x4e\x49\xe1\x83\xa9\xb1\xbe\x3f\x84\x85\x27\x6a\
|
||||
\x2a\xf4\x14\xd8\x0b\x42\x76\x5e\xc1\xf8\xd0\xf0\xb8\xdd\xda\x5a\
|
||||
\xaa\xb1\x90\xb3\x3c\xfb\xbf\x2c\x00\x45\x81\x3b\x0f\x9f\xdf\x4a\
|
||||
\xfd\x90\xe3\x21\xd8\x87\x20\xd9\x94\x49\x63\x17\x82\xcd\xb0\xf0\
|
||||
\xb6\xf8\x84\x0f\x0b\x01\xfb\xbd\x15\x5c\x24\x44\xf8\xca\x2d\x6b\
|
||||
\xbd\xd5\x20\x2d\x6c\xfb\x9f\x2d\x20\xe6\x6d\xca\x9f\x08\x1f\x76\
|
||||
\x4f\x0a\xb4\xc2\xbc\x20\x5b\x04\xaf\xd6\xc1\xdb\x8e\x83\x56\x11\
|
||||
\xc5\x08\x45\x74\x82\xa3\x47\x5a\x1c\xfc\xed\x0b\x40\x36\x7d\xfc\
|
||||
\xec\xed\xe4\xaf\xe5\x55\xc6\x82\x83\x20\xcf\xda\x64\x67\x33\x74\
|
||||
\x9f\x30\x06\x28\x05\x3e\x70\xf2\x5a\x2e\xc4\x03\xba\xb0\x7e\xb8\
|
||||
\xd5\xc3\x7f\xbb\x09\xb5\x30\x59\xf2\xfb\x8f\x5d\x61\xa0\x67\x37\
|
||||
\xef\x6c\x61\x72\x41\x94\xf2\x78\x59\x63\xd3\xaa\x05\xda\x00\x4a\
|
||||
\x8a\x9b\x5b\x98\x7d\xba\x8f\x20\xb0\x7b\xab\x5c\x45\x65\xad\x41\
|
||||
\x69\x59\xb9\x65\x6d\x5d\xa3\xa1\x18\x99\xc4\x6c\xef\xe8\x60\x2a\
|
||||
\xd1\x69\x19\xba\xda\x1a\x91\x5c\x47\x8d\xec\x1d\xd5\xb0\x85\x29\
|
||||
\x8f\x08\x52\xee\x9d\xff\x26\x08\x79\x9f\x49\xe3\x47\xad\x06\xf8\
|
||||
\x75\x55\xb0\x4f\x5d\x8d\x9e\xf4\x23\xca\x02\x3c\xe8\x17\x1b\x9f\
|
||||
\x72\x36\x3a\x2e\xc5\x1e\xe5\xa8\x70\x7f\x50\xb9\x11\xa3\xc9\xcb\
|
||||
\x00\x1a\x56\x40\xd0\x02\x83\x0d\x42\xe8\x17\x8b\x7e\x9b\xca\x66\
|
||||
\xb6\xb0\xb2\x2d\xcc\x0c\x4f\x93\x43\x5e\xc6\x1c\x44\x05\x41\x61\
|
||||
\x4c\x11\x1a\x83\x5f\xc9\x8f\x28\x40\xa3\xc9\x32\x84\xb5\x0f\x35\
|
||||
\x37\xbe\x22\x6a\x0e\x04\x6d\xcd\xdb\xfe\xcf\x62\x21\xf4\x69\xca\
|
||||
\xca\x50\xb0\x89\x8e\x36\x9c\xc0\x02\xf0\x11\x4c\xba\x03\x7b\x9f\
|
||||
\x94\x91\x99\x99\xcb\xc0\xc0\xbb\x19\x59\x0f\x1b\x84\x2a\x31\x70\
|
||||
\x19\x31\x0c\xf2\x37\x42\xf4\x9b\x64\x03\x94\xb0\x90\x01\xa8\xce\
|
||||
\x16\x25\x00\xee\x85\xf8\x8f\x1e\x37\xca\xa3\x05\xdb\x1c\xc6\x58\
|
||||
\x6d\x03\xc5\x4a\x85\x14\x53\xdc\x01\x18\xfb\xb7\xb6\xb6\x12\x27\
|
||||
\x3a\x8e\xc2\x16\xcd\x9b\xc2\xed\x2b\x2e\xfd\x5a\x7d\xf1\xfa\x63\
|
||||
\x12\x84\xdf\x4c\x4d\x75\xa5\xf7\x03\x8d\xf5\x13\x60\x21\x57\x5a\
|
||||
\x5b\x59\x44\x5e\x1e\xb2\xb2\x14\xe4\x05\xa5\xc8\x4c\x66\xab\x9c\
|
||||
\x28\xa5\x1a\x9b\x9a\x95\xe0\x68\xb5\x50\x89\xf6\x7b\xca\xa7\xa4\
|
||||
\xe7\xcc\x78\x13\x9f\xea\x2b\xe8\x6e\xd1\x7f\x51\x78\xdb\xaa\xaa\
|
||||
\x6b\x4d\x4f\x5d\xbc\x9f\x04\x32\xc9\x0b\x3c\x5d\x51\xb9\x83\xdb\
|
||||
\xd7\xda\xda\x86\x9d\xbd\xe2\xff\xa9\xe4\x4b\xb9\xee\x8c\xc9\x8e\
|
||||
\xb3\x21\x2b\xe0\x14\xd7\xb2\x73\x0b\xd6\x80\x1e\xc4\xc6\x26\x26\
|
||||
\x77\x2c\x93\xc9\x62\x07\x3e\x7b\xdd\x02\x9b\x53\x4b\x36\x33\xd1\
|
||||
\xbf\x9f\x94\x9a\xe5\x29\x4a\xb9\xa0\xe7\xd1\x47\x66\x7b\x38\x4d\
|
||||
\x13\xd5\x8f\x5c\x2e\x72\xbd\x02\xe5\xe5\xd5\x82\x25\x2d\xff\x27\
|
||||
\x61\x31\xef\x92\x3e\x5a\x83\x4b\x85\x14\x71\x24\x1f\x0f\xb0\xeb\
|
||||
\xd6\x23\xa7\x6f\x10\x15\x68\x72\x6d\x7b\xb6\xf8\x4a\xe1\x31\x06\
|
||||
\xcc\x48\x02\x92\x80\x7d\x80\x17\x61\x43\x0c\x51\x7c\xc2\x9e\x85\
|
||||
\xc5\x64\x15\x7d\x2e\xab\xea\x43\x93\x25\x2e\x9e\x3f\xd5\x96\xec\
|
||||
\xe1\xee\x38\xa7\xae\xae\x51\x03\xc1\x04\x61\x0a\xa6\x67\xe4\x4e\
|
||||
\xdd\x7d\xf8\x62\x29\xda\x15\x3d\x5d\xcd\x70\x3c\x2b\x81\xe8\xbc\
|
||||
\x37\x21\x39\x63\x3e\xef\x58\x0d\x35\xe5\x84\x85\x73\xdd\xed\x91\
|
||||
\x67\xe2\x75\xcf\xe0\x24\x60\xf3\xeb\x64\xd6\xf8\xce\xc6\x94\xe9\
|
||||
\x7d\x04\x61\x39\x1b\x92\xb8\x0f\x06\x7a\x7d\x8b\xe7\xcf\x9c\xe4\
|
||||
\xca\xdb\xf7\x34\x34\xe6\x04\x91\x4d\xac\x01\x07\xa3\x04\xe9\x54\
|
||||
\x2c\x89\x48\x68\xd7\xd1\xd6\x78\x0d\xf1\xe8\x20\x9e\xc4\x93\x51\
|
||||
\xbe\x85\x8a\xad\xd5\x35\xf5\x3a\x37\xee\x3d\x7d\x58\xf2\xa5\xc2\
|
||||
\x5c\x08\xf6\x57\x15\xac\xa9\x72\x9d\x24\x81\xd0\x39\x6a\xc4\xe0\
|
||||
\x23\xe3\xc7\x8e\xd8\x2c\x18\x71\x51\xd5\x12\x32\xd3\x2a\x29\x49\
|
||||
\x71\x99\xcd\x6b\xbc\x31\x2a\x45\x4a\x58\x6d\xbc\x02\xf8\xeb\x6f\
|
||||
\x5b\xe7\xd3\xad\x4c\xe4\xea\x64\xbb\x18\xfd\x5a\x5a\x58\x34\xb8\
|
||||
\xab\x9b\xe0\xee\x38\x47\x44\xbf\xdb\x12\x9f\x98\xbe\x59\x49\x51\
|
||||
\xa1\x91\x4a\x95\x7c\x41\xe6\xa9\x8c\x31\xf0\x92\x10\xda\xb5\xdc\
|
||||
\xfc\x22\x7b\xb0\xbf\x09\x5f\xca\x2a\xcd\xea\x1a\x1a\xd5\x11\xce\
|
||||
\x41\xee\x52\xb1\x8f\x5c\x9e\x96\x86\xea\x5b\x93\x01\xfd\x02\xe8\
|
||||
\x0a\xb4\x9c\xef\xdd\x8d\x07\x4f\xc2\xa2\x60\xeb\x65\x56\x2c\xfe\
|
||||
\x43\xa8\xf2\xef\x12\x33\xea\xc0\x5d\xe6\x3a\xd8\x59\xf9\xa1\xd4\
|
||||
\x5b\xa0\xc8\xa6\x88\xee\x04\x89\x48\x92\x6d\x61\xb1\x50\xa5\x15\
|
||||
\x9b\x3c\x69\x0c\xe6\x39\xdd\x89\xb3\x6f\xc9\x69\xd9\x84\xc4\xd4\
|
||||
\x2c\xaa\x50\x2c\x84\x76\x12\x01\xb1\x9f\x01\x63\xb0\xab\x6a\x60\
|
||||
\x62\x23\xcd\x4c\x0c\x30\x75\x55\x25\xa1\x63\x5a\x98\x2d\x79\xa8\
|
||||
\xbc\xeb\x60\x37\x7c\x3b\x6f\xfb\x97\xaf\x95\x56\xfe\x81\x61\x51\
|
||||
\x10\x43\xc4\xa6\xb9\x3a\x60\x00\x5f\xba\xcd\x85\xac\xa4\x05\xa0\
|
||||
\xca\x30\xf2\xef\x0a\xf1\xa0\x84\x29\x28\xde\x01\xf7\x86\x24\xba\
|
||||
\xb2\x2b\x4e\xfc\x67\x21\x4c\x6e\x09\x13\x4e\x7e\xd6\xbd\x80\x17\
|
||||
\x37\x54\x94\x15\x09\x1e\xdf\x92\x7e\x3e\x8a\x4f\xfc\x50\x51\x51\
|
||||
\x55\xa3\x04\xa0\x72\xd1\x6f\x5b\x00\x95\x22\x5d\x0e\x2e\x91\x54\
|
||||
\x59\x55\x23\x7a\x10\x9b\x68\x0e\xc9\x74\xc9\xe7\xe2\x72\x6b\x22\
|
||||
\x81\x44\x09\x7f\x1d\x7f\x1c\x2e\xa7\xfc\xe8\x91\x43\x21\x58\x99\
|
||||
\x09\x9d\x82\x3c\xd2\x93\x90\x48\x39\x0d\x35\xa5\x44\x94\x84\xfe\
|
||||
\xb6\x05\xa0\x32\x2b\x78\xa5\xcc\x37\xef\xd2\x8c\xc6\xd8\x58\x62\
|
||||
\x52\x52\xdd\x63\xe2\xf0\xa1\xc6\x90\x09\x1b\xab\x17\x14\x95\xac\
|
||||
\xac\xa9\x6b\xc0\xa6\xbb\x39\x40\x6e\x2f\x0f\x59\xef\x0b\x80\x16\
|
||||
\xf5\x9c\xc8\x2c\x48\xb7\x1e\x3c\xfb\x04\xee\x55\x17\xee\xc2\xf4\
|
||||
\x5e\xa7\x7c\x3f\x4a\x8b\xe6\x4d\xb6\x3c\xe4\x77\x3d\xef\xf2\xad\
|
||||
\xc7\x34\xf0\x26\x62\xb0\x20\x21\x5e\x0c\xc3\x74\xb4\xd5\x31\xc9\
|
||||
\xb2\xca\xfa\x9b\xf7\x83\xeb\xcb\x2b\x6b\x38\xb0\x66\xde\x4c\x97\
|
||||
\x6e\x63\x23\x5e\xbf\x67\x64\xe5\x16\xf4\x9b\xe6\x6a\xef\xd5\x87\
|
||||
\x26\x97\xff\xdb\x17\x00\x36\xde\x00\x09\x8d\x32\xa4\xfc\xce\x90\
|
||||
\xa5\x2d\xcf\xce\x2b\x74\xd4\xd3\xd5\xca\x07\x08\x41\x55\x52\xec\
|
||||
\x53\x0d\xb0\x41\x16\x14\x56\x42\xa5\x65\x9a\x1c\xb5\xc8\xd5\xc9\
|
||||
\x6e\xfd\xad\x07\x21\x21\x14\x8a\x94\xf8\x80\xfe\x3a\xfc\x99\xfe\
|
||||
\x87\xdc\xca\xe7\xe1\x6f\x74\x00\x07\x9d\x40\xff\x53\xfe\xa9\xa4\
|
||||
\xbb\xa7\x64\x64\xa0\x13\x8c\x7e\x2c\x56\x1b\x35\x22\xfa\xfd\xc5\
|
||||
\x9c\xbc\xc2\xc9\x48\x71\x0d\xf0\x4e\x54\xaa\x74\xbe\x32\x5d\x21\
|
||||
\x04\xa2\xf7\xca\x97\x91\x6f\xaf\x01\xe2\x14\xf7\x98\xec\x20\x58\
|
||||
\x06\xaa\x84\x85\x29\x9a\x0f\xec\x7f\xdb\x65\x82\xed\xca\x1e\xe5\
|
||||
\xc4\xbf\x8a\x62\xe3\x53\xd7\x31\x0a\x4b\xf6\xd3\x15\xe4\x89\xbe\
|
||||
\x3e\xd3\xb9\x55\xb6\xb6\xb6\x76\xdd\xab\xb7\x83\x7c\x37\xec\x3c\
|
||||
\xe9\xdb\x85\xa1\xd0\x82\xb9\xf3\x22\x63\x12\xf2\x42\x5e\xc6\xea\
|
||||
\x41\x1c\x38\x0e\x8b\x5c\xd5\xe3\xa4\xfe\x57\xd1\x40\x63\xbd\x1b\
|
||||
\x99\xd9\x0c\x97\xe2\xd2\x0a\xd3\x8c\x6c\x86\x9c\xe9\x00\x3d\x3c\
|
||||
\x97\xc0\x7c\xe6\xba\xe3\x48\x14\xc3\xef\xc9\xe7\x92\xaf\x99\x37\
|
||||
\xee\x05\xcb\x4b\x4b\x4b\x36\x2c\xf5\x9a\x36\xb2\xaf\x96\x5a\x6c\
|
||||
\xaf\xaa\x12\xbf\x8a\x64\xa8\x94\x32\xef\x39\x6e\x36\xa8\x26\xfd\
|
||||
\x28\xf8\xd5\x19\x00\x81\x86\x60\x52\xb2\x4c\x16\x4b\x16\xb0\x53\
|
||||
\x23\x20\x4c\x4e\x8d\x1a\xd5\xaf\xc5\xc4\xc4\x9a\xca\x2b\xaa\x07\
|
||||
\xfc\x31\xc5\x71\xa6\x7e\x3f\xad\xf0\xef\xf1\xfd\x2f\x9a\x21\xe5\
|
||||
\x2e\x6b\x8c\x93\x5e\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\
|
||||
\x82\
|
||||
"
|
||||
|
||||
qt_resource_name = b"\
|
||||
\x00\x06\
|
||||
\x07\x03\x7d\xc3\
|
||||
\x00\x69\
|
||||
\x00\x6d\x00\x61\x00\x67\x00\x65\x00\x73\
|
||||
\x00\x03\
|
||||
\x00\x00\x70\x37\
|
||||
\x00\x69\
|
||||
\x00\x6d\x00\x67\
|
||||
\x00\x07\
|
||||
\x05\xd4\x57\xa7\
|
||||
\x00\x6f\
|
||||
\x00\x67\x00\x61\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
"
|
||||
|
||||
qt_resource_struct = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x12\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
"
|
||||
|
||||
|
||||
def qInitResources():
|
||||
QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
|
||||
|
||||
|
||||
def qCleanupResources():
|
||||
QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
|
||||
|
||||
|
||||
qInitResources()
|
|
@ -1 +1 @@
|
|||
1.1.1b
|
||||
5.7.0
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
<item>
|
||||
<widget class="QLabel" name="VersionLabel">
|
||||
<property name="text">
|
||||
<string>Version 1.1.0</string>
|
||||
<string>Version 0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -217,9 +217,6 @@ p, li { white-space: pre-wrap; }
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="OGAgent.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
|
|
|
@ -4,20 +4,42 @@ address=0.0.0.0
|
|||
port=8000
|
||||
|
||||
# This is a comma separated list of paths where to look for modules to load
|
||||
path=test_modules/server
|
||||
#path=test_modules/server,more_modules/server
|
||||
|
||||
# Remote OpenGnsys Service
|
||||
remote=https://192.168.2.10/opengnsys/rest
|
||||
remote=https://192.168.2.1/opengnsys/rest
|
||||
# Alternate OpenGnsys Service (comment out to enable this option)
|
||||
#altremote=https://10.0.2.2/opengnsys/rest
|
||||
|
||||
# Log Level, if ommited, will be set to INFO
|
||||
# Execution level (permitted operations): status, halt, full
|
||||
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
|
||||
#[Sample1]
|
||||
#value1=Mariete
|
||||
#value2=Yo
|
||||
#remote=https://172.27.0.1:9999/rest
|
||||
[ogAdmClient]
|
||||
#path=test_modules/server,more_modules/server
|
||||
|
||||
remote={}://{}/opengnsys/rest
|
||||
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
|
||||
|
|
|
@ -32,8 +32,9 @@
|
|||
|
||||
# pylint: disable-msg=E1101,W0703
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
import json
|
||||
|
@ -43,8 +44,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):
|
||||
|
@ -58,8 +59,12 @@ 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
|
||||
|
@ -84,13 +89,14 @@ class REST(object):
|
|||
the deserialized JSON result or raises an exception in case of error
|
||||
"""
|
||||
|
||||
def __init__(self, url):
|
||||
def __init__(self, url, ca_file=None, crt_file=None, key_file=None):
|
||||
"""
|
||||
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 += '/'
|
||||
|
@ -101,6 +107,52 @@ 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
|
||||
|
@ -131,20 +183,39 @@ 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:
|
||||
r = requests.get(url, verify=VERIFY_CERT, timeout=TIMEOUT)
|
||||
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)
|
||||
else:
|
||||
r = requests.get(url)
|
||||
else: # POST
|
||||
logger.debug('Requesting using POST {}, data: {}'.format(url, data))
|
||||
if self.newerRequestLib:
|
||||
r = requests.post(url, data=data, headers={'content-type': 'application/json'},
|
||||
verify=VERIFY_CERT, timeout=TIMEOUT)
|
||||
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)
|
||||
else:
|
||||
r = requests.post(url, data=data, headers={'content-type': 'application/json'})
|
||||
|
||||
r.raise_for_status()
|
||||
ct = r.headers['Content-Type']
|
||||
if 'application/json' != ct:
|
||||
raise Exception (f'response content-type is not "application/json" but "{ct}"')
|
||||
r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise ConnectionError(e)
|
||||
code = e.response.status_code
|
||||
logger.warning (f'request failed, HTTP code "{code}"')
|
||||
return None
|
||||
except Exception as e:
|
||||
raise ConnectionError(exceptionToMessage(e))
|
||||
|
||||
|
@ -156,12 +227,17 @@ 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))
|
||||
|
||||
return self._request(url, data)
|
||||
try:
|
||||
res = self._request(url, data)
|
||||
return res
|
||||
except:
|
||||
logger.exception()
|
||||
return None
|
||||
|
|
|
@ -29,19 +29,15 @@
|
|||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
import modules
|
||||
from RESTApi import REST, RESTError
|
||||
from . import modules
|
||||
from .RESTApi import REST, RESTError
|
||||
|
||||
try:
|
||||
with open('../VERSION', 'r') as v:
|
||||
VERSION = v.read().strip()
|
||||
except IOError:
|
||||
VERSION = '1.1.1b'
|
||||
VERSION='0'
|
||||
|
||||
__title__ = 'OpenGnsys Agent'
|
||||
__version__ = VERSION
|
||||
|
|
|
@ -26,25 +26,26 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
"""
|
||||
# pylint: disable=unused-wildcard-import, wildcard-import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ConfigParser import SafeConfigParser
|
||||
|
||||
from configparser import ConfigParser
|
||||
|
||||
config = None
|
||||
|
||||
|
||||
def readConfig(client=False):
|
||||
'''
|
||||
"""
|
||||
Reads configuration file
|
||||
If client is False, will read ogagent.cfg as configuration
|
||||
If client is True, will read ogclient.cfg as configuration
|
||||
|
||||
This is this way so we can protect ogagent.cfg against reading for non admin users on all platforms.
|
||||
'''
|
||||
cfg = SafeConfigParser()
|
||||
"""
|
||||
cfg = ConfigParser()
|
||||
if client is True:
|
||||
fname = 'ogclient.cfg'
|
||||
else:
|
||||
|
@ -55,4 +56,3 @@ def readConfig(client=False):
|
|||
return None
|
||||
|
||||
return cfg
|
||||
|
|
@ -25,27 +25,26 @@
|
|||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
from __future__ import unicode_literals, print_function
|
||||
"""
|
||||
|
||||
# Pydev can't parse "six.moves.xxxx" because it is loaded lazy
|
||||
import six
|
||||
|
||||
import os
|
||||
import json
|
||||
import ssl
|
||||
import threading
|
||||
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
|
||||
|
||||
import json
|
||||
import threading
|
||||
import ssl
|
||||
|
||||
from .utils import exceptionToMessage
|
||||
from .certs import createSelfSignedCert
|
||||
from .log import logger
|
||||
|
||||
VERIFY_TLS=True
|
||||
|
||||
class HTTPServerHandler(BaseHTTPRequestHandler):
|
||||
service = None
|
||||
protocol_version = 'HTTP/1.0'
|
||||
|
@ -56,22 +55,23 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
|
|||
self.send_response(code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'error': message}))
|
||||
self.wfile.write(str.encode(json.dumps({'error': message})))
|
||||
return
|
||||
|
||||
def sendJsonResponse(self, data):
|
||||
self.send_response(200)
|
||||
try: self.send_response(200)
|
||||
except Exception as e: logger.warn ('exception: "{}"'.format(str(e)))
|
||||
data = json.dumps(data)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', len(data))
|
||||
self.send_header('Content-Length', str(len(data)))
|
||||
self.end_headers()
|
||||
# Send the html message
|
||||
self.wfile.write(data)
|
||||
self.wfile.write(str.encode(data))
|
||||
|
||||
|
||||
# parseURL
|
||||
def parseUrl(self):
|
||||
# Very simple path & params splitter
|
||||
"""
|
||||
Very simple path & params splitter
|
||||
"""
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
|
||||
try:
|
||||
|
@ -79,22 +79,46 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
|
|||
except Exception:
|
||||
params = {}
|
||||
|
||||
## quick override because universities do not actually want the module to be extracted out of the URL
|
||||
module = 'ogAdmClient' if os.path.exists ('/scripts/oginit') else 'opengnsys'
|
||||
|
||||
for v in self.service.modules:
|
||||
if v.name == path[0]: # Case Sensitive!!!!
|
||||
return (v, path[1:], params)
|
||||
if v.name == module: # Case Sensitive!!!!
|
||||
return v, path[1:], params
|
||||
|
||||
return (None, path, params)
|
||||
return None, path, params
|
||||
|
||||
def notifyMessage(self, module, path, getParams, postParams):
|
||||
'''
|
||||
def notifyMessage(self, module, path, get_params, post_params):
|
||||
"""
|
||||
Locates witch module will process the message based on path (first folder on url path)
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
data = module.processServerMessage(path, getParams, postParams, self)
|
||||
if module is None:
|
||||
raise Exception ({ '_httpcode': 404, '_msg': f'Module {path[0]} not found' })
|
||||
data = module.processServerMessage(path, get_params, post_params, self)
|
||||
self.sendJsonResponse(data)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
n_args = len (e.args)
|
||||
if 0 == n_args:
|
||||
logger.debug ('Empty exception raised from message processor for "{}"'.format(path[0]))
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
else:
|
||||
arg0 = e.args[0]
|
||||
if type (arg0) is str:
|
||||
logger.debug ('Message processor for "{}" returned exception string "{}"'.format(path[0], str(e)))
|
||||
self.sendJsonError (500, exceptionToMessage(e))
|
||||
elif type (arg0) is dict:
|
||||
if '_httpcode' in arg0:
|
||||
logger.debug ('Message processor for "{}" returned HTTP code "{}" with exception string "{}"'.format(path[0], str(arg0['_httpcode']), str(arg0['_msg'])))
|
||||
self.sendJsonError (arg0['_httpcode'], arg0['_msg'])
|
||||
else:
|
||||
logger.debug ('Message processor for "{}" returned exception dict "{}" with no HTTP code'.format(path[0], str(e)))
|
||||
self.sendJsonError (500, exceptionToMessage(e))
|
||||
else:
|
||||
logger.debug ('Message processor for "{}" returned non-string and non-dict exception "{}", type "{}"'.format(path[0], str(e), type(e)))
|
||||
self.sendJsonError (500, exceptionToMessage(e))
|
||||
## not reached
|
||||
|
||||
def do_GET(self):
|
||||
module, path, params = self.parseUrl()
|
||||
|
@ -102,39 +126,73 @@ class HTTPServerHandler(BaseHTTPRequestHandler):
|
|||
self.notifyMessage(module, path, params, None)
|
||||
|
||||
def do_POST(self):
|
||||
module, path, getParams = self.parseUrl()
|
||||
module, path, get_params = self.parseUrl()
|
||||
post_params = None
|
||||
|
||||
# Tries to get JSON content (UTF-8 encoded)
|
||||
try:
|
||||
length = int(self.headers.getheader('content-length'))
|
||||
length = int(self.headers.get('content-length'))
|
||||
content = self.rfile.read(length).decode('utf-8')
|
||||
logger.debug('length: {}, content >>{}<<'.format(length, content))
|
||||
postParams = json.loads(content)
|
||||
logger.debug('length: {0}, content >>{1}<<'.format(length, content))
|
||||
post_params = json.loads(content)
|
||||
except Exception as e:
|
||||
self.sendJsonError(500, exceptionToMessage(e))
|
||||
|
||||
self.notifyMessage(module, path, getParams, postParams)
|
||||
|
||||
self.notifyMessage(module, path, get_params, post_params)
|
||||
|
||||
def log_error(self, fmt, *args):
|
||||
logger.error('HTTP ' + fmt % args)
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
logger.info('HTTP ' + fmt % args)
|
||||
logger.debug('HTTP ' + fmt % args)
|
||||
|
||||
|
||||
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
|
||||
pass
|
||||
|
||||
|
||||
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)
|
||||
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
|
||||
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 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')
|
||||
self.server.socket = context.wrap_socket(self.server.socket, server_side=True)
|
||||
|
||||
logger.debug('Initialized HTTPS Server thread on {}'.format(address))
|
||||
|
||||
|
@ -146,5 +204,3 @@ class HTTPServerThread(threading.Thread):
|
|||
|
||||
def run(self):
|
||||
self.server.serve_forever()
|
||||
|
||||
|
||||
|
|
|
@ -25,16 +25,16 @@
|
|||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
import queue
|
||||
import socket
|
||||
import threading
|
||||
import six
|
||||
import traceback
|
||||
import json
|
||||
|
||||
from opengnsys.utils import toUnicode
|
||||
from opengnsys.log import logger
|
||||
|
@ -59,7 +59,7 @@ from opengnsys.log import logger
|
|||
# BYTE
|
||||
# 0 1-2 3 4 ...
|
||||
# MSG_ID DATA_LENGTH (little endian) Data (can be 0 length)
|
||||
# With a previos "MAGIC" header in fron of each message
|
||||
# With a previous "MAGIC" header in front of each message
|
||||
|
||||
# Client messages
|
||||
MSG_LOGOFF = 0xA1 # Request log off from an user
|
||||
|
@ -84,7 +84,7 @@ REV_DICT = {
|
|||
REQ_MESSAGE: 'REQ_MESSAGE'
|
||||
}
|
||||
|
||||
MAGIC = b'\x4F\x47\x41\x00' # OGA in hexa with a padded 0 to the right
|
||||
MAGIC = b'\x4F\x47\x41\x00' # OGA in hex with a padded 0 to the right
|
||||
|
||||
|
||||
# States for client processor
|
||||
|
@ -99,10 +99,10 @@ class ClientProcessor(threading.Thread):
|
|||
self.parent = parent
|
||||
self.clientSocket = clientSocket
|
||||
self.running = False
|
||||
self.messages = six.moves.queue.Queue(32) # @UndefinedVariable
|
||||
self.messages = queue.Queue(32)
|
||||
|
||||
def stop(self):
|
||||
logger.debug('Stoping client processor')
|
||||
logger.debug('Stopping client processor')
|
||||
self.running = False
|
||||
|
||||
def processRequest(self, msg, data):
|
||||
|
@ -117,6 +117,7 @@ class ClientProcessor(threading.Thread):
|
|||
state = None
|
||||
recv_msg = None
|
||||
recv_data = None
|
||||
msg_len = 0
|
||||
while self.running:
|
||||
try:
|
||||
counter = 1024
|
||||
|
@ -127,7 +128,7 @@ class ClientProcessor(threading.Thread):
|
|||
# Client disconnected
|
||||
self.running = False
|
||||
break
|
||||
buf = six.byte2int(b) # Empty buffer, this is set as non-blocking
|
||||
buf = int.from_bytes(b, 'big') # Empty buffer, this is set as non-blocking
|
||||
if state is None:
|
||||
if buf in (REQ_MESSAGE, REQ_LOGIN, REQ_LOGOUT):
|
||||
logger.debug('State set to {}'.format(buf))
|
||||
|
@ -152,7 +153,7 @@ class ClientProcessor(threading.Thread):
|
|||
recv_data = b''
|
||||
continue
|
||||
elif state == ST_RECEIVING:
|
||||
recv_data += six.int2byte(buf)
|
||||
recv_data += bytes([buf])
|
||||
msg_len -= 1
|
||||
if msg_len == 0:
|
||||
self.processRequest(recv_msg, recv_data)
|
||||
|
@ -173,7 +174,7 @@ class ClientProcessor(threading.Thread):
|
|||
|
||||
try:
|
||||
msg = self.messages.get(block=True, timeout=1)
|
||||
except six.moves.queue.Empty: # No message got in time @UndefinedVariable
|
||||
except queue.Empty: # No message got in time @UndefinedVariable
|
||||
continue
|
||||
|
||||
logger.debug('Got message {}={}'.format(msg, REV_DICT.get(msg[0])))
|
||||
|
@ -181,7 +182,7 @@ class ClientProcessor(threading.Thread):
|
|||
try:
|
||||
m = msg[1] if msg[1] is not None else b''
|
||||
l = len(m)
|
||||
data = MAGIC + six.int2byte(msg[0]) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + m
|
||||
data = MAGIC + bytes([msg[0]]) + bytes([l & 0xFF]) + bytes([l >> 8]) + m
|
||||
try:
|
||||
self.clientSocket.sendall(data)
|
||||
except socket.error as e:
|
||||
|
@ -220,20 +221,20 @@ class ServerIPC(threading.Thread):
|
|||
for t in self.threads:
|
||||
t.join()
|
||||
|
||||
def sendMessage(self, msgId, msgData):
|
||||
'''
|
||||
def sendMessage(self, msg_id, msg_data):
|
||||
"""
|
||||
Notify message to all listening threads
|
||||
'''
|
||||
logger.debug('Sending message {}({}),{} to all clients'.format(msgId, REV_DICT.get(msgId), msgData))
|
||||
"""
|
||||
logger.debug('Sending message {}({}),{} to all clients'.format(msg_id, REV_DICT.get(msg_id), msg_data))
|
||||
|
||||
# Convert to bytes so length is correctly calculated
|
||||
if isinstance(msgData, six.text_type):
|
||||
msgData = msgData.encode('utf8')
|
||||
if isinstance(msg_data, str):
|
||||
msg_data = str.encode(msg_data)
|
||||
|
||||
for t in self.threads:
|
||||
if t.isAlive():
|
||||
if t.is_alive():
|
||||
logger.debug('Sending to {}'.format(t))
|
||||
t.messages.put((msgId, msgData))
|
||||
t.messages.put((msg_id, msg_data))
|
||||
|
||||
def sendLoggofMessage(self):
|
||||
self.sendMessage(MSG_LOGOFF, '')
|
||||
|
@ -242,18 +243,18 @@ class ServerIPC(threading.Thread):
|
|||
self.sendMessage(MSG_MESSAGE, message)
|
||||
|
||||
def sendPopupMessage(self, title, message):
|
||||
self.sendMessage(MSG_POPUP, {'title':title, 'message':message})
|
||||
self.sendMessage(MSG_POPUP, {'title': title, 'message': message})
|
||||
|
||||
def sendScriptMessage(self, script):
|
||||
self.sendMessage(MSG_SCRIPT, script)
|
||||
|
||||
def cleanupFinishedThreads(self):
|
||||
'''
|
||||
"""
|
||||
Cleans up current threads list
|
||||
'''
|
||||
"""
|
||||
aliveThreads = []
|
||||
for t in self.threads:
|
||||
if t.isAlive():
|
||||
if t.is_alive():
|
||||
logger.debug('Thread {} is alive'.format(t))
|
||||
aliveThreads.append(t)
|
||||
self.threads[:] = aliveThreads
|
||||
|
@ -262,7 +263,7 @@ class ServerIPC(threading.Thread):
|
|||
self.running = True
|
||||
|
||||
self.serverSocket.bind(('localhost', self.port))
|
||||
self.serverSocket.setblocking(1)
|
||||
self.serverSocket.setblocking(True)
|
||||
self.serverSocket.listen(4)
|
||||
|
||||
while True:
|
||||
|
@ -289,7 +290,7 @@ class ClientIPC(threading.Thread):
|
|||
self.port = listenPort
|
||||
self.running = False
|
||||
self.clientSocket = None
|
||||
self.messages = six.moves.queue.Queue(32) # @UndefinedVariable
|
||||
self.messages = queue.Queue(32) # @UndefinedVariable
|
||||
|
||||
self.connect()
|
||||
|
||||
|
@ -300,7 +301,7 @@ class ClientIPC(threading.Thread):
|
|||
while self.running:
|
||||
try:
|
||||
return self.messages.get(timeout=1)
|
||||
except six.moves.queue.Empty: # @UndefinedVariable
|
||||
except queue.Empty:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
@ -310,11 +311,11 @@ class ClientIPC(threading.Thread):
|
|||
if data is None:
|
||||
data = b''
|
||||
|
||||
if isinstance(data, six.text_type): # Convert to bytes if necessary
|
||||
data = data.encode('utf-8')
|
||||
if isinstance(data, str):
|
||||
data = str.encode(data)
|
||||
|
||||
l = len(data)
|
||||
msg = six.int2byte(msg) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + data
|
||||
msg = bytes([msg]) + bytes([l & 0xFF]) + bytes([l >> 8]) + data
|
||||
self.clientSocket.sendall(msg)
|
||||
|
||||
def sendLogin(self, user_data):
|
||||
|
@ -324,20 +325,20 @@ class ClientIPC(threading.Thread):
|
|||
self.sendRequestMessage(REQ_LOGOUT, username)
|
||||
|
||||
def sendMessage(self, module, message, data=None):
|
||||
'''
|
||||
"""
|
||||
Sends a message "message" with data (data will be encoded as json, so ensure that it is serializable)
|
||||
@param module: Module that will receive this message
|
||||
@param message: Message to send. This message is "customized", and understand by modules
|
||||
@param data: Data to be send as message companion
|
||||
'''
|
||||
"""
|
||||
msg = '\0'.join((module, message, json.dumps(data)))
|
||||
self.sendRequestMessage(REQ_MESSAGE, msg)
|
||||
|
||||
def messageReceived(self):
|
||||
'''
|
||||
"""
|
||||
Override this method to automatically get notified on new message
|
||||
received. Message is at self.messages queue
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
def receiveBytes(self, number):
|
||||
|
@ -420,4 +421,3 @@ class ClientIPC(threading.Thread):
|
|||
self.clientSocket.close()
|
||||
except Exception:
|
||||
pass # If can't close, nothing happens, just end thread
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import threading
|
||||
import subprocess
|
||||
import hashlib
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from opengnsys import operations
|
||||
from opengnsys.log import logger
|
||||
|
||||
def job_readstdout(job):
|
||||
for l in iter(job['p'].stdout.readline, b''):
|
||||
job['stdout'] += l.decode ('utf-8', 'ignore')
|
||||
|
||||
def job_readstderr(job):
|
||||
for l in iter(job['p'].stderr.readline, b''):
|
||||
job['stderr'] += l.decode ('utf-8', 'ignore')
|
||||
|
||||
class JobMgr():
|
||||
jobs = {}
|
||||
|
||||
def launch_job(self, script, is_client):
|
||||
logger.debug ('in launch_job(), is_client "{}"'.format(is_client))
|
||||
args = operations.build_popen_args (script)
|
||||
logger.debug ('args "{}"'.format (args))
|
||||
now = datetime.now (tz=timezone.utc)
|
||||
ts = now.strftime ('%Y-%m-%d %H:%M:%S.%f%z') ## '%s' doesn't work on windows
|
||||
jobid = 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[jobid] = { 'p': p, 'pid': p.pid, 'starttime': ts, 'script': script, 'client': is_client, 'status': 'running', 'stdout': '', 'stderr': '' }
|
||||
self.jobs[jobid]['t1'] = threading.Thread (target=job_readstdout, args=(self.jobs[jobid],))
|
||||
self.jobs[jobid]['t2'] = threading.Thread (target=job_readstderr, args=(self.jobs[jobid],))
|
||||
self.jobs[jobid]['t1'].start()
|
||||
self.jobs[jobid]['t2'].start()
|
||||
logger.debug ('jobs "{}"'.format (self.jobs))
|
||||
return jobid
|
||||
|
||||
def prepare_jobs(self):
|
||||
## can't return self.jobs because the Popen object at self.jobs[id]['p'] is not serializable. So, need to create a new dict to return
|
||||
st = []
|
||||
for jobid in self.jobs:
|
||||
j = self.jobs[jobid]
|
||||
entry = dict ((k, j[k]) for k in ['pid', 'starttime', 'script', 'client', 'status', 'stdout', 'stderr'])
|
||||
entry['jobid'] = jobid
|
||||
if j['p'].poll() is not None: ## process finished
|
||||
entry['rc'] = j['p'].returncode
|
||||
entry['status'] = 'finished'
|
||||
st.append (entry)
|
||||
return st
|
||||
|
||||
def terminate_job(self, jobid):
|
||||
if jobid not in self.jobs: return {}
|
||||
p = self.jobs[jobid]['p']
|
||||
p.terminate()
|
||||
time.sleep (1)
|
||||
if p.poll() is not None:
|
||||
return { 'terminated': True }
|
||||
|
||||
p.kill()
|
||||
time.sleep (1)
|
||||
if p.poll() is not None:
|
||||
return { 'killed': True }
|
||||
|
||||
return { 'killed': False }
|
|
@ -26,10 +26,10 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
|
||||
|
||||
from opengnsys.service import CommonService
|
||||
from opengnsys.service import IPC_PORT
|
||||
|
@ -45,7 +45,7 @@ import json
|
|||
|
||||
try:
|
||||
from prctl import set_proctitle # @UnresolvedImport
|
||||
except Exception: # Platform may not include prctl, so in case it's not available, we let the "name" as is
|
||||
except ImportError: # Platform may not include prctl, so in case it's not available, we let the "name" as is
|
||||
def set_proctitle(_):
|
||||
pass
|
||||
|
||||
|
@ -64,7 +64,6 @@ class OGAgentSvc(Daemon, CommonService):
|
|||
# Call modules initialization
|
||||
# They are called in sequence, no threading is done at this point, so ensure modules onActivate always returns
|
||||
|
||||
|
||||
# *********************
|
||||
# * Main Service loop *
|
||||
# *********************
|
||||
|
@ -93,6 +92,7 @@ def usage():
|
|||
sys.stderr.write("usage: {} start|stop|restart|fg|login 'username'|logout 'username'|message 'module' 'message' 'json'\n".format(sys.argv[0]))
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.setLevel('INFO')
|
||||
|
||||
|
@ -106,7 +106,6 @@ if __name__ == '__main__':
|
|||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
if len(sys.argv) == 3 and sys.argv[1] in ('login', 'logout'):
|
||||
logger.debug('Running client opengnsys')
|
||||
client = None
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
|
|
|
@ -25,18 +25,16 @@
|
|||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
"""
|
||||
@author: : http://www.jejik.com/authors/sander_marechal/
|
||||
@see: : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
|
||||
'''
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import atexit
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from opengnsys.log import logger
|
||||
|
||||
from signal import SIGTERM
|
||||
|
||||
|
||||
|
@ -89,7 +87,7 @@ class Daemon:
|
|||
sys.stderr.flush()
|
||||
si = open(self.stdin, 'r')
|
||||
so = open(self.stdout, 'a+')
|
||||
se = open(self.stderr, 'a+', 0)
|
||||
se = open(self.stderr, 'a+')
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
|
|
@ -26,18 +26,18 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import six
|
||||
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log)
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in six.moves.xrange(6)) # @UndefinedVariable
|
||||
from ..log_format import JsonFormatter
|
||||
|
||||
# Logging levels
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
|
||||
|
||||
|
||||
class LocalLogger(object):
|
||||
|
@ -46,19 +46,29 @@ class LocalLogger(object):
|
|||
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
|
||||
# Try to open logger at /var/log path
|
||||
# If it fails (access denied normally), will try to open one at user's home folder, and if
|
||||
# agaim it fails, open it at the tmpPath
|
||||
# again it fails, open it at the tmpPath
|
||||
|
||||
for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()):
|
||||
try:
|
||||
fname = os.path.join(logDir, 'opengnsys.log')
|
||||
logging.basicConfig(
|
||||
filename=fname,
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
self.logger = logging.getLogger('opengnsys')
|
||||
os.chmod(fname, 0o0600)
|
||||
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)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
@ -71,7 +81,7 @@ class LocalLogger(object):
|
|||
# our loglevels are 10000 (other), 20000 (debug), ....
|
||||
# logging levels are 10 (debug), 20 (info)
|
||||
# OTHER = logging.NOTSET
|
||||
self.logger.log(int(level / 1000) - 10, message)
|
||||
self.logger.log(int(level / 1000) - 10, message, stacklevel=4)
|
||||
|
||||
def isWindows(self):
|
||||
return False
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import socket
|
||||
import platform
|
||||
|
@ -44,7 +44,6 @@ import array
|
|||
import six
|
||||
import distro
|
||||
from opengnsys import utils
|
||||
from .renamer import rename
|
||||
|
||||
|
||||
def _getMacAddr(ifname):
|
||||
|
@ -104,7 +103,7 @@ def _getInterfaces():
|
|||
0x8912, # SIOCGIFCONF
|
||||
struct.pack(str('iL'), space, names.buffer_info()[0])
|
||||
))[0]
|
||||
namestr = names.tostring()
|
||||
namestr = names.tobytes()
|
||||
# return namestr, outbytes
|
||||
return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)]
|
||||
|
||||
|
@ -114,13 +113,6 @@ def _getIpAndMac(ifname):
|
|||
return (ip, mac)
|
||||
|
||||
|
||||
def getComputerName():
|
||||
'''
|
||||
Returns computer name, with no domain
|
||||
'''
|
||||
return socket.gethostname().split('.')[0]
|
||||
|
||||
|
||||
def getNetworkInfo():
|
||||
'''
|
||||
Obtains a list of network interfaces
|
||||
|
@ -135,10 +127,6 @@ def getNetworkInfo():
|
|||
yield utils.Bunch(name=ifname, mac=mac, ip=ip)
|
||||
|
||||
|
||||
def getDomainName():
|
||||
return ''
|
||||
|
||||
|
||||
def getLinuxVersion():
|
||||
"""
|
||||
Returns the version of the Linux distribution
|
||||
|
@ -191,83 +179,6 @@ def logoff():
|
|||
subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']])
|
||||
|
||||
|
||||
def renameComputer(newName):
|
||||
rename(newName)
|
||||
|
||||
|
||||
def joinDomain(domain, ou, account, password, executeInOneStep=False):
|
||||
pass
|
||||
|
||||
|
||||
def changeUserPassword(user, oldPassword, newPassword):
|
||||
'''
|
||||
Simple password change for user using command line
|
||||
'''
|
||||
os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword))
|
||||
|
||||
|
||||
class XScreenSaverInfo(ctypes.Structure):
|
||||
_fields_ = [('window', ctypes.c_long),
|
||||
('state', ctypes.c_int),
|
||||
('kind', ctypes.c_int),
|
||||
('til_or_since', ctypes.c_ulong),
|
||||
('idle', ctypes.c_ulong),
|
||||
('eventMask', ctypes.c_ulong)]
|
||||
|
||||
# Initialize xlib & xss
|
||||
try:
|
||||
xlibPath = ctypes.util.find_library('X11')
|
||||
xssPath = ctypes.util.find_library('Xss')
|
||||
xlib = ctypes.cdll.LoadLibrary(xlibPath)
|
||||
xss = ctypes.cdll.LoadLibrary(xssPath)
|
||||
|
||||
# Fix result type to XScreenSaverInfo Structure
|
||||
xss.XScreenSaverQueryExtension.restype = ctypes.c_int
|
||||
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure
|
||||
except Exception: # Libraries not accesible, not found or whatever..
|
||||
xlib = xss = None
|
||||
|
||||
|
||||
def initIdleDuration(atLeastSeconds):
|
||||
'''
|
||||
On linux we set the screensaver to at least required seconds, or we never will get "idle"
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)])
|
||||
# And now reset it
|
||||
subprocess.call(['/usr/bin/xset', 's', 'reset'])
|
||||
|
||||
|
||||
def getIdleDuration():
|
||||
'''
|
||||
Returns idle duration, in seconds
|
||||
'''
|
||||
if xlib is None or xss is None:
|
||||
return 0 # Libraries not available
|
||||
|
||||
# production code might want to not hardcode the offset 16...
|
||||
display = xlib.XOpenDisplay(None)
|
||||
|
||||
event_base = ctypes.c_int()
|
||||
error_base = ctypes.c_int()
|
||||
|
||||
available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base))
|
||||
if available != 1:
|
||||
return 0 # No screen saver is available, no way of getting idle
|
||||
|
||||
info = xss.XScreenSaverAllocInfo()
|
||||
xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info)
|
||||
|
||||
if info.contents.state != 0:
|
||||
return 3600 * 100 * 1000 # If screen saver is active, return a high enough value
|
||||
|
||||
return info.contents.idle / 1000.0
|
||||
|
||||
|
||||
def getCurrentUser():
|
||||
'''
|
||||
Returns current logged in user
|
||||
|
@ -303,3 +214,7 @@ def get_etc_path():
|
|||
Returns etc directory path.
|
||||
"""
|
||||
return os.sep + 'etc'
|
||||
|
||||
|
||||
def build_popen_args(script):
|
||||
return ['/bin/sh', '-c', script]
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import platform
|
||||
import os
|
||||
import sys
|
||||
import pkgutil
|
||||
|
||||
from opengnsys.log import logger
|
||||
|
||||
renamers = {}
|
||||
|
||||
|
||||
# Renamers now are for IPv4 only addresses
|
||||
def rename(newName):
|
||||
distribution = platform.linux_distribution()[0].lower().strip()
|
||||
if distribution in renamers:
|
||||
return renamers[distribution](newName)
|
||||
|
||||
# Try Debian renamer, simplest one
|
||||
logger.info('Renamer for platform "{0}" not found, tryin debian renamer'.format(distribution))
|
||||
return renamers['debian'](newName)
|
||||
|
||||
|
||||
# Do load of packages
|
||||
def _init():
|
||||
pkgpath = os.path.dirname(sys.modules[__name__].__file__)
|
||||
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
||||
__import__(__name__ + '.' + name, globals(), locals())
|
||||
|
||||
_init()
|
|
@ -1,68 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from opengnsys.linux.renamer import renamers
|
||||
from opengnsys.log import logger
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def rename(newName):
|
||||
'''
|
||||
Debian renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using Debian renamer')
|
||||
|
||||
with open('/etc/hostname', 'w') as hostname:
|
||||
hostname.write(newName)
|
||||
|
||||
# Force system new name
|
||||
os.system('/bin/hostname %s' % newName)
|
||||
|
||||
# add name to "hosts"
|
||||
with open('/etc/hosts', 'r') as hosts:
|
||||
lines = hosts.readlines()
|
||||
with open('/etc/hosts', 'w') as hosts:
|
||||
hosts.write("127.0.1.1\t%s\n" % newName)
|
||||
for l in lines:
|
||||
if l[:9] == '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
continue
|
||||
hosts.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['debian'] = rename
|
||||
renamers['ubuntu'] = rename
|
|
@ -1,66 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from opengnsys.linux.renamer import renamers
|
||||
from opengnsys.log import logger
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def rename(newName):
|
||||
'''
|
||||
RH, Centos, Fedora Renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using SUSE renamer')
|
||||
|
||||
with open('/etc/hostname', 'w') as hostname:
|
||||
hostname.write(newName)
|
||||
|
||||
# Force system new name
|
||||
os.system('/bin/hostname %s' % newName)
|
||||
|
||||
# add name to "hosts"
|
||||
with open('/etc/hosts', 'r') as hosts:
|
||||
lines = hosts.readlines()
|
||||
with open('/etc/hosts', 'w') as hosts:
|
||||
hosts.write("127.0.1.1\t{}\n".format(newName))
|
||||
for l in lines:
|
||||
if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
hosts.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['opensuse'] = rename
|
||||
renamers['suse'] = rename
|
|
@ -1,74 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from opengnsys.linux.renamer import renamers
|
||||
from opengnsys.log import logger
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def rename(newName):
|
||||
'''
|
||||
RH, Centos, Fedora Renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using RH renamer')
|
||||
|
||||
with open('/etc/hostname', 'w') as hostname:
|
||||
hostname.write(newName)
|
||||
|
||||
# Force system new name
|
||||
os.system('/bin/hostname %s' % newName)
|
||||
|
||||
# add name to "hosts"
|
||||
with open('/etc/hosts', 'r') as hosts:
|
||||
lines = hosts.readlines()
|
||||
with open('/etc/hosts', 'w') as hosts:
|
||||
hosts.write("127.0.1.1\t{}\n".format(newName))
|
||||
for l in lines:
|
||||
if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
hosts.write(l)
|
||||
|
||||
with open('/etc/sysconfig/network', 'r') as net:
|
||||
lines = net.readlines()
|
||||
with open('/etc/sysconfig/network', 'w') as net:
|
||||
net.write('HOSTNAME={}\n'.format(newName))
|
||||
for l in lines:
|
||||
if l[:8] != 'HOSTNAME':
|
||||
net.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['centos linux'] = rename
|
||||
renamers['fedora'] = rename
|
|
@ -33,8 +33,9 @@
|
|||
|
||||
# This is a simple module loader, so we can add "external opengnsys" modules as addons
|
||||
# Modules under "opengsnsys/modules" are always autoloaded
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import sys
|
||||
import pkgutil
|
||||
import os.path
|
||||
|
||||
|
@ -42,6 +43,8 @@ from opengnsys.workers import ServerWorker
|
|||
from opengnsys.workers import ClientWorker
|
||||
from .log import logger
|
||||
|
||||
PY3_12 = sys.version_info[0:2] >= (3, 12)
|
||||
|
||||
|
||||
def loadModules(controller, client=False):
|
||||
'''
|
||||
|
@ -55,12 +58,10 @@ def loadModules(controller, client=False):
|
|||
ogModules = []
|
||||
|
||||
if client is False:
|
||||
from opengnsys.modules.server import OpenGnSys # @UnusedImport
|
||||
from .modules import server # @UnusedImport, just used to ensure opengnsys modules are initialized
|
||||
modPath = 'opengnsys.modules.server'
|
||||
modType = ServerWorker
|
||||
else:
|
||||
from opengnsys.modules.client import OpenGnSys # @UnusedImport @Reimport
|
||||
from .modules import client # @UnusedImport, just used to ensure opengnsys modules are initialized
|
||||
modPath = 'opengnsys.modules.client'
|
||||
modType = ClientWorker
|
||||
|
@ -90,7 +91,11 @@ def loadModules(controller, client=False):
|
|||
for (module_loader, name, ispkg) in pkgutil.iter_modules(paths, modPath + '.'):
|
||||
if ispkg:
|
||||
logger.debug('Found module package {}'.format(name))
|
||||
module_loader.find_module(name).load_module(name)
|
||||
if PY3_12:
|
||||
loader = module_loader.find_spec(name).loader
|
||||
else:
|
||||
loader = module_loader.find_module(name)
|
||||
loader.load_module(name)
|
||||
|
||||
|
||||
if controller.config.has_option('opengnsys', 'path') is True:
|
||||
|
@ -98,14 +103,14 @@ def loadModules(controller, client=False):
|
|||
else:
|
||||
paths = ()
|
||||
|
||||
# paths += (os.path.dirname(sys.modules[modPath].__file__),)
|
||||
|
||||
logger.debug('Loading modules from {}'.format(paths))
|
||||
paths += (os.path.dirname(sys.modules[modPath].__file__),)
|
||||
|
||||
# Load modules
|
||||
logger.debug('Loading modules from {}'.format(paths))
|
||||
doLoad(paths)
|
||||
|
||||
# Add to list of available modules
|
||||
logger.debug('Adding {} classes'.format('server' if modType == ServerWorker else 'client'))
|
||||
recursiveAdd(modType)
|
||||
|
||||
return ogModules
|
||||
|
|
|
@ -26,22 +26,21 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
|
||||
|
||||
import traceback
|
||||
import sys
|
||||
import six
|
||||
import traceback
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from opengnsys.windows.log import LocalLogger # @UnusedImport
|
||||
from opengnsys.windows.log import LocalLogger
|
||||
else:
|
||||
from opengnsys.linux.log import LocalLogger # @Reimport
|
||||
from opengnsys.linux.log import LocalLogger
|
||||
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log)
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in six.moves.xrange(6)) # @UndefinedVariable
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
|
||||
|
||||
_levelName = {
|
||||
'OTHER': OTHER,
|
||||
|
@ -52,17 +51,18 @@ _levelName = {
|
|||
'FATAL': FATAL
|
||||
}
|
||||
|
||||
|
||||
class Logger(object):
|
||||
def __init__(self):
|
||||
self.logLevel = INFO
|
||||
self.logger = LocalLogger()
|
||||
|
||||
def setLevel(self, level):
|
||||
'''
|
||||
"""
|
||||
Sets log level filter (minimum level required for a log message to be processed)
|
||||
:param level: Any message with a level below this will be filtered out
|
||||
'''
|
||||
if isinstance(level, six.string_types):
|
||||
"""
|
||||
if isinstance(level, str):
|
||||
level = _levelName.get(level, INFO)
|
||||
|
||||
self.logLevel = level # Ensures level is an integer or fails
|
||||
|
@ -79,6 +79,9 @@ class Logger(object):
|
|||
def warn(self, message):
|
||||
self.log(WARN, message)
|
||||
|
||||
def warning(self, message):
|
||||
self.log(WARN, message)
|
||||
|
||||
def info(self, message):
|
||||
self.log(INFO, message)
|
||||
|
||||
|
@ -94,7 +97,7 @@ class Logger(object):
|
|||
except Exception:
|
||||
tb = '(could not get traceback!)'
|
||||
|
||||
self.log(DEBUG, tb)
|
||||
self.log(DEBUG, 'traceback follows: "{}"'.format(tb))
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
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)
|
|
@ -29,4 +29,4 @@
|
|||
'''
|
||||
@author: Ramón M. Gómez, ramongomez at us dot es
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import socket
|
||||
import platform
|
||||
|
@ -42,45 +42,69 @@ import subprocess
|
|||
import struct
|
||||
import array
|
||||
import six
|
||||
import json
|
||||
from opengnsys import utils
|
||||
import netifaces
|
||||
|
||||
ip_a_s = None
|
||||
|
||||
## make sure /usr/local/bin is in PATH
|
||||
## we need that to run /usr/local/bin/ip, which uses 'env' and pulls from PATH anyway
|
||||
def check_path():
|
||||
path = os.getenv ('PATH', '')
|
||||
usr_local_bin = '/usr/local/bin'
|
||||
|
||||
if usr_local_bin not in path.split (os.pathsep):
|
||||
os.environ['PATH'] = usr_local_bin + os.pathsep + path
|
||||
|
||||
def _getMacAddr(ifname):
|
||||
'''
|
||||
Returns the mac address of an interface
|
||||
Mac is returned as unicode utf-8 encoded
|
||||
'''
|
||||
if isinstance(ifname, list):
|
||||
return dict([(name, _getMacAddr(name)) for name in ifname])
|
||||
if isinstance(ifname, six.text_type):
|
||||
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
|
||||
try:
|
||||
return netifaces.ifaddresses(ifname)[18][0]['addr']
|
||||
except Exception:
|
||||
return None
|
||||
for interface in ip_a_s:
|
||||
if interface.get ('ifname') != ifname: continue
|
||||
return interface.get ('address')
|
||||
return None
|
||||
|
||||
|
||||
def _getIpAddr(ifname):
|
||||
'''
|
||||
Returns the IP address of an interface
|
||||
Returns the first IP address of an interface
|
||||
IPv4 is preferred over IPv6
|
||||
IP is returned as unicode utf-8 encoded
|
||||
'''
|
||||
if isinstance(ifname, list):
|
||||
return dict([(name, _getIpAddr(name)) for name in ifname])
|
||||
if isinstance(ifname, six.text_type):
|
||||
ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7)
|
||||
try:
|
||||
return netifaces.ifaddresses(ifname)[2][0]['addr']
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
## loop and return the first IPv4 address found
|
||||
for interface in ip_a_s:
|
||||
if interface.get ('ifname') != ifname: continue
|
||||
for addr_info in interface.get ('addr_info', []):
|
||||
ip_address = addr_info.get ('local')
|
||||
try:
|
||||
ip_address.index ('.')
|
||||
return ip_address
|
||||
except: pass
|
||||
|
||||
## if nothing found, loop again and return the first IP found, which will be an IPv6
|
||||
for interface in ip_a_s:
|
||||
if interface.get ('ifname') != ifname: continue
|
||||
for addr_info in interface.get ('addr_info', []):
|
||||
return addr_info.get ('local')
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _getInterfaces():
|
||||
'''
|
||||
Returns a list of interfaces names
|
||||
'''
|
||||
return netifaces.interfaces()
|
||||
global ip_a_s
|
||||
|
||||
check_path()
|
||||
result = subprocess.run (['/usr/local/bin/ip', '-json', 'address', 'show'], capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0: raise Exception (f'Command "ip" failed with exit code {result.returncode}')
|
||||
ip_a_s = json.loads (result.stdout)
|
||||
return [i.get('ifname') for i in ip_a_s]
|
||||
|
||||
|
||||
def _getIpAndMac(ifname):
|
||||
|
@ -88,13 +112,6 @@ def _getIpAndMac(ifname):
|
|||
return (ip, mac)
|
||||
|
||||
|
||||
def getComputerName():
|
||||
'''
|
||||
Returns computer name, with no domain
|
||||
'''
|
||||
return socket.gethostname().split('.')[0]
|
||||
|
||||
|
||||
def getNetworkInfo():
|
||||
'''
|
||||
Obtains a list of network interfaces
|
||||
|
@ -109,10 +126,6 @@ def getNetworkInfo():
|
|||
yield utils.Bunch(name=ifname, mac=mac, ip=ip)
|
||||
|
||||
|
||||
def getDomainName():
|
||||
return ''
|
||||
|
||||
|
||||
def getMacosVersion():
|
||||
return 'macOS {}'.format(platform.mac_ver()[0])
|
||||
|
||||
|
@ -152,87 +165,10 @@ def logoff():
|
|||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
# Exec logout using AppleSctipt
|
||||
# Exec logout using AppleScript
|
||||
subprocess.call('/usr/bin/osascript -e \'tell app "System Events" to «event aevtrlgo»\'', shell=True)
|
||||
|
||||
|
||||
def renameComputer(newName):
|
||||
rename(newName)
|
||||
|
||||
|
||||
def joinDomain(domain, ou, account, password, executeInOneStep=False):
|
||||
pass
|
||||
|
||||
|
||||
def changeUserPassword(user, oldPassword, newPassword):
|
||||
'''
|
||||
Simple password change for user using command line
|
||||
'''
|
||||
os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword))
|
||||
|
||||
|
||||
class XScreenSaverInfo(ctypes.Structure):
|
||||
_fields_ = [('window', ctypes.c_long),
|
||||
('state', ctypes.c_int),
|
||||
('kind', ctypes.c_int),
|
||||
('til_or_since', ctypes.c_ulong),
|
||||
('idle', ctypes.c_ulong),
|
||||
('eventMask', ctypes.c_ulong)]
|
||||
|
||||
# Initialize xlib & xss
|
||||
try:
|
||||
xlibPath = ctypes.util.find_library('X11')
|
||||
xssPath = ctypes.util.find_library('Xss')
|
||||
xlib = ctypes.cdll.LoadLibrary(xlibPath)
|
||||
xss = ctypes.cdll.LoadLibrary(xssPath)
|
||||
|
||||
# Fix result type to XScreenSaverInfo Structure
|
||||
xss.XScreenSaverQueryExtension.restype = ctypes.c_int
|
||||
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure
|
||||
except Exception: # Libraries not accesible, not found or whatever..
|
||||
xlib = xss = None
|
||||
|
||||
|
||||
def initIdleDuration(atLeastSeconds):
|
||||
'''
|
||||
On linux we set the screensaver to at least required seconds, or we never will get "idle"
|
||||
'''
|
||||
# Workaround for dummy thread
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)])
|
||||
# And now reset it
|
||||
subprocess.call(['/usr/bin/xset', 's', 'reset'])
|
||||
|
||||
|
||||
def getIdleDuration():
|
||||
'''
|
||||
Returns idle duration, in seconds
|
||||
'''
|
||||
if xlib is None or xss is None:
|
||||
return 0 # Libraries not available
|
||||
|
||||
# production code might want to not hardcode the offset 16...
|
||||
display = xlib.XOpenDisplay(None)
|
||||
|
||||
event_base = ctypes.c_int()
|
||||
error_base = ctypes.c_int()
|
||||
|
||||
available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base))
|
||||
if available != 1:
|
||||
return 0 # No screen saver is available, no way of getting idle
|
||||
|
||||
info = xss.XScreenSaverAllocInfo()
|
||||
xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info)
|
||||
|
||||
if info.contents.state != 0:
|
||||
return 3600 * 100 * 1000 # If screen saver is active, return a high enough value
|
||||
|
||||
return info.contents.idle / 1000.0
|
||||
|
||||
|
||||
def getCurrentUser():
|
||||
'''
|
||||
Returns current logged in user
|
||||
|
@ -244,7 +180,19 @@ def getSessionLanguage():
|
|||
'''
|
||||
Returns the user's session language
|
||||
'''
|
||||
return locale.getdefaultlocale()[0]
|
||||
lang = locale.getdefaultlocale()[0]
|
||||
if lang is None:
|
||||
return 'C'
|
||||
else:
|
||||
return lang
|
||||
|
||||
|
||||
def get_session_type():
|
||||
"""
|
||||
Minimal implementation of this required function
|
||||
:return: string
|
||||
"""
|
||||
return 'unknown'
|
||||
|
||||
|
||||
def showPopup(title, message):
|
||||
|
@ -261,3 +209,7 @@ def get_etc_path():
|
|||
Returns etc directory path.
|
||||
"""
|
||||
return os.sep + 'etc'
|
||||
|
||||
|
||||
def build_popen_args(script):
|
||||
return ['/bin/sh', '-c', script]
|
||||
|
|
|
@ -28,41 +28,47 @@
|
|||
"""
|
||||
@author: Ramón M. Gómez, ramongomez at us dot es
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from opengnsys.workers import ClientWorker
|
||||
|
||||
from opengnsys import operations
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.scriptThread import ScriptExecutorThread
|
||||
from opengnsys.jobmgr import JobMgr
|
||||
|
||||
|
||||
class OpenGnSysWorker(ClientWorker):
|
||||
name = 'opengnsys'
|
||||
jobmgr = JobMgr()
|
||||
|
||||
@staticmethod
|
||||
def onActivation():
|
||||
def onActivation(self):
|
||||
logger.debug('Activate invoked')
|
||||
|
||||
@staticmethod
|
||||
def onDeactivation():
|
||||
def onDeactivation(self):
|
||||
logger.debug('Deactivate invoked')
|
||||
|
||||
# Processes script execution
|
||||
@staticmethod
|
||||
def process_script(json_params):
|
||||
logger.debug('Processed message: script({})'.format(json_params))
|
||||
thr = ScriptExecutorThread(json_params['code'])
|
||||
thr.start()
|
||||
def process_script(self, json_params):
|
||||
script = json_params['code']
|
||||
logger.debug('Processing message: script({})'.format(script))
|
||||
self.jobmgr.launch_job (script, True)
|
||||
#self.sendServerMessage('script', {'op', 'launched'})
|
||||
|
||||
@staticmethod
|
||||
def process_logoff(json_params):
|
||||
def process_terminatescript(self, json_params):
|
||||
jobid = json_params['jobid']
|
||||
logger.debug('Processing terminatescript request, jobid "{}"'.format (jobid))
|
||||
self.jobmgr.terminate_job (jobid)
|
||||
|
||||
def process_preparescripts(self, json_params):
|
||||
logger.debug('Processing preparescripts request')
|
||||
st = self.jobmgr.prepare_jobs()
|
||||
logger.debug('Sending preparescripts to server with data "{}"'.format(st))
|
||||
self.sendServerMessage('preparescripts', st)
|
||||
|
||||
def process_logoff(self, json_params):
|
||||
logger.debug('Processed message: logoff({})'.format(json_params))
|
||||
operations.logoff()
|
||||
|
||||
@staticmethod
|
||||
def process_popup(json_params):
|
||||
def process_popup(self, json_params):
|
||||
logger.debug('Processed message: popup({})'.format(json_params))
|
||||
ret = operations.showPopup(json_params['title'], json_params['message'])
|
||||
#self.sendServerMessage('popup', {'op', ret})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
|
@ -28,22 +29,23 @@
|
|||
"""
|
||||
@author: Ramón M. Gómez, ramongomez at us dot es
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import threading
|
||||
import base64
|
||||
import os
|
||||
import platform
|
||||
import time
|
||||
import random
|
||||
import shutil
|
||||
import string
|
||||
import urllib
|
||||
import threading
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from opengnsys.workers import ServerWorker
|
||||
from opengnsys import REST, RESTError
|
||||
from opengnsys import operations
|
||||
from configparser import NoOptionError
|
||||
from opengnsys import REST, operations, VERSION
|
||||
from opengnsys.log import logger
|
||||
from opengnsys.scriptThread import ScriptExecutorThread
|
||||
from opengnsys.jobmgr import JobMgr
|
||||
from opengnsys.workers import ServerWorker
|
||||
|
||||
|
||||
# Check authorization header decorator
|
||||
|
@ -53,55 +55,102 @@ def check_secret(fnc):
|
|||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
this, path, get_params, post_params, server = args # @UnusedVariable
|
||||
if this.random == server.headers['Authorization']:
|
||||
fnc(*args, **kwargs)
|
||||
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(e)
|
||||
logger.debug (str(e))
|
||||
raise Exception(e)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# Error handler decorator.
|
||||
def catch_background_error(fnc):
|
||||
def wrapper(*args, **kwargs):
|
||||
this = args[0]
|
||||
try:
|
||||
fnc(*args, **kwargs)
|
||||
except Exception as e:
|
||||
this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(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 OpenGnSysWorker(ServerWorker):
|
||||
name = 'opengnsys'
|
||||
name = 'opengnsys' # Module name
|
||||
interface = None # Bound interface for OpenGnsys
|
||||
REST = None # REST object
|
||||
logged_in = False # User session flag
|
||||
user = [] # User sessions
|
||||
session_type = '' # User session type
|
||||
locked = {}
|
||||
random = None # Random string for secure connections
|
||||
length = 32 # Random string length
|
||||
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
|
||||
"""
|
||||
t = 0
|
||||
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')
|
||||
|
||||
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
|
||||
url = self.service.config.get('opengnsys', 'remote')
|
||||
self.REST = REST(url)
|
||||
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'
|
||||
# Get network interfaces until they are active or timeout (5 minutes)
|
||||
for t in range(0, 300):
|
||||
try:
|
||||
self.interface = list(operations.getNetworkInfo())[0] # Get first network interface
|
||||
# Get the first network interface
|
||||
self.interface = list(operations.getNetworkInfo())[0]
|
||||
except Exception as e:
|
||||
# Wait 1 sec. and retry
|
||||
logger.warn (e)
|
||||
time.sleep(1)
|
||||
finally:
|
||||
# Exit loop if interface is active
|
||||
|
@ -111,30 +160,38 @@ class OpenGnSysWorker(ServerWorker):
|
|||
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
|
||||
for t in range(0, 100):
|
||||
init_retries = 100
|
||||
for t in range(0, init_retries):
|
||||
try:
|
||||
try:
|
||||
self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||
'secret': self.random, 'ostype': operations.os_type,
|
||||
'osversion': operations.os_version})
|
||||
'osversion': operations.os_version,
|
||||
'agent_version': VERSION})
|
||||
break
|
||||
except:
|
||||
except Exception as 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('opengnsys', 'altremote'))
|
||||
self.REST = REST(self.service.config.get(self.name, 'altremote'), ca_file=ca_file, crt_file=crt_file, key_file=key_file)
|
||||
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})
|
||||
'osversion': operations.os_version, 'alt_url': True,
|
||||
'agent_version': VERSION})
|
||||
break
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.warn (str (e))
|
||||
time.sleep(3)
|
||||
# Raise error after timeout
|
||||
if 0 < t < 100:
|
||||
if t < init_retries-1:
|
||||
logger.debug('Successful connection after {} tries'.format(t))
|
||||
elif t == 100:
|
||||
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']:
|
||||
try:
|
||||
|
@ -148,24 +205,29 @@ 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})
|
||||
|
||||
def processClientMessage(self, message, data):
|
||||
logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data))
|
||||
|
||||
def onLogin(self, data):
|
||||
"""
|
||||
Sends session login notification to OpenGnsys server
|
||||
"""
|
||||
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.logged_in = True
|
||||
self.user.append ({'username': user, 'login_ts': time.time() })
|
||||
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})
|
||||
|
@ -174,8 +236,15 @@ class OpenGnSysWorker(ServerWorker):
|
|||
"""
|
||||
Sends session logout notification to OpenGnsys server
|
||||
"""
|
||||
logger.debug('Received logout for {}'.format(user))
|
||||
self.logged_in = False
|
||||
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)))
|
||||
try:
|
||||
self.user.pop()
|
||||
except IndexError:
|
||||
pass
|
||||
self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, 'user': user})
|
||||
|
||||
def process_ogclient(self, path, get_params, post_params, server):
|
||||
|
@ -206,34 +275,34 @@ class OpenGnSysWorker(ServerWorker):
|
|||
raise Exception('Message processor for "{}" not found'.format(path[0]))
|
||||
return operation(path[1:], get_params, post_params)
|
||||
|
||||
# Warning: the order of the decorators matters
|
||||
@execution_level('status')
|
||||
@check_secret
|
||||
def process_status(self, path, get_params, post_params, server):
|
||||
"""
|
||||
Returns client status (OS type or execution status) and login status
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param get_params: optional parameter "detail" to show extended status
|
||||
:param post_params:
|
||||
:param server:
|
||||
:return: JSON object {"status": "status_code", "loggedin": boolean}
|
||||
:return: JSON object {"status": "status_code", "loggedin": boolean, ...}
|
||||
"""
|
||||
res = {'status': '', 'loggedin': self.logged_in, 'session': self.session_type}
|
||||
if platform.system() == 'Linux': # GNU/Linux
|
||||
# Check if it's OpenGnsys Client.
|
||||
if os.path.exists('/scripts/oginit'):
|
||||
# Check if OpenGnsys Client is busy.
|
||||
if self.locked:
|
||||
res['status'] = 'BSY'
|
||||
else:
|
||||
res['status'] = 'OPG'
|
||||
else:
|
||||
# Check if there is an active session.
|
||||
res['status'] = 'LNX'
|
||||
elif platform.system() == 'Windows': # Windows
|
||||
# Check if there is an active session.
|
||||
res['status'] = 'WIN'
|
||||
elif platform.system() == 'Darwin': # Mac OS X ??
|
||||
res['status'] = 'OSX'
|
||||
st = {'linux': 'LNX', 'macos': 'OSX', 'windows': 'WIN'}
|
||||
try:
|
||||
# Standard status
|
||||
res = {'status': st[operations.os_type.lower()], 'loggedin': len(self.user) > 0,
|
||||
'session': self.session_type}
|
||||
# Detailed status
|
||||
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']})
|
||||
except KeyError:
|
||||
# Unknown operating system
|
||||
res = {'status': 'UNK'}
|
||||
return res
|
||||
|
||||
@execution_level('halt')
|
||||
@check_secret
|
||||
def process_reboot(self, path, get_params, post_params, server):
|
||||
"""
|
||||
|
@ -252,6 +321,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
threading.Thread(target=rebt).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
@execution_level('halt')
|
||||
@check_secret
|
||||
def process_poweroff(self, path, get_params, post_params, server):
|
||||
"""
|
||||
|
@ -271,6 +341,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
threading.Thread(target=pwoff).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_script(self, path, get_params, post_params, server):
|
||||
"""
|
||||
|
@ -282,21 +353,53 @@ class OpenGnSysWorker(ServerWorker):
|
|||
:return: JSON object {"op": "launched"}
|
||||
"""
|
||||
logger.debug('Processing script request')
|
||||
# Decoding script (Windows scripts need a subprocess call per line)
|
||||
script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8')
|
||||
if operations.os_type == 'Windows':
|
||||
script = 'import subprocess; {0}'.format(
|
||||
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
|
||||
else:
|
||||
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
||||
# Executing script.
|
||||
if post_params.get('client', 'false') == 'false':
|
||||
thr = ScriptExecutorThread(script)
|
||||
thr.start()
|
||||
else:
|
||||
self.sendClientMessage('script', {'code': script})
|
||||
return {'op': 'launched'}
|
||||
# Decoding script
|
||||
script = urllib.parse.unquote(base64.b64decode(post_params.get('script')).decode('utf-8'))
|
||||
logger.debug('received script "{}"'.format(script))
|
||||
|
||||
if post_params.get('client', 'false') == 'false':
|
||||
jobid = self.jobmgr.launch_job (script, False)
|
||||
return {'op': 'launched', 'jobid': jobid}
|
||||
|
||||
else: ## post_params.get('client') is not 'false'
|
||||
## send script as-is
|
||||
self.sendClientMessage('script', {'code': script})
|
||||
#return {'op': 'launched', 'jobid': jobid} ## TODO obtain jobid generated at the client (can it be done?)
|
||||
return {'op': 'launched'}
|
||||
|
||||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_terminatescript(self, path, get_params, post_params, server):
|
||||
jobid = post_params.get('jobid', None)
|
||||
logger.debug('Processing terminate_script request, jobid "{}"'.format (jobid))
|
||||
if jobid is None:
|
||||
return {}
|
||||
self.sendClientMessage('terminatescript', {'jobid': jobid})
|
||||
self.jobmgr.terminate_job (jobid)
|
||||
return {}
|
||||
|
||||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_preparescripts(self, path, get_params, post_params, server):
|
||||
logger.debug('Processing preparescripts request')
|
||||
self.st = self.jobmgr.prepare_jobs()
|
||||
logger.debug('Sending preparescripts to client')
|
||||
self.sendClientMessage('preparescripts', None)
|
||||
return {}
|
||||
|
||||
def process_client_preparescripts(self, params):
|
||||
logger.debug('Processing preparescripts message from client')
|
||||
for p in params:
|
||||
#logger.debug ('p "{}"'.format(p))
|
||||
self.st.append (p)
|
||||
|
||||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_getscripts(self, path, get_params, post_params, server):
|
||||
logger.debug('Processing getscripts request')
|
||||
return self.st
|
||||
|
||||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_logoff(self, path, get_params, post_params, server):
|
||||
"""
|
||||
|
@ -307,6 +410,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
self.sendClientMessage('logoff', {})
|
||||
return {'op': 'sent to client'}
|
||||
|
||||
@execution_level('full')
|
||||
@check_secret
|
||||
def process_popup(self, path, get_params, post_params, server):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,875 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# Copyright (c) 2024-2025 Qindel Formación y Servicios S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
@author: Ramón M. Gómez, ramongomez at us dot es
|
||||
@author: Natalia Serrano, nserrano at qindel dot com
|
||||
"""
|
||||
|
||||
import base64
|
||||
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
|
||||
|
||||
# Check authorization header decorator
|
||||
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 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
|
||||
|
||||
def onDeactivation (self):
|
||||
"""
|
||||
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})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def InventariandoSoftware (self, dsk, par, nfn):
|
||||
sft_src = f'/tmp/CSft-{self.IPlocal}-{par}'
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [dsk, par, sft_src])
|
||||
herror = 0
|
||||
except:
|
||||
herror = 1
|
||||
|
||||
if herror:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
b64 = ''
|
||||
self.muestraMensaje (20)
|
||||
else:
|
||||
if not os.path.exists (sft_src):
|
||||
raise Exception (f'interfaceAdmin({nfn}) returned success but did not create file ({sft_src})')
|
||||
sft_src_contents = Path (sft_src).read_bytes()
|
||||
|
||||
b64 = base64.b64encode (sft_src_contents).decode ('utf-8')
|
||||
self.muestraMensaje (19)
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_InventarioSoftware',
|
||||
'dsk': dsk, ## not in the original C code, around ogAdmClient.c:1944
|
||||
'par': par,
|
||||
'contents': b64,
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, 0)
|
||||
|
||||
def ejecutaArchivo (self,fn):
|
||||
logger.debug ('fn ({})'.format (fn))
|
||||
|
||||
## in the "file" there's not just some bash, but a sequence of parameters such as "nfn=Function\rparam1=foo\rparam2=bar"
|
||||
buffer = subprocess.run (['cat', fn], capture_output=True).stdout.strip().decode ('utf-8')
|
||||
logger.debug ('buffer ({})'.format (buffer.replace ('\r', '\\r'))) ## change \r so as not to mess with the log
|
||||
if buffer:
|
||||
for l in buffer.split ('@'):
|
||||
if not len (l): continue
|
||||
logger.debug ('line ({})'.format (l.replace ('\r', '\\r'))) ## change \r so as not to mess with the log
|
||||
## at this point, an option would be fire up a curl to localhost, but we can also parse the params and locally call the desired function:
|
||||
post_params = {}
|
||||
for param in l.split ("\r"):
|
||||
k, v = param.split ('=')
|
||||
post_params[k] = v
|
||||
logger.debug ('post_params "{}"'.format (post_params))
|
||||
|
||||
func_name = post_params.pop ('nfn', None)
|
||||
if func_name is None:
|
||||
logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
break
|
||||
func = getattr (self, 'process_' + func_name)
|
||||
## func is already a ref to self.func, so we don't have to call self.func(...) or func(self, ...)
|
||||
|
||||
logger.debug ('calling function "{}" with post_params "{}"'.format (func_name, post_params))
|
||||
output = func ([], {}, post_params, None)
|
||||
logger.debug ('output "{}"'.format (output))
|
||||
if not output:
|
||||
logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
break
|
||||
|
||||
def inclusionCliente (self):
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
|
||||
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 })
|
||||
logger.debug ('res ({})'.format (res))
|
||||
|
||||
## RESPUESTA_InclusionCliente
|
||||
if (type (res) is not dict or 0 == res['res']) :
|
||||
logger.error ('Ha ocurrido algún problema en el proceso de inclusión del cliente')
|
||||
return False
|
||||
|
||||
if (not res['ido'] or not res['npc']):
|
||||
logger.error ('Se han recibido parámetros con valores no válidos')
|
||||
return False
|
||||
|
||||
self.idordenador = res['ido'] ## Identificador del ordenador
|
||||
self.nombreordenador = res['npc'] ## Nombre del ordenador
|
||||
self.cache = res['che'] ## Tamaño de la caché reservada al cliente
|
||||
self.idproautoexec = res['exe'] ## Procedimento de inicio (Autoexec)
|
||||
self.idcentro = res['idc'] ## Identificador de la Unidad Organizativa
|
||||
self.idaula = res['ida'] ## Identificador del aula
|
||||
|
||||
return True
|
||||
|
||||
def cuestionCache (self):
|
||||
return True ## ogAdmClient.c:425
|
||||
|
||||
def autoexecCliente (self):
|
||||
res = self.enviaMensajeServidor ('AutoexecCliente', { 'exe': self.idproautoexec })
|
||||
logger.debug ('res ({})'.format (res))
|
||||
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
|
||||
logger.error ('Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración')
|
||||
return False
|
||||
|
||||
## RESPUESTA_AutoexecCliente
|
||||
if (type (res) is not dict or 0 == res['res']) :
|
||||
logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
return False
|
||||
|
||||
logger.info (res)
|
||||
res = self.enviaMensajeServidor ('enviaArchivo', { 'nfl': res['nfl'] })
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
|
||||
logger.error ('Ha ocurrido algún problema al recibir un archivo por la red')
|
||||
return False
|
||||
logger.debug (f'res ({res})')
|
||||
|
||||
fileautoexec = '/tmp/_autoexec_{}'.format (self.IPlocal)
|
||||
logger.debug ('fileautoexec ({})'.format (fileautoexec))
|
||||
with open (fileautoexec, 'w') as fd:
|
||||
fd.write (base64.b64decode (res['contents']).decode ('utf-8'))
|
||||
|
||||
self.ejecutaArchivo (fileautoexec)
|
||||
|
||||
return True
|
||||
|
||||
def comandosPendientes (self):
|
||||
while (True):
|
||||
res = self.enviaMensajeServidor ('ComandosPendientes') ## receives just one command
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración')
|
||||
logger.error ('Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración')
|
||||
return False
|
||||
|
||||
logger.info (res)
|
||||
if ('NoComandosPtes' == res['nfn']):
|
||||
break
|
||||
|
||||
## TODO manage the rest of cases... we might have to do something similar to ejecutaArchivo
|
||||
#if (!gestionaTrama (ptrTrama)){ // Análisis de la trama
|
||||
# logger.error ('Ha ocurrido algún problema al procesar la trama recibida')
|
||||
# return False
|
||||
#}
|
||||
## ATM let's just return false to avoid a possible infinite loop
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def procesaComandos (self):
|
||||
res = self.enviaMensajeServidor ('DisponibilidadComandos', { 'tpc': 'OPG' }) ## Activar disponibilidad
|
||||
logger.debug ('res ({})'.format (res))
|
||||
|
||||
if (type (res) is not dict):
|
||||
logger.error ('Ha ocurrido algún problema al enviar una petición de comandos interactivos al Servidor de Administración')
|
||||
return False
|
||||
|
||||
logger.info ('Disponibilidad de comandos activada') ## Disponibilidad de cliente activada
|
||||
|
||||
## we now return true and the outer agent code gets to wait for requests from outside
|
||||
## TODO thing is, ogAdmClient always calls comandosPendientes() after every received request. How do we do that here?
|
||||
#
|
||||
#ptrTrama=recibeMensaje (&socket_c);
|
||||
#if (!ptrTrama){
|
||||
# errorLog (modulo,46,FALSE); 'Ha ocurrido algún problema al recibir un comando interactivo desde el Servidor de Administración'
|
||||
# return;
|
||||
#}
|
||||
#close (socket_c);
|
||||
#if (!gestionaTrama (ptrTrama)){ // Análisis de la trama
|
||||
# errorLog (modulo,39,FALSE); 'Ha ocurrido algún problema al procesar la trama recibida'
|
||||
# return;
|
||||
#}
|
||||
#if (!comandosPendientes (ptrTrama)){
|
||||
# errorLog (modulo,42,FALSE); 'Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración'
|
||||
#}
|
||||
|
||||
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()):
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
logger.info ('Cliente iniciado')
|
||||
logger.info ('Procesando caché')
|
||||
if not self.cuestionCache():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
if self.idproautoexec > 0:
|
||||
logger.info ('Ejecución de archivo Autoexec')
|
||||
if not self.autoexecCliente():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
logger.info ('Procesa comandos pendientes')
|
||||
if not self.comandosPendientes():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
logger.info ('Acciones pendientes procesadas')
|
||||
|
||||
self.muestraMenu()
|
||||
self.procesaComandos()
|
||||
|
||||
logger.info ('onActivation ok')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def do_CrearImagen (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']
|
||||
|
||||
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])
|
||||
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_CrearImagen',
|
||||
'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:
|
||||
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']
|
||||
|
||||
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] + 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_RestaurarImagen',
|
||||
'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:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
dsk = post_params['dsk']
|
||||
cfg = post_params['cfg']
|
||||
ids = post_params['ids']
|
||||
|
||||
self.muestraMensaje (4)
|
||||
|
||||
params = []
|
||||
disk_info = cfg.pop (0)
|
||||
logger.debug (f'disk_info ({disk_info})')
|
||||
for k in ['dis']:
|
||||
params.append (f'{k}={disk_info[k]}')
|
||||
disk_info_str = '*'.join (params)
|
||||
|
||||
partitions = []
|
||||
for entry in cfg:
|
||||
logger.debug (f'entry ({entry})')
|
||||
params = []
|
||||
for k in ['par', 'cpt', 'sfi', 'tam', 'ope']:
|
||||
params.append (f'{k}={entry[k]}')
|
||||
partitions.append ('*'.join (params))
|
||||
part_info_str = '%'.join (partitions)
|
||||
|
||||
cfg_str = f'{disk_info_str}!{part_info_str}%'
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn, ['ignored', cfg_str])
|
||||
self.muestraMensaje (14)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
self.muestraMensaje (13)
|
||||
herror = 1
|
||||
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
|
||||
return {}
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_Configurar',
|
||||
'cfg': self.cfg2obj (cfg),
|
||||
}
|
||||
self.muestraMenu()
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_InventarioHardware (self, post_params):
|
||||
for k in ['nfn', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
ids = post_params['ids']
|
||||
|
||||
self.muestraMensaje (6)
|
||||
|
||||
hrdsrc = f'/tmp/Chrd-{self.IPlocal}' ## Nombre que tendra el archivo de inventario
|
||||
hrddst = f'/tmp/Shrd-{self.IPlocal}' ## Nombre que tendra el archivo en el Servidor
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [hrdsrc])
|
||||
hrdsrc_contents = Path (hrdsrc).read_bytes()
|
||||
logger.debug (f'hrdsrc_contents 1 ({hrdsrc_contents})')
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
self.muestraMensaje (18)
|
||||
herror = 1
|
||||
|
||||
if herror:
|
||||
hrddst = ''
|
||||
else:
|
||||
logger.debug (f'hrdsrc_contents 2 ({hrdsrc_contents})')
|
||||
## Envía fichero de inventario al servidor
|
||||
res = self.enviaMensajeServidor ('recibeArchivo', { 'nfl': hrddst, 'contents': base64.b64encode (hrdsrc_contents).decode ('utf-8') })
|
||||
logger.debug (res)
|
||||
if not res:
|
||||
logger.error ('Ha ocurrido algún problema al enviar un archivo por la red')
|
||||
herror = 12 ## Error de envío de fichero por la red
|
||||
self.muestraMensaje (17)
|
||||
|
||||
## Envia respuesta de ejecución de la función de interface
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_InventarioHardware',
|
||||
'hrd': hrddst,
|
||||
}
|
||||
self.muestraMenu()
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_InventarioSoftware (self, post_params):
|
||||
for k in ['nfn', 'dsk', 'par', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
dsk = post_params['dsk']
|
||||
par = post_params['par']
|
||||
ids = post_params['ids']
|
||||
|
||||
self.muestraMensaje (7)
|
||||
|
||||
try:
|
||||
cmd = self.InventariandoSoftware (dsk, par, 'InventarioSoftware')
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
cmd = { 'nfn': 'RESPUESTA_InventarioSoftware' }
|
||||
herror = 1
|
||||
|
||||
self.muestraMenu()
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_Actualizar (self, post_params):
|
||||
self.muestraMensaje (1)
|
||||
#if !comandosPendientes: error 84 'Ha ocurrido algún problema al reiniciar la sesión del cliente'
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
|
||||
logger.error ('LeeConfiguracion() failed')
|
||||
return {}
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_Actualizar',
|
||||
'cfg': self.cfg2obj (cfg),
|
||||
}
|
||||
self.muestraMenu()
|
||||
return self.respuestaEjecucionComando (cmd, 0)
|
||||
|
||||
def do_Comando (self, post_params):
|
||||
for k in ['nfn', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
ids = post_params['ids']
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
herror = 1
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_Comando',
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_ConsolaRemota (self, post_params):
|
||||
for k in ['nfn', 'scp']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
scp = base64.b64decode (unquote (post_params['scp'])).decode ('utf-8')
|
||||
filescript = f'/tmp/_script_{self.IPlocal}'
|
||||
ecosrc = f'/tmp/_econsola_{self.IPlocal}'
|
||||
ecodst = f'/tmp/_Seconsola_{self.IPlocal}' ## Nombre que tendra el archivo en el Servidor
|
||||
|
||||
with open (filescript, 'w') as fd:
|
||||
fd.write (scp)
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [filescript, ecosrc])
|
||||
ecosrc_contents = Path (ecosrc).read_bytes()
|
||||
except:
|
||||
logger.error ('Error al ejecutar el comando')
|
||||
return {}
|
||||
|
||||
logger.debug ('sending recibeArchivo to server')
|
||||
res = self.enviaMensajeServidor ('recibeArchivo', { 'nfl': ecodst, 'contents': base64.b64encode (ecosrc_contents).decode ('utf-8') })
|
||||
logger.debug (res)
|
||||
if not res:
|
||||
logger.error ('Ha ocurrido algún problema al enviar un archivo por la red')
|
||||
|
||||
return {}
|
||||
|
||||
def do_Apagar (self, post_params):
|
||||
for k in ['nfn', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
ids = post_params['ids']
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
herror = 1
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_Apagar',
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_Reiniciar (self, post_params):
|
||||
for k in ['nfn', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
ids = post_params['ids']
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
herror = 1
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_Reiniciar',
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_IniciarSesion (self, post_params):
|
||||
for k in ['nfn', 'dsk', 'par', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
dsk = post_params['dsk']
|
||||
par = post_params['par']
|
||||
ids = post_params['ids']
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [dsk, par])
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
herror = 1
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_IniciarSesion',
|
||||
}
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
def do_EjecutarScript (self, post_params):
|
||||
for k in ['nfn', 'scp', 'ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
nfn = post_params['nfn']
|
||||
scp = base64.b64decode (unquote (post_params['scp'])).decode ('utf-8')
|
||||
ids = post_params['ids']
|
||||
|
||||
self.muestraMensaje (8)
|
||||
|
||||
filescript = f'/tmp/_script_{self.IPlocal}' ## Nombre del archivo de script
|
||||
with open (filescript, 'w') as fd:
|
||||
fd.write (scp)
|
||||
|
||||
try:
|
||||
self.interfaceAdmin (nfn, [filescript])
|
||||
self.muestraMensaje (22)
|
||||
herror = 0
|
||||
except:
|
||||
logger.warning ('Error al ejecutar el comando')
|
||||
self.muestraMensaje (21)
|
||||
herror = 1
|
||||
|
||||
## Toma configuración de particiones
|
||||
cfg = self.LeeConfiguracion()
|
||||
if not cfg:
|
||||
logger.warning ('No se ha podido recuperar la configuración de las particiones del disco')
|
||||
herror = 36
|
||||
|
||||
#herror=ejecutarCodigoBash(scp); ## ogAdmClient.c:2004
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_EjecutarScript',
|
||||
'cfg': self.cfg2obj (cfg),
|
||||
}
|
||||
self.muestraMenu()
|
||||
return self.respuestaEjecucionComando (cmd, herror, ids)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@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,))
|
||||
|
||||
def process_Sondeo (self, path, get_params, post_params, server):
|
||||
logger.debug ('in process_Sondeo, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
return {} ## ogAdmClient.c:920
|
||||
|
||||
@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))
|
||||
|
||||
for k in ['ids']:
|
||||
if k not in post_params:
|
||||
logger.error (f'required parameter ({k}) not in POST params')
|
||||
return {}
|
||||
|
||||
ids = post_params['ids']
|
||||
|
||||
cmd = {
|
||||
'nfn': 'RESPUESTA_Arrancar',
|
||||
'tpc': 'OPG',
|
||||
}
|
||||
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_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')
|
||||
# raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
|
||||
|
||||
#def process_CrearSoftIncremental (self, path, get_params, post_params, server):
|
||||
# logger.debug ('in process_CrearSoftIncremental, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
# 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)))
|
||||
return self._long_running_job ('RestaurarImagen', self.do_RestaurarImagen, args=(post_params,))
|
||||
|
||||
#def process_RestaurarImagenBasica (self, path, get_params, post_params, server):
|
||||
# logger.debug ('in process_RestaurarImagenBasica, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
# logger.warning ('this method has been removed')
|
||||
# raise Exception ({ '_httpcode': 404, '_msg': 'This method has been removed' })
|
||||
|
||||
#def process_RestaurarSoftIncremental (self, path, get_params, post_params, server):
|
||||
# logger.warning ('in process_RestaurarSoftIncremental')
|
||||
# logger.debug ('in process_RestaurarSoftIncremental, path "{}" get_params "{}" post_params "{}" server "{}"'.format (path, get_params, post_params, server))
|
||||
# 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']
|
||||
r = self.killer (jid)
|
||||
logger.debug (f'r bef ({r})')
|
||||
r.update ({ 'nfn':'RESPUESTA_KillJob', 'job':jid })
|
||||
logger.debug (f'r aft ({r})')
|
||||
return r
|
|
@ -31,7 +31,7 @@
|
|||
"""
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
# Importing platform operations and getting operating system data.
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 201 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
# pylint: disable-msg=E1101,W0703
|
||||
|
||||
from opengnsys.log import logger
|
||||
|
||||
import threading
|
||||
import six
|
||||
|
||||
|
||||
class ScriptExecutorThread(threading.Thread):
|
||||
def __init__(self, script):
|
||||
super(ScriptExecutorThread, self).__init__()
|
||||
self.script = script
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
logger.debug('Executing script: {}'.format(self.script))
|
||||
six.exec_(self.script, globals(), None)
|
||||
except Exception as e:
|
||||
logger.error('Error executing script: {}'.format(e))
|
|
@ -26,23 +26,21 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
"""
|
||||
|
||||
from .log import logger
|
||||
from .config import readConfig
|
||||
from .utils import exceptionToMessage
|
||||
import json
|
||||
import socket
|
||||
import time
|
||||
|
||||
from . import ipc
|
||||
from . import httpserver
|
||||
from .config import readConfig
|
||||
from .loader import loadModules
|
||||
from .log import logger
|
||||
from .utils import exceptionToMessage
|
||||
|
||||
import socket
|
||||
import time
|
||||
import json
|
||||
import six
|
||||
|
||||
IPC_PORT = 10398
|
||||
|
||||
|
@ -58,21 +56,17 @@ class CommonService(object):
|
|||
logger.info('Initializing OpenGnsys Agent')
|
||||
|
||||
# Read configuration file before proceding & ensures minimal config is there
|
||||
|
||||
self.config = readConfig()
|
||||
|
||||
# Get opengnsys section as dict
|
||||
cfg = dict(self.config.items('opengnsys'))
|
||||
|
||||
# Set up log level
|
||||
logger.setLevel(cfg.get('log', 'INFO'))
|
||||
|
||||
|
||||
logger.debug('Loaded configuration from opengnsys.cfg:')
|
||||
for section in self.config.sections():
|
||||
logger.debug('Section {} = {}'.format(section, self.config.items(section)))
|
||||
|
||||
|
||||
if logger.logger.isWindows():
|
||||
# Logs will also go to windows event log for services
|
||||
logger.logger.serviceLogger = True
|
||||
|
@ -90,15 +84,16 @@ class CommonService(object):
|
|||
logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
|
||||
|
||||
def stop(self):
|
||||
'''
|
||||
"""
|
||||
Requests service termination
|
||||
'''
|
||||
"""
|
||||
self.isAlive = False
|
||||
|
||||
# ********************************
|
||||
# * Internal messages processors *
|
||||
# ********************************
|
||||
def notifyLogin(self, username):
|
||||
def notifyLogin(self, data):
|
||||
username = data.decode('utf-8')
|
||||
for v in self.modules:
|
||||
try:
|
||||
logger.debug('Notifying login of user {} to module {}'.format(username, v.name))
|
||||
|
@ -106,7 +101,8 @@ class CommonService(object):
|
|||
except Exception as e:
|
||||
logger.error('Got exception {} processing login message on {}'.format(e, v.name))
|
||||
|
||||
def notifyLogout(self, username):
|
||||
def notifyLogout(self, data):
|
||||
username = data.decode('utf-8')
|
||||
for v in self.modules:
|
||||
try:
|
||||
logger.debug('Notifying logout of user {} to module {}'.format(username, v.name))
|
||||
|
@ -115,7 +111,7 @@ class CommonService(object):
|
|||
logger.error('Got exception {} processing logout message on {}'.format(e, v.name))
|
||||
|
||||
def notifyMessage(self, data):
|
||||
module, message, data = data.split('\0')
|
||||
module, message, data = data.decode('utf-8').split('\0')
|
||||
for v in self.modules:
|
||||
if v.name == module: # Case Sensitive!!!!
|
||||
try:
|
||||
|
@ -127,12 +123,11 @@ class CommonService(object):
|
|||
|
||||
logger.error('Module {} not found, messsage {} not sent'.format(module, message))
|
||||
|
||||
|
||||
def clientMessageProcessor(self, msg, data):
|
||||
'''
|
||||
"""
|
||||
Callback, invoked from IPC, on its own thread (not the main thread).
|
||||
This thread will "block" communication with agent untill finished, but this should be no problem
|
||||
'''
|
||||
"""
|
||||
logger.debug('Got message {}'.format(msg))
|
||||
|
||||
if msg == ipc.REQ_LOGIN:
|
||||
|
@ -143,14 +138,9 @@ class CommonService(object):
|
|||
self.notifyMessage(data)
|
||||
|
||||
def initialize(self):
|
||||
# ******************************************
|
||||
# * Initialize listeners, modules, etc...
|
||||
# ******************************************
|
||||
|
||||
if six.PY3 is False:
|
||||
import threading
|
||||
threading._DummyThread._Thread__stop = lambda x: 42
|
||||
|
||||
"""
|
||||
Initialize listeners, modules, etc...
|
||||
"""
|
||||
logger.debug('Starting IPC listener at {}'.format(IPC_PORT))
|
||||
self.ipc = ipc.ServerIPC(self.ipcport, clientMessageProcessor=self.clientMessageProcessor)
|
||||
self.ipc.start()
|
||||
|
@ -168,7 +158,7 @@ class CommonService(object):
|
|||
validMods.append(mod)
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error("Activation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
|
||||
logger.debug("Activation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
|
||||
|
||||
self.modules[:] = validMods # copy instead of assignment
|
||||
|
||||
|
@ -203,23 +193,23 @@ class CommonService(object):
|
|||
# Methods that CAN BE overridden by agents
|
||||
# ****************************************
|
||||
def doWait(self, miliseconds):
|
||||
'''
|
||||
"""
|
||||
Invoked to wait a bit
|
||||
CAN be OVERRIDDEN
|
||||
'''
|
||||
"""
|
||||
time.sleep(float(miliseconds) / 1000)
|
||||
|
||||
def notifyStop(self):
|
||||
'''
|
||||
"""
|
||||
Overridden to log stop
|
||||
'''
|
||||
"""
|
||||
logger.info('Service is being stopped')
|
||||
|
||||
# ***************************************************
|
||||
# * Helpers, convenient methods to facilitate comms *
|
||||
# ***************************************************
|
||||
def sendClientMessage(self, toModule, message, data):
|
||||
'''
|
||||
"""
|
||||
Sends a message to the clients using IPC
|
||||
The data is converted to json, so ensure that it is serializable.
|
||||
All IPC is asynchronous, so if you expect a response, this will be sent by client using another message
|
||||
|
@ -227,23 +217,23 @@ class CommonService(object):
|
|||
@param toModule: Module that will receive this message
|
||||
@param message: Message to send
|
||||
@param data: data to send
|
||||
'''
|
||||
"""
|
||||
self.ipc.sendMessageMessage('\0'.join((toModule, message, json.dumps(data))))
|
||||
|
||||
def sendScriptMessage(self, script):
|
||||
'''
|
||||
"""
|
||||
Sends an script to be executed by client
|
||||
'''
|
||||
"""
|
||||
self.ipc.sendScriptMessage(script)
|
||||
|
||||
def sendLogoffMessage(self):
|
||||
'''
|
||||
"""
|
||||
Sends a logoff message to client
|
||||
'''
|
||||
"""
|
||||
self.ipc.sendLoggofMessage()
|
||||
|
||||
def sendPopupMessage(self, title, message):
|
||||
'''
|
||||
Sends a poup box to be displayed by client
|
||||
'''
|
||||
"""
|
||||
Sends a popup box to be displayed by client
|
||||
"""
|
||||
self.ipc.sendPopupMessage(title, message)
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import sys
|
||||
import six
|
||||
|
@ -61,7 +61,7 @@ def exceptionToMessage(e):
|
|||
if isinstance(arg, Exception):
|
||||
msg = msg + exceptionToMessage(arg)
|
||||
else:
|
||||
msg = msg + toUnicode(arg) + '. '
|
||||
msg = msg + str(arg) + '. '
|
||||
return msg
|
||||
|
||||
|
||||
|
|
|
@ -120,5 +120,9 @@ class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
win32serviceutil.HandleCommandLine(OGAgentSvc)
|
||||
if len(sys.argv) == 1:
|
||||
servicemanager.Initialize()
|
||||
servicemanager.PrepareToHostSingle(OGAgentSvc)
|
||||
servicemanager.StartServiceCtrlDispatcher()
|
||||
else:
|
||||
win32serviceutil.HandleCommandLine(OGAgentSvc)
|
||||
|
|
|
@ -36,6 +36,8 @@ 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))
|
||||
|
||||
|
@ -43,14 +45,25 @@ OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
|
|||
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\temp
|
||||
logging.basicConfig(
|
||||
filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'),
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
# 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)
|
||||
|
||||
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):
|
||||
|
@ -58,7 +71,7 @@ class LocalLogger(object):
|
|||
# our loglevels are 10000 (other), 20000 (debug), ....
|
||||
# logging levels are 10 (debug), 20 (info)
|
||||
# OTHER = logging.NOTSET
|
||||
self.logger.log(level / 1000 - 10, message)
|
||||
self.logger.log(int(level / 1000 - 10), message, stacklevel=4)
|
||||
|
||||
if level < INFO or self.serviceLogger is False: # Only information and above will be on event log
|
||||
return
|
||||
|
|
|
@ -35,6 +35,7 @@ import os
|
|||
import locale
|
||||
import subprocess
|
||||
import ctypes
|
||||
import base64
|
||||
from ctypes.wintypes import DWORD, LPCWSTR
|
||||
import win32com.client # @UnresolvedImport, pylint: disable=import-error
|
||||
import win32net # @UnresolvedImport, pylint: disable=import-error
|
||||
|
@ -46,15 +47,6 @@ from opengnsys import utils
|
|||
from opengnsys.log import logger
|
||||
|
||||
|
||||
def getErrorMessage(res=0):
|
||||
msg = win32api.FormatMessage(res)
|
||||
return msg.decode('windows-1250', 'ignore')
|
||||
|
||||
|
||||
def getComputerName():
|
||||
return win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
|
||||
|
||||
|
||||
def getNetworkInfo():
|
||||
'''
|
||||
Obtains a list of network interfaces
|
||||
|
@ -64,7 +56,7 @@ def getNetworkInfo():
|
|||
ip: ip of the interface
|
||||
'''
|
||||
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
||||
wmobj = obj.ConnectServer("localhost", "root\cimv2")
|
||||
wmobj = obj.ConnectServer("localhost", "root\\cimv2")
|
||||
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
|
||||
try:
|
||||
for obj in adapters:
|
||||
|
@ -81,33 +73,16 @@ def getNetworkInfo():
|
|||
return
|
||||
|
||||
|
||||
def getDomainName():
|
||||
'''
|
||||
Will return the domain name if we belong a domain, else None
|
||||
(if part of a network group, will also return None)
|
||||
'''
|
||||
# Status:
|
||||
# 0 = Unknown
|
||||
# 1 = Unjoined
|
||||
# 2 = Workgroup
|
||||
# 3 = Domain
|
||||
domain, status = win32net.NetGetJoinInformation()
|
||||
if status != 3:
|
||||
domain = None
|
||||
|
||||
return domain
|
||||
|
||||
|
||||
def getWindowsVersion():
|
||||
'''
|
||||
Returns Windows version.
|
||||
'''
|
||||
import _winreg
|
||||
reg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
|
||||
import winreg
|
||||
reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
|
||||
try:
|
||||
data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'ReleaseId')[0])
|
||||
data = '{} {}'.format(winreg.QueryValueEx(reg, 'ProductName')[0], winreg.QueryValueEx(reg, 'ReleaseId')[0])
|
||||
except Exception:
|
||||
data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'CurrentBuildNumber')[0])
|
||||
data = '{} {}'.format(winreg.QueryValueEx(reg, 'ProductName')[0], winreg.QueryValueEx(reg, 'CurrentBuildNumber')[0])
|
||||
reg.Close()
|
||||
return data
|
||||
|
||||
|
@ -115,8 +90,8 @@ def getWindowsVersion():
|
|||
EWX_LOGOFF = 0x00000000
|
||||
EWX_SHUTDOWN = 0x00000001
|
||||
EWX_REBOOT = 0x00000002
|
||||
EWX_FORCE = 0x00000004
|
||||
EWX_POWEROFF = 0x00000008
|
||||
#EWX_FORCE = 0x00000004
|
||||
#EWX_POWEROFF = 0x00000008
|
||||
EWX_FORCEIFHUNG = 0x00000010
|
||||
|
||||
|
||||
|
@ -137,109 +112,6 @@ def logoff():
|
|||
win32api.ExitWindowsEx(EWX_LOGOFF)
|
||||
|
||||
|
||||
def renameComputer(newName):
|
||||
# Needs admin privileges to work
|
||||
if ctypes.windll.kernel32.SetComputerNameExW(DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)) == 0: # @UndefinedVariable
|
||||
# win32api.FormatMessage -> returns error string
|
||||
# win32api.GetLastError -> returns error code
|
||||
# (just put this comment here to remember to log this when logger is available)
|
||||
error = getErrorMessage()
|
||||
computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
|
||||
raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error))
|
||||
|
||||
|
||||
NETSETUP_JOIN_DOMAIN = 0x00000001
|
||||
NETSETUP_ACCT_CREATE = 0x00000002
|
||||
NETSETUP_ACCT_DELETE = 0x00000004
|
||||
NETSETUP_WIN9X_UPGRADE = 0x00000010
|
||||
NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020
|
||||
NETSETUP_JOIN_UNSECURE = 0x00000040
|
||||
NETSETUP_MACHINE_PWD_PASSED = 0x00000080
|
||||
NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400
|
||||
NETSETUP_DEFER_SPN_SET = 0x1000000
|
||||
|
||||
|
||||
def joinDomain(domain, ou, account, password, executeInOneStep=False):
|
||||
'''
|
||||
Joins machine to a windows domain
|
||||
:param domain: Domain to join to
|
||||
:param ou: Ou that will hold machine
|
||||
:param account: Account used to join domain
|
||||
:param password: Password of account used to join domain
|
||||
:param executeInOneStep: If true, means that this machine has been renamed and wants to add NETSETUP_JOIN_WITH_NEW_NAME to request so we can do rename/join in one step.
|
||||
'''
|
||||
# If account do not have domain, include it
|
||||
if '@' not in account and '\\' not in account:
|
||||
if '.' in domain:
|
||||
account = account + '@' + domain
|
||||
else:
|
||||
account = domain + '\\' + account
|
||||
|
||||
# Do log
|
||||
flags = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
|
||||
|
||||
if executeInOneStep:
|
||||
flags |= NETSETUP_JOIN_WITH_NEW_NAME
|
||||
|
||||
flags = DWORD(flags)
|
||||
|
||||
domain = LPCWSTR(domain)
|
||||
|
||||
# Must be in format "ou=.., ..., dc=...,"
|
||||
ou = LPCWSTR(ou) if ou is not None and ou != '' else None
|
||||
account = LPCWSTR(account)
|
||||
password = LPCWSTR(password)
|
||||
|
||||
res = ctypes.windll.netapi32.NetJoinDomain(None, domain, ou, account, password, flags)
|
||||
# Machine found in another ou, use it and warn this on log
|
||||
if res == 2224:
|
||||
flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN)
|
||||
res = ctypes.windll.netapi32.NetJoinDomain(None, domain, None, account, password, flags)
|
||||
if res != 0:
|
||||
# Log the error
|
||||
error = getErrorMessage(res)
|
||||
if res == 1355:
|
||||
error = "DC Is not reachable"
|
||||
print('{} {}'.format(res, error))
|
||||
raise Exception('Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(domain.value, account.value, ', under OU {}'.format(ou.value) if ou.value is not None else '', res, error))
|
||||
|
||||
|
||||
def changeUserPassword(user, oldPassword, newPassword):
|
||||
computerName = LPCWSTR(getComputerName())
|
||||
user = LPCWSTR(user)
|
||||
oldPassword = LPCWSTR(oldPassword)
|
||||
newPassword = LPCWSTR(newPassword)
|
||||
|
||||
res = ctypes.windll.netapi32.NetUserChangePassword(computerName, user, oldPassword, newPassword)
|
||||
|
||||
if res != 0:
|
||||
# Log the error, and raise exception to parent
|
||||
error = getErrorMessage()
|
||||
raise Exception('Error changing password for user {}: {}'.format(user.value, error))
|
||||
|
||||
|
||||
class LASTINPUTINFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('cbSize', ctypes.c_uint),
|
||||
('dwTime', ctypes.c_uint),
|
||||
]
|
||||
|
||||
|
||||
def initIdleDuration(atLeastSeconds):
|
||||
'''
|
||||
In windows, there is no need to set screensaver
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def getIdleDuration():
|
||||
lastInputInfo = LASTINPUTINFO()
|
||||
lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo)
|
||||
ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo))
|
||||
millis = ctypes.windll.kernel32.GetTickCount() - lastInputInfo.dwTime # @UndefinedVariable
|
||||
return millis / 1000.0
|
||||
|
||||
|
||||
def getCurrentUser():
|
||||
'''
|
||||
Returns current logged in username
|
||||
|
@ -275,3 +147,10 @@ def get_etc_path():
|
|||
Returns etc directory path.
|
||||
"""
|
||||
return os.path.join('C:', os.sep, 'Windows', 'System32', 'drivers', 'etc')
|
||||
|
||||
|
||||
def build_popen_args(script):
|
||||
## turn the script into utf16le, then to b64 again, and feed the blob to powershell
|
||||
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]
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
from .server_worker import ServerWorker
|
||||
from .client_worker import ClientWorker
|
||||
from .oglive_worker import ogLiveWorker, ThreadWithResult
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
"""
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class ClientWorker(object):
|
||||
'''
|
||||
"""
|
||||
A ServerWorker is a server module that "works" for service
|
||||
Most method are invoked inside their own thread, except onActivation & onDeactivation.
|
||||
This two methods are invoked inside main service thread, take that into account when creating them
|
||||
|
@ -43,7 +43,7 @@ class ClientWorker(object):
|
|||
https://sampleserver:8888/[name]/....
|
||||
Remember that module names and REST path are case sensitive!!!
|
||||
|
||||
'''
|
||||
"""
|
||||
name = None
|
||||
service = None
|
||||
|
||||
|
@ -51,15 +51,15 @@ class ClientWorker(object):
|
|||
self.service = service
|
||||
|
||||
def activate(self):
|
||||
'''
|
||||
"""
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for activation in a future
|
||||
'''
|
||||
"""
|
||||
self.onActivation()
|
||||
|
||||
def deactivate(self):
|
||||
'''
|
||||
"""
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for deactivation in a future
|
||||
'''
|
||||
"""
|
||||
self.onDeactivation()
|
||||
|
||||
def processMessage(self, message, params):
|
||||
|
@ -83,32 +83,31 @@ class ClientWorker(object):
|
|||
return operation(params)
|
||||
|
||||
def onActivation(self):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service for activation.
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
def onDeactivation(self):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service before unloading service
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
# *************************************
|
||||
# * Helper, convenient helper methods *
|
||||
# *************************************
|
||||
def sendServerMessage(self, message, data):
|
||||
'''
|
||||
"""
|
||||
Sends a message to connected ipc clients
|
||||
By convenience, it uses the "current" moduel name as destination module name also.
|
||||
If you need to send a message to a different module, you can use self.service.sendClientMessage(module, message, data) instead
|
||||
og this helmer
|
||||
'''
|
||||
"""
|
||||
self.service.ipc.sendMessage(self.name, message, data)
|
||||
|
|
@ -0,0 +1,529 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2024 Qindel Formación y Servicios S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
@author: Natalia Serrano, nserrano at qindel dot com
|
||||
"""
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
|
||||
import os
|
||||
import 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
|
||||
import signal
|
||||
|
||||
from configparser import NoOptionError
|
||||
from opengnsys import REST
|
||||
from opengnsys.log import logger
|
||||
from .server_worker import ServerWorker
|
||||
|
||||
## https://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread
|
||||
class ThreadWithResult (threading.Thread):
|
||||
def run (self):
|
||||
try:
|
||||
self.result = None
|
||||
if self._target is not None:
|
||||
## the first arg in self._args is the queue
|
||||
self.pid_q = self._args[0]
|
||||
self.stdout_q = self._args[1]
|
||||
self._args = self._args[2:]
|
||||
try:
|
||||
self.result = self._target (*self._args, **self._kwargs)
|
||||
except Exception as e:
|
||||
self.result = { 'res': 2, 'der': f'got exception: ({e})' } ## res=2 as defined in ogAdmClient.c:2048
|
||||
finally:
|
||||
# Avoid a refcycle if the thread is running a function with an argument that has a member that points to the thread.
|
||||
del self._target, self._args, self._kwargs, self.pid_q, self.stdout_q
|
||||
|
||||
class ogLiveWorker(ServerWorker):
|
||||
thread_list = {}
|
||||
thread_lock = threading.Lock()
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
def notifier (self, job_id, result):
|
||||
result['job_id'] = job_id
|
||||
self.REST.sendMessage ('clients/status/webhook', result)
|
||||
|
||||
def killer (self, job_id):
|
||||
logger.debug (f'killer() called, job_id ({job_id})')
|
||||
if job_id not in self.thread_list: return { 'res': 2, 'der': 'Unknown job' }
|
||||
|
||||
with self.thread_lock:
|
||||
if 'thread' not in self.thread_list[job_id]: return { 'res': 2, 'der': 'Job is not running' }
|
||||
t = self.thread_list[job_id]['thread']
|
||||
pid = self.thread_list[job_id]['child_pid']
|
||||
logger.debug (f'pid ({pid})')
|
||||
try_times = 8
|
||||
sig = signal.SIGTERM
|
||||
msg = f'could not kill pid ({pid}) after ({try_times}) tries'
|
||||
success = 2 ## mimic cmd['res'] in respuestaEjecucionComando(): "1" means success, "2" means failed
|
||||
while True:
|
||||
t.join (0.05)
|
||||
if not t.is_alive():
|
||||
msg = 'job terminated'
|
||||
success = 1
|
||||
logger.debug (msg)
|
||||
self.thread_list[job_id]['child_pid'] = None
|
||||
break
|
||||
## race condition: if the subprocess finishes just here, then we already checked that t.is_alive() is true, but os.path.exists(/proc/pid) will be false below. msg will be 'nothing to kill'.
|
||||
## this is fine in the first iteration of the loop, before we send any signals. In the rest of iterations, after some signals were sent, msg should be 'job terminated' instead.
|
||||
if pid:
|
||||
if os.path.exists (f'/proc/{pid}'):
|
||||
logger.debug (f'sending signal ({sig}) to pid ({pid})')
|
||||
## if the process finishes just here, nothing happens: the signal is sent to the void
|
||||
os.kill (pid, sig)
|
||||
#subprocess.run (['kill', '--signal', str(sig), str(pid)])
|
||||
else:
|
||||
msg = f'pid ({pid}) is gone, nothing to kill'
|
||||
success = 1
|
||||
logger.debug (msg)
|
||||
self.thread_list[job_id]['child_pid'] = None
|
||||
break
|
||||
else:
|
||||
msg = 'no PID to kill'
|
||||
logger.debug (msg)
|
||||
|
||||
if not try_times: break
|
||||
if 4 == try_times: sig = signal.SIGKILL ## change signal after a few tries
|
||||
try_times -= 1
|
||||
time.sleep (0.4)
|
||||
|
||||
return { 'res':success, 'der':msg }
|
||||
|
||||
def _extract_progress (self, job_id, ary=[]):
|
||||
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()})")
|
||||
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})')
|
||||
|
||||
if self.pid_q:
|
||||
if not self.pid_q.empty():
|
||||
elem['child_pid'] = self.pid_q.get()
|
||||
logger.debug (f'queue not empty, got pid ({elem["child_pid"]})')
|
||||
|
||||
if self.stdout_q:
|
||||
partial = ''
|
||||
while not self.stdout_q.empty():
|
||||
partial += self.stdout_q.get()
|
||||
lines = partial.splitlines()
|
||||
if len (lines):
|
||||
p = self._extract_progress (k, lines)
|
||||
if p:
|
||||
m = { "job_id": k, "progress": p }
|
||||
self.REST.sendMessage ('clients/status/webhook', { "job_id": k, "progress": p })
|
||||
|
||||
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']
|
||||
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', 'EjecutarScript', 'getConfiguration', 'getIpAddress', 'IniciarSesion', 'InventarioHardware', 'InventarioSoftware', 'Reiniciar', 'RestaurarImagen']:
|
||||
## python
|
||||
logger.debug (f'({method}) is a python method')
|
||||
exe = '{}/{}.py'.format (self.pathinterface, method)
|
||||
proc = [exe]+parametros
|
||||
else: ## ConsolaRemota procesaCache
|
||||
## bash
|
||||
logger.debug (f'({method}) is a bash method')
|
||||
exe = '{}/{}'.format (self.pathinterface, method)
|
||||
|
||||
LANG = os.environ.get ('LANG', 'en_GB.UTF-8').replace ('UTF_8', 'UTF-8')
|
||||
devel_bash_prefix = f'''
|
||||
PATH=/opt/opengnsys/scripts/:$PATH;
|
||||
source /opt/opengnsys/etc/lang.{LANG}.conf;
|
||||
for I in /opt/opengnsys/lib/engine/bin/*.lib; do source $I; done;
|
||||
for i in $(declare -F |cut -f3 -d" "); do export -f $i; done;
|
||||
'''
|
||||
|
||||
if parametros:
|
||||
proc = ['bash', '-c', '{} {} {}'.format (devel_bash_prefix, exe, ' '.join (parametros))]
|
||||
else:
|
||||
proc = ['bash', '-c', '{} {}'.format (devel_bash_prefix, exe)]
|
||||
|
||||
logger.debug ('subprocess.run ("{}")'.format (' '.join (proc)))
|
||||
p = subprocess.Popen (proc, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if self.pid_q:
|
||||
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
|
||||
|
||||
sout = serr = ''
|
||||
while p.poll() is None:
|
||||
for l in iter (p.stdout.readline, b''):
|
||||
partial = l.decode ('utf-8', 'ignore')
|
||||
if self.stdout_q: self.stdout_q.put (partial)
|
||||
sout += partial
|
||||
for l in iter (p.stderr.readline, b''):
|
||||
partial = l.decode ('utf-8', 'ignore')
|
||||
serr += partial
|
||||
time.sleep (1)
|
||||
sout = sout.strip()
|
||||
serr = serr.strip()
|
||||
|
||||
## DEBUG
|
||||
logger.debug (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}')
|
||||
## /DEBUG
|
||||
if 0 != p.returncode:
|
||||
cmd_txt = ' '.join (proc)
|
||||
logger.error (f'command ({cmd_txt}) failed, stderr follows:')
|
||||
for l in serr.splitlines():
|
||||
logger.error (f' {l}')
|
||||
raise Exception (f'command ({cmd_txt}) failed, see log for details')
|
||||
return sout
|
||||
|
||||
def tomaIPlocal (self):
|
||||
try:
|
||||
self.IPlocal = self.interfaceAdmin ('getIpAddress')
|
||||
except Exception as e:
|
||||
logger.error (e)
|
||||
logger.error ('No se ha podido recuperar la dirección IP del cliente')
|
||||
return False
|
||||
logger.info ('local IP is "{}"'.format (self.IPlocal))
|
||||
return True
|
||||
|
||||
def tomaMAClocal (self):
|
||||
## tomaIPlocal() calls interfaceAdm('getIpAddress')
|
||||
## getIpAddress runs 'ip addr show' and returns the IP address of every network interface except "lo"
|
||||
## (ie. breaks badly if there's more than one network interface)
|
||||
## let's make the same assumptions here
|
||||
mac = subprocess.run (["ip -json link show |jq -r '.[] |select (.ifname != \"lo\") |.address'"], shell=True, capture_output=True, text=True)
|
||||
self.mac = mac.stdout.strip()
|
||||
return True
|
||||
|
||||
def enviaMensajeServidor (self, path, obj={}):
|
||||
obj['iph'] = self.IPlocal ## Ip del ordenador
|
||||
obj['ido'] = self.idordenador ## Identificador del ordenador
|
||||
obj['npc'] = self.nombreordenador ## Nombre del ordenador
|
||||
obj['idc'] = self.idcentro ## Identificador del centro
|
||||
obj['ida'] = self.idaula ## Identificador del aula
|
||||
|
||||
res = self.REST.sendMessage ('/'.join ([self.name, path]), obj)
|
||||
|
||||
if (type (res) is not dict):
|
||||
logger.error (f'response is not a dict ({res})')
|
||||
return False
|
||||
|
||||
return res
|
||||
|
||||
## en C, esto envia una trama de respuesta al servidor. Devuelve un boolean
|
||||
## en python, simplemente termina de construir la respuesta y la devuelve; no envía nada por la red. El caller la usa en return() para enviar implícitamente la respuesta
|
||||
def respuestaEjecucionComando (self, cmd, herror, ids=None):
|
||||
if ids: ## Existe seguimiento
|
||||
cmd['ids'] = ids ## Añade identificador de la sesión
|
||||
|
||||
if 0 == herror: ## el comando terminó con resultado satisfactorio
|
||||
cmd['res'] = 1
|
||||
cmd['der'] = ''
|
||||
else: ## el comando tuvo algún error
|
||||
cmd['res'] = 2
|
||||
cmd['der'] = self.tbErroresScripts[herror]
|
||||
|
||||
return cmd
|
||||
|
||||
def cargaPaginaWeb (self, url=None):
|
||||
if (not url): url = self.urlMenu
|
||||
|
||||
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.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
|
||||
|
||||
return True
|
||||
|
||||
def muestraMenu (self):
|
||||
self.cargaPaginaWeb()
|
||||
|
||||
def muestraMensaje (self, idx):
|
||||
self.cargaPaginaWeb (f'{self.urlMsg}?idx={idx}')
|
||||
|
||||
def LeeConfiguracion (self):
|
||||
try:
|
||||
parametroscfg = self.interfaceAdmin ('getConfiguration') ## Configuración de los Sistemas Operativos del cliente
|
||||
except Exception as e:
|
||||
logger.error (e)
|
||||
logger.error ('No se ha podido recuperar la dirección IP del cliente')
|
||||
return None
|
||||
#logger.debug ('parametroscfg ({})'.format (parametroscfg))
|
||||
return parametroscfg
|
||||
|
||||
def cfg2obj (self, cfg):
|
||||
obj = []
|
||||
ptrPar = cfg.split ('\n')
|
||||
for line in ptrPar:
|
||||
elem = {}
|
||||
ptrCfg = line.split ('\t')
|
||||
|
||||
for item in ptrCfg:
|
||||
if '=' not in item:
|
||||
logger.warning (f'invalid item ({item})')
|
||||
continue
|
||||
k, v = item.split ('=', maxsplit=1)
|
||||
elem[k] = v
|
||||
|
||||
obj.append (elem)
|
||||
|
||||
return obj
|
||||
|
||||
def onActivation (self):
|
||||
if not os.path.exists ('/scripts/oginit'):
|
||||
## no estamos en oglive, este modulo no debe cargarse
|
||||
## esta lógica la saco de src/opengnsys/linux/operations.py, donde hay un if similar
|
||||
raise Exception ('Refusing to load within an operating system')
|
||||
|
||||
self.pathinterface = None
|
||||
self.IPlocal = None ## Ip del ordenador
|
||||
self.mac = None ## MAC del ordenador
|
||||
self.idordenador = None ## Identificador del ordenador
|
||||
self.nombreordenador = None ## Nombre del ordenador
|
||||
self.cache = None
|
||||
self.idproautoexec = None
|
||||
self.idcentro = None ## Identificador del centro
|
||||
self.idaula = None ## Identificador del aula
|
||||
self.pid_q = None ## for passing PIDs around
|
||||
self.stdout_q = None ## for passing stdout
|
||||
self.progress_jobs = {}
|
||||
|
||||
ogcore_scheme = os.environ.get ('OGAGENTCFG_OGCORE_SCHEME', 'https')
|
||||
ogcore_ip = os.environ.get ('OGAGENTCFG_OGCORE_IP', '192.168.2.1')
|
||||
ogcore_port = os.environ.get ('OGAGENTCFG_OGCORE_PORT', '8443')
|
||||
urlmenu_scheme = os.environ.get ('OGAGENTCFG_URLMENU_SCHEME', 'https')
|
||||
urlmenu_ip = os.environ.get ('OGAGENTCFG_URLMENU_IP', '192.168.2.1')
|
||||
urlmenu_port = os.environ.get ('OGAGENTCFG_URLMENU_PORT', '8443')
|
||||
ogcore_ip_port = ':'.join (map (str, filter (None, [ogcore_ip, ogcore_port ])))
|
||||
urlmenu_ip_port = ':'.join (map (str, filter (None, [urlmenu_ip, urlmenu_port])))
|
||||
try:
|
||||
url = self.service.config.get (self.name, 'remote')
|
||||
loglevel = self.service.config.get (self.name, 'log')
|
||||
self.pathinterface = self.service.config.get (self.name, 'pathinterface')
|
||||
self.urlMenu = self.service.config.get (self.name, 'urlMenu')
|
||||
self.urlMsg = self.service.config.get (self.name, 'urlMsg')
|
||||
|
||||
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)
|
||||
|
||||
if not self.tomaIPlocal():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
if not self.tomaMAClocal():
|
||||
raise Exception ('Se han generado errores. No se puede continuar la ejecución de este módulo')
|
||||
|
||||
threading.Thread (name='monitoring_thread', target=self.mon, daemon=True).start()
|
||||
|
||||
def _long_running_job (self, name, f, args):
|
||||
any_job_running = False
|
||||
for k in self.thread_list:
|
||||
if self.thread_list[k]['running']:
|
||||
any_job_running = True
|
||||
break
|
||||
if any_job_running:
|
||||
logger.info ('some job is already running, refusing to launch another one')
|
||||
return { 'job_id': None, 'message': 'some job is already running, refusing to launch another one' }
|
||||
|
||||
job_id = '{}-{}'.format (name, ''.join (random.choice ('0123456789abcdef') for _ in range (8)))
|
||||
import queue
|
||||
self.pid_q = queue.Queue() ## a single queue works for us because we never have more than one long_running_job at the same time
|
||||
self.stdout_q = queue.Queue()
|
||||
self.thread_list[job_id] = {
|
||||
'thread': ThreadWithResult (target=f, args=(self.pid_q, self.stdout_q) + args),
|
||||
'starttime': time.time(),
|
||||
'child_pid': None,
|
||||
'running': True,
|
||||
'result': None
|
||||
}
|
||||
self.thread_list[job_id]['thread'].start()
|
||||
return { 'job_id': job_id }
|
||||
|
||||
## para matar threads tengo lo siguiente:
|
||||
## - aqui en _long_running_job meto una cola en self.pid_q
|
||||
## - (self.pid_q fue inicializado a None al instanciar el objeto, para evitar error "objeto no tiene 'pid_q'")
|
||||
## - en el thread_list también tengo un child_pid para almacenar el pid de los procesos hijos
|
||||
## - al crear el ThreadWithResult le paso la cola, y luego en run() la recojo y la meto en el self.pid_q del thread
|
||||
## - en interfaceAdmin() al hacer subprocess.Popen(), recojo el pid y lo escribo en la queue
|
||||
## - en mon() recojo pids de la queue y los meto en thread_list 'child_pid'
|
||||
## - algunas funciones llaman a interfaceAdmin más de una vez, y escriben más de un pid en la cola, y en mon() voy recogiendo y actualizando
|
||||
## - por ejemplo EjecutarScript llama a interfaceAdmin() y luego llama a LeeConfiguracion() el cual llama a interfaceAdmin() otra vez
|
||||
## - y cuando nos llamen a KillJob, terminamos en killer() el cual coge el 'child_pid' y zas
|
||||
## - pero a lo mejor el child ya terminó
|
||||
## - o a lo mejor el KillJob nos llegó demasiado pronto y todavía no hubo ningún child
|
||||
##
|
||||
## $ curl --insecure -X POST --data '{"nfn":"EjecutarScript","scp":"cd /usr; sleep 30; pwd; ls","ids":"0"}' https://192.168.2.199:8000/ogAdmClient/EjecutarScript
|
||||
## {"job_id": "EjecutarScript-333feb3f"}
|
||||
## $ curl --insecure -X POST --data '{"job_id":"EjecutarScript-333feb3f"}' https://192.168.2.199:8000/ogAdmClient/KillJob
|
||||
##
|
||||
## funciona bien, excepto que el PID no muere xD, ni siquiera haciendo subprocess.run('kill')
|
||||
|
||||
## para mostrar el progreso de los jobs reutilizo la misma infra
|
||||
## una cola self.stdout_q
|
||||
## en interfaceAdmin escribo la stdout parcial que ya venia recogiendo
|
||||
## mon() lo recoge y le hace un POST a ogcore
|
|
@ -25,14 +25,14 @@
|
|||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
"""
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class ServerWorker(object):
|
||||
'''
|
||||
"""
|
||||
A ServerWorker is a server module that "works" for service
|
||||
Most method are invoked inside their own thread, except onActivation & onDeactivation.
|
||||
This two methods are invoked inside main service thread, take that into account when creating them
|
||||
|
@ -43,7 +43,7 @@ class ServerWorker(object):
|
|||
https://sampleserver:8888/[name]/....
|
||||
Remember that module names and REST path are case sensitive!!!
|
||||
|
||||
'''
|
||||
"""
|
||||
name = None
|
||||
service = None
|
||||
locked = False
|
||||
|
@ -52,28 +52,28 @@ class ServerWorker(object):
|
|||
self.service = service
|
||||
|
||||
def activate(self):
|
||||
'''
|
||||
"""
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for activation in a future
|
||||
'''
|
||||
"""
|
||||
self.onActivation()
|
||||
|
||||
def deactivate(self):
|
||||
'''
|
||||
"""
|
||||
Convenient method to wrap onActivation, so we can include easyly custom common logic for deactivation in a future
|
||||
'''
|
||||
"""
|
||||
self.onDeactivation()
|
||||
|
||||
def process(self, getParams, postParams, server):
|
||||
'''
|
||||
"""
|
||||
This method is invoked on a message received with an empty path (that means a message with only the module name, like in "http://example.com/Sample"
|
||||
Override it if you expect messages with that pattern
|
||||
|
||||
Overriden method must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are)
|
||||
'''
|
||||
"""
|
||||
raise NotImplementedError('Generic message processor is not supported')
|
||||
|
||||
def processServerMessage(self, path, getParams, postParams, server):
|
||||
'''
|
||||
"""
|
||||
This method can be overriden to provide your own message proccessor, or better you can
|
||||
implement a method that is called exactly as "process_" + path[0] (module name has been removed from path array) and this default processMessage will invoke it
|
||||
* Example:
|
||||
|
@ -88,7 +88,7 @@ class ServerWorker(object):
|
|||
will be invoked directly
|
||||
|
||||
The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are)
|
||||
'''
|
||||
"""
|
||||
if self.locked is True:
|
||||
raise Exception('system is busy')
|
||||
|
||||
|
@ -96,14 +96,14 @@ class ServerWorker(object):
|
|||
return self.process(getParams, postParams, server)
|
||||
try:
|
||||
operation = getattr(self, 'process_' + path[0])
|
||||
except Exception:
|
||||
raise Exception('Message processor for "{}" not found'.format(path[0]))
|
||||
except:
|
||||
raise Exception ({ '_httpcode': 404, '_msg': f'{path[0]}: method not found' })
|
||||
|
||||
return operation(path[1:], getParams, postParams, server)
|
||||
|
||||
|
||||
def processClientMessage(self, message, data):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service when a client message is received (A message from user space Agent)
|
||||
|
||||
This method can be overriden to provide your own message proccessor, or better you can
|
||||
|
@ -116,7 +116,7 @@ class ServerWorker(object):
|
|||
self.process_client_mazinger(jsonParams)
|
||||
|
||||
The methods returns nothing (client communications are done asynchronously)
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
operation = getattr(self, 'process_client_' + message)
|
||||
except Exception:
|
||||
|
@ -128,52 +128,52 @@ class ServerWorker(object):
|
|||
|
||||
|
||||
def onActivation(self):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service for activation.
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
def onDeactivation(self):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service before unloading service
|
||||
This MUST be overridden by modules!
|
||||
This method is invoked inside main thread, so if it "hangs", complete service will hang
|
||||
This should be no problem, but be advised about this
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def onLogin(self, user):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service when an user login is detected
|
||||
This CAN be overridden by modules
|
||||
This method is invoked whenever the client (user space agent) notifies the server (Service) that a user has logged in.
|
||||
This method is run on its own thread
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
def onLogout(self, user):
|
||||
'''
|
||||
"""
|
||||
Invoked by Service when an user login is detected
|
||||
This CAN be overridden by modules
|
||||
This method is invoked whenever the client (user space agent) notifies the server (Service) that a user has logged in.
|
||||
This method is run on its own thread
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
# *************************************
|
||||
# * Helper, convenient helper methods *
|
||||
# *************************************
|
||||
def sendClientMessage(self, message, data):
|
||||
'''
|
||||
"""
|
||||
Sends a message to connected ipc clients
|
||||
By convenience, it uses the "current" moduel name as destination module name also.
|
||||
If you need to send a message to a different module, you can use self.service.sendClientMessage(module, message, data) instead
|
||||
og this helmer
|
||||
'''
|
||||
"""
|
||||
self.service.sendClientMessage(self.name, message, data)
|
||||
|
||||
def sendScriptMessage(self, script):
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
'''
|
||||
Created on Jul 9, 2015
|
||||
|
||||
@author: dkmaster
|
||||
'''
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
# Pydev can't parse "six.moves.xxxx" because it is loaded lazy
|
||||
from six.moves.socketserver import ThreadingMixIn # @UnresolvedImport
|
||||
from six.moves.SimpleHTTPServer import SimpleHTTPRequestHandler # @UnresolvedImport
|
||||
from six.moves.BaseHTTPServer import HTTPServer # @UnresolvedImport
|
||||
from six.moves.urllib.parse import unquote # @UnresolvedImport
|
||||
|
||||
import json
|
||||
import threading
|
||||
import ssl
|
||||
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
# For testing
|
||||
# --------------------
|
||||
CERTFILE = 'UDSActor.pem'
|
||||
|
||||
|
||||
def createSelfSignedCert(force=False):
|
||||
|
||||
certFile = os.path.join(tempfile.gettempdir(), CERTFILE)
|
||||
|
||||
if os.path.exists(certFile) and not force:
|
||||
return certFile
|
||||
|
||||
certData = '''-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb50K3mIznNklz
|
||||
yVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxbfxHbeRnoYTWV2nKk4+tHqmvz
|
||||
ujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqCfItWgL5pJopDpNHFul9Rn3ds
|
||||
PMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPmVLdF4uJ3Tuz8TSy2gWLs5aSr
|
||||
5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuDUGNBvBQFac1G7qUcMReeu8Zr
|
||||
DUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDqDUK1Oqs9X35yOQfDOAFYHiix
|
||||
PX0IsXOZAgMBAAECggEBAJi3000RrIUZUp6Ph0gzPMuCjDEEwWiQA7CPNX1gpb8O
|
||||
dp0WhkDhUroWIaICYPSXtOwUTtVjRqivMoxPy1Thg3EIoGC/rdeSdlXRHMEGicwJ
|
||||
yVyalFnatr5Xzg5wkxVh4XMd0zeDt7e3JD7s0QLo5lm1CEzd77qz6lhzFic5/1KX
|
||||
bzdULtTlq60dazg2hEbcS4OmM1UMCtRVDAsOIUIZPL0M9j1C1d1iEdYnh2xshKeG
|
||||
/GOfo95xsgdMlGjtv3hUT5ryKVoEsu+36rGb4VfhPfUvvoVbRx5QZpW+QvxaYh5E
|
||||
Fi0JEROozFwG31Y++8El7J3yQko8cFBa1lYYUwwpNAECgYEAykT+GiM2YxJ4uVF1
|
||||
OoKiE9BD53i0IG5j87lGPnWqzEwYBwnqjEKDTou+uzMGz3MDV56UEFNho7wUWh28
|
||||
LpEkjJB9QgbsugjxIBr4JoL/rYk036e/6+U8I95lvYWrzb+rBMIkRDYI7kbQD/mQ
|
||||
piYUpuCkTymNAu2RisK6bBzJslkCgYEAxVE23OQvkCeOV8hJNPZGpJ1mDS+TiOow
|
||||
oOScMZmZpail181eYbAfMsCr7ri812lSj98NvA2GNVLpddil6LtS1cQ5p36lFBtV
|
||||
xQUMZiFz4qVbEak+izL+vPaev/mXXsOcibAIQ+qI/0txFpNhJjpaaSy6vRCBYFmc
|
||||
8pgSoBnBI0ECgYAUKCn2atnpp5aWSTLYgNosBU4vDA1PShD14dnJMaqyr0aZtPhF
|
||||
v/8b3btFJoGgPMLxgWEZ+2U4ju6sSFhPf7FXvLJu2QfQRkHZRDbEh7t5DLpTK4Fp
|
||||
va9vl6Ml7uM/HsGpOLuqfIQJUs87OFCc7iCSvMJDDU37I7ekT2GKkpfbCQKBgBrE
|
||||
0NeY0WcSJrp7/oqD2sOcYurpCG/rrZs2SIZmGzUhMxaa0vIXzbO59dlWELB8pmnE
|
||||
Tf20K//x9qA5OxDe0PcVPukdQlH+/1zSOYNliG44FqnHtyd1TJ/gKVtMBiAiE4uO
|
||||
aSClod5Yosf4SJbCFd/s5Iyfv52NqsAyp1w3Aj/BAoGAVCnEiGUfyHlIR+UH4zZW
|
||||
GXJMeqdZLfcEIszMxLePkml4gUQhoq9oIs/Kw+L1DDxUwzkXN4BNTlFbOSu9gzK1
|
||||
dhuIUGfS6RPL88U+ivC3A0y2jT43oUMqe3hiRt360UQ1GXzp2dMnR9odSRB1wHoO
|
||||
IOjEBZ8341/c9ZHc5PCGAG8=
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID7zCCAtegAwIBAgIJAIrEIthCfxUCMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD
|
||||
VQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMREwDwYDVQQHDAhBbGNvcmNvbjEMMAoG
|
||||
A1UECgwDVURTMQ4wDAYDVQQLDAVBY3RvcjESMBAGA1UEAwwJVURTIEFjdG9yMSgw
|
||||
JgYJKoZIhvcNAQkBFhlzdXBwb3J0QHVkc2VudGVycHJpc2UuY29tMB4XDTE0MTAy
|
||||
NjIzNDEyNFoXDTI0MTAyMzIzNDEyNFowgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQI
|
||||
DAZNYWRyaWQxETAPBgNVBAcMCEFsY29yY29uMQwwCgYDVQQKDANVRFMxDjAMBgNV
|
||||
BAsMBUFjdG9yMRIwEAYDVQQDDAlVRFMgQWN0b3IxKDAmBgkqhkiG9w0BCQEWGXN1
|
||||
cHBvcnRAdWRzZW50ZXJwcmlzZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCb50K3mIznNklzyVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxb
|
||||
fxHbeRnoYTWV2nKk4+tHqmvzujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqC
|
||||
fItWgL5pJopDpNHFul9Rn3dsPMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPm
|
||||
VLdF4uJ3Tuz8TSy2gWLs5aSr5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuD
|
||||
UGNBvBQFac1G7qUcMReeu8ZrDUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDq
|
||||
DUK1Oqs9X35yOQfDOAFYHiixPX0IsXOZAgMBAAGjUDBOMB0GA1UdDgQWBBRShS90
|
||||
5lJTNvYPIEqP3GxWwG5iiDAfBgNVHSMEGDAWgBRShS905lJTNvYPIEqP3GxWwG5i
|
||||
iDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAU0Sp4gXhQmRVzq+7+
|
||||
vRFUkQuPj4Ga/d9r5Wrbg3hck3+5pwe9/7APoq0P/M0DBhQpiJKjrD6ydUevC+Y/
|
||||
43ZOJPhMlNw0o6TdQxOkX6FDwQanLLs7sfvJvqtVzYn3nuRFKT3dvl7Zg44QMw2M
|
||||
ay42q59fAcpB4LaDx/i7gOYSS5eca3lYW7j7YSr/+ozXK2KlgUkuCUHN95lOq+dF
|
||||
trmV9mjzM4CNPZqKSE7kpHRywgrXGPCO000NvEGSYf82AtgRSFKiU8NWLQSEPdcB
|
||||
k//2dsQZw2cRZ8DrC2B6Tb3M+3+CA6wVyqfqZh1SZva3LfGvq/C+u+ItguzPqNpI
|
||||
xtvM
|
||||
-----END CERTIFICATE-----'''
|
||||
with open(certFile, "wt") as f:
|
||||
f.write(certData)
|
||||
|
||||
return certFile
|
||||
# --------------
|
||||
|
||||
|
||||
class HTTPServerHandler(SimpleHTTPRequestHandler):
|
||||
service = None
|
||||
protocol_version = 'HTTP/1.1'
|
||||
server_version = 'OpenGnsys Agent Server'
|
||||
sys_version = ''
|
||||
|
||||
def sendJsonError(self, code, message):
|
||||
self.send_response(code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'error': message}))
|
||||
return
|
||||
|
||||
def sendJsonResponse(self, data):
|
||||
self.send_response(200)
|
||||
data = json.dumps(data)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', len(data))
|
||||
self.end_headers()
|
||||
# Send the html message
|
||||
self.wfile.write(data)
|
||||
|
||||
|
||||
# parseURL
|
||||
def parseUrl(self):
|
||||
# Very simple path & params splitter
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
|
||||
try:
|
||||
params = dict((v[0], unquote(v[1])) for v in (v.split('=') for v in self.path.split('?')[1].split('&')))
|
||||
except Exception:
|
||||
params = {}
|
||||
|
||||
return (path, params)
|
||||
|
||||
|
||||
def do_GET(self):
|
||||
path, params = self.parseUrl()
|
||||
|
||||
self.sendJsonResponse({'path': path, 'params': params})
|
||||
|
||||
def do_POST(self):
|
||||
path, getParams = self.parseUrl()
|
||||
|
||||
# Now post parameters, that are in JSON format
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
|
||||
pass
|
||||
|
||||
class HTTPServerThread(threading.Thread):
|
||||
def __init__(self, address, service):
|
||||
super(self.__class__, self).__init__()
|
||||
|
||||
HTTPServerHandler.service = service
|
||||
|
||||
self.certFile = createSelfSignedCert()
|
||||
self.server = HTTPThreadingServer(address, HTTPServerHandler)
|
||||
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
|
||||
|
||||
def getServerUrl(self):
|
||||
return 'https://{}:{}/{}'.format(self.server.server_address[0], self.server.server_address[1], HTTPServerHandler.uuid)
|
||||
|
||||
def stop(self):
|
||||
self.server.shutdown()
|
||||
|
||||
def run(self):
|
||||
self.server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
thr = HTTPServerThread(('0.0.0.0', 8000), None)
|
||||
print('Server started: {}'.format(thr))
|
||||
thr.start()
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
pywin32
|
||||
pyqt6
|
||||
requests
|
||||
six
|
||||
pycryptodome
|
||||
pyinstaller
|
143
src/setup.py
|
@ -1,143 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Ramón M. Gómez, ramongomez at us dot es
|
||||
"""
|
||||
|
||||
# ModuleFinder can't handle runtime changes to __path__, but win32com uses them
|
||||
try:
|
||||
# py2exe 0.6.4 introduced a replacement modulefinder.
|
||||
# This means we have to add package paths there, not to the built-in
|
||||
# one. If this new modulefinder gets integrated into Python, then
|
||||
# we might be able to revert this some day.
|
||||
# if this doesn't work, try import modulefinder
|
||||
try:
|
||||
import py2exe.mf as modulefinder
|
||||
except ImportError:
|
||||
import modulefinder
|
||||
import win32com
|
||||
import sys
|
||||
for p in win32com.__path__[1:]:
|
||||
modulefinder.AddPackagePath("win32com", p)
|
||||
for extra in ["win32com.shell"]: # ,"win32com.mapi"
|
||||
__import__(extra)
|
||||
m = sys.modules[extra]
|
||||
for p in m.__path__[1:]:
|
||||
modulefinder.AddPackagePath(extra, p)
|
||||
except ImportError:
|
||||
# no build path setup, no worries.
|
||||
pass
|
||||
|
||||
import os
|
||||
from distutils.core import setup
|
||||
import py2exe
|
||||
import sys
|
||||
|
||||
# Reading version file:
|
||||
try:
|
||||
with open('VERSION', 'r') as v:
|
||||
VERSION = v.read().rstrip()
|
||||
except IOError:
|
||||
VERSION = '1.1.0'
|
||||
|
||||
sys.argv.append('py2exe')
|
||||
|
||||
|
||||
def get_requests_cert_file():
|
||||
"""Add Python requests or certifi .pem file for installers."""
|
||||
import requests
|
||||
f = os.path.join(os.path.dirname(requests.__file__), 'cacert.pem')
|
||||
if not os.path.exists(f):
|
||||
import certifi
|
||||
f = os.path.join(os.path.dirname(certifi.__file__), 'cacert.pem')
|
||||
return f
|
||||
|
||||
|
||||
class Target:
|
||||
|
||||
def __init__(self, **kw):
|
||||
self.__dict__.update(kw)
|
||||
# for the versioninfo resources
|
||||
self.version = VERSION
|
||||
self.name = 'OGAgentService'
|
||||
self.description = 'OpenGnsys Agent Service'
|
||||
self.author = 'Adolfo Gomez'
|
||||
self.url = 'https://opengnsys.es/'
|
||||
self.company_name = "OpenGnsys Project"
|
||||
self.copyright = "(c) 2014 VirtualCable S.L.U."
|
||||
self.name = "OpenGnsys Agent"
|
||||
|
||||
# Now you need to pass arguments to setup
|
||||
# windows is a list of scripts that have their own UI and
|
||||
# thus don't need to run in a console.
|
||||
|
||||
|
||||
udsservice = Target(
|
||||
description='OpenGnsys Agent Service',
|
||||
modules=['opengnsys.windows.OGAgentService'],
|
||||
icon_resources=[(0, 'img\\oga.ico'), (1, 'img\\oga.ico')],
|
||||
cmdline_style='pywin32'
|
||||
)
|
||||
|
||||
# Some test_modules are hidden to py2exe by six, we ensure that they appear on "includes"
|
||||
HIDDEN_BY_SIX = ['SocketServer', 'SimpleHTTPServer', 'urllib']
|
||||
|
||||
setup(
|
||||
windows=[
|
||||
{
|
||||
'script': 'OGAgentUser.py',
|
||||
'icon_resources': [(0, 'img\\oga.ico'), (1, 'img\\oga.ico')]
|
||||
},
|
||||
],
|
||||
console=[
|
||||
{
|
||||
'script': 'OGAServiceHelper.py'
|
||||
}
|
||||
],
|
||||
service=[udsservice],
|
||||
data_files=[('', [get_requests_cert_file()]), ('cfg', ['cfg/ogagent.cfg', 'cfg/ogclient.cfg'])],
|
||||
options={
|
||||
'py2exe': {
|
||||
'bundle_files': 3,
|
||||
'compressed': True,
|
||||
'optimize': 2,
|
||||
'includes': ['sip', 'PyQt4', 'win32com.shell', 'requests', 'encodings', 'encodings.utf_8'] + HIDDEN_BY_SIX,
|
||||
'excludes': ['doctest', 'unittest'],
|
||||
'dll_excludes': ['msvcp90.dll'],
|
||||
'dist_dir': '..\\bin',
|
||||
}
|
||||
},
|
||||
name='OpenGnsys Agent',
|
||||
version=VERSION,
|
||||
description='OpenGnsys Agent',
|
||||
author='Adolfo Gomez',
|
||||
author_email='agomez@virtualcable.es',
|
||||
zipfile='OGAgent.zip',
|
||||
)
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from opengnsys.workers import ClientWorker
|
||||
|
||||
class Sample1(ClientWorker):
|
||||
name = 'Sample1'
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# Module must be imported on package, so we can initialize and load it
|
||||
from sample1 import Sample1
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from opengnsys.workers import ServerWorker
|
||||
|
||||
from .sample_pkg import test
|
||||
|
||||
class Sample1(ServerWorker):
|
||||
name='Sample1'
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
def test():
|
||||
return 'Test'
|
|
@ -1,210 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2015 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
# Pydev can't parse "six.moves.xxxx" because it is loaded lazy
|
||||
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
|
||||
|
||||
import json
|
||||
import threading
|
||||
import ssl
|
||||
|
||||
import logging
|
||||
from tempfile import gettempdir
|
||||
from os.path import exists, join
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CERTFILE = 'OGTestServer.pem'
|
||||
|
||||
|
||||
def createSelfSignedCert(force=False):
|
||||
|
||||
certFile = join(gettempdir(), CERTFILE)
|
||||
|
||||
if exists(certFile) and not force:
|
||||
return certFile
|
||||
|
||||
certData = '''-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb50K3mIznNklz
|
||||
yVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxbfxHbeRnoYTWV2nKk4+tHqmvz
|
||||
ujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqCfItWgL5pJopDpNHFul9Rn3ds
|
||||
PMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPmVLdF4uJ3Tuz8TSy2gWLs5aSr
|
||||
5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuDUGNBvBQFac1G7qUcMReeu8Zr
|
||||
DUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDqDUK1Oqs9X35yOQfDOAFYHiix
|
||||
PX0IsXOZAgMBAAECggEBAJi3000RrIUZUp6Ph0gzPMuCjDEEwWiQA7CPNX1gpb8O
|
||||
dp0WhkDhUroWIaICYPSXtOwUTtVjRqivMoxPy1Thg3EIoGC/rdeSdlXRHMEGicwJ
|
||||
yVyalFnatr5Xzg5wkxVh4XMd0zeDt7e3JD7s0QLo5lm1CEzd77qz6lhzFic5/1KX
|
||||
bzdULtTlq60dazg2hEbcS4OmM1UMCtRVDAsOIUIZPL0M9j1C1d1iEdYnh2xshKeG
|
||||
/GOfo95xsgdMlGjtv3hUT5ryKVoEsu+36rGb4VfhPfUvvoVbRx5QZpW+QvxaYh5E
|
||||
Fi0JEROozFwG31Y++8El7J3yQko8cFBa1lYYUwwpNAECgYEAykT+GiM2YxJ4uVF1
|
||||
OoKiE9BD53i0IG5j87lGPnWqzEwYBwnqjEKDTou+uzMGz3MDV56UEFNho7wUWh28
|
||||
LpEkjJB9QgbsugjxIBr4JoL/rYk036e/6+U8I95lvYWrzb+rBMIkRDYI7kbQD/mQ
|
||||
piYUpuCkTymNAu2RisK6bBzJslkCgYEAxVE23OQvkCeOV8hJNPZGpJ1mDS+TiOow
|
||||
oOScMZmZpail181eYbAfMsCr7ri812lSj98NvA2GNVLpddil6LtS1cQ5p36lFBtV
|
||||
xQUMZiFz4qVbEak+izL+vPaev/mXXsOcibAIQ+qI/0txFpNhJjpaaSy6vRCBYFmc
|
||||
8pgSoBnBI0ECgYAUKCn2atnpp5aWSTLYgNosBU4vDA1PShD14dnJMaqyr0aZtPhF
|
||||
v/8b3btFJoGgPMLxgWEZ+2U4ju6sSFhPf7FXvLJu2QfQRkHZRDbEh7t5DLpTK4Fp
|
||||
va9vl6Ml7uM/HsGpOLuqfIQJUs87OFCc7iCSvMJDDU37I7ekT2GKkpfbCQKBgBrE
|
||||
0NeY0WcSJrp7/oqD2sOcYurpCG/rrZs2SIZmGzUhMxaa0vIXzbO59dlWELB8pmnE
|
||||
Tf20K//x9qA5OxDe0PcVPukdQlH+/1zSOYNliG44FqnHtyd1TJ/gKVtMBiAiE4uO
|
||||
aSClod5Yosf4SJbCFd/s5Iyfv52NqsAyp1w3Aj/BAoGAVCnEiGUfyHlIR+UH4zZW
|
||||
GXJMeqdZLfcEIszMxLePkml4gUQhoq9oIs/Kw+L1DDxUwzkXN4BNTlFbOSu9gzK1
|
||||
dhuIUGfS6RPL88U+ivC3A0y2jT43oUMqe3hiRt360UQ1GXzp2dMnR9odSRB1wHoO
|
||||
IOjEBZ8341/c9ZHc5PCGAG8=
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID7zCCAtegAwIBAgIJAIrEIthCfxUCMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD
|
||||
VQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMREwDwYDVQQHDAhBbGNvcmNvbjEMMAoG
|
||||
A1UECgwDVURTMQ4wDAYDVQQLDAVBY3RvcjESMBAGA1UEAwwJVURTIEFjdG9yMSgw
|
||||
JgYJKoZIhvcNAQkBFhlzdXBwb3J0QHVkc2VudGVycHJpc2UuY29tMB4XDTE0MTAy
|
||||
NjIzNDEyNFoXDTI0MTAyMzIzNDEyNFowgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQI
|
||||
DAZNYWRyaWQxETAPBgNVBAcMCEFsY29yY29uMQwwCgYDVQQKDANVRFMxDjAMBgNV
|
||||
BAsMBUFjdG9yMRIwEAYDVQQDDAlVRFMgQWN0b3IxKDAmBgkqhkiG9w0BCQEWGXN1
|
||||
cHBvcnRAdWRzZW50ZXJwcmlzZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCb50K3mIznNklzyVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxb
|
||||
fxHbeRnoYTWV2nKk4+tHqmvzujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqC
|
||||
fItWgL5pJopDpNHFul9Rn3dsPMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPm
|
||||
VLdF4uJ3Tuz8TSy2gWLs5aSr5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuD
|
||||
UGNBvBQFac1G7qUcMReeu8ZrDUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDq
|
||||
DUK1Oqs9X35yOQfDOAFYHiixPX0IsXOZAgMBAAGjUDBOMB0GA1UdDgQWBBRShS90
|
||||
5lJTNvYPIEqP3GxWwG5iiDAfBgNVHSMEGDAWgBRShS905lJTNvYPIEqP3GxWwG5i
|
||||
iDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAU0Sp4gXhQmRVzq+7+
|
||||
vRFUkQuPj4Ga/d9r5Wrbg3hck3+5pwe9/7APoq0P/M0DBhQpiJKjrD6ydUevC+Y/
|
||||
43ZOJPhMlNw0o6TdQxOkX6FDwQanLLs7sfvJvqtVzYn3nuRFKT3dvl7Zg44QMw2M
|
||||
ay42q59fAcpB4LaDx/i7gOYSS5eca3lYW7j7YSr/+ozXK2KlgUkuCUHN95lOq+dF
|
||||
trmV9mjzM4CNPZqKSE7kpHRywgrXGPCO000NvEGSYf82AtgRSFKiU8NWLQSEPdcB
|
||||
k//2dsQZw2cRZ8DrC2B6Tb3M+3+CA6wVyqfqZh1SZva3LfGvq/C+u+ItguzPqNpI
|
||||
xtvM
|
||||
-----END CERTIFICATE-----'''
|
||||
with open(certFile, "wt") as f:
|
||||
f.write(certData)
|
||||
|
||||
return certFile
|
||||
|
||||
class HTTPServerHandler(BaseHTTPRequestHandler):
|
||||
service = None
|
||||
protocol_version = 'HTTP/1.0'
|
||||
server_version = 'OpenGnsys Test REST Server'
|
||||
sys_version = ''
|
||||
|
||||
def sendJsonError(self, code, message):
|
||||
self.send_response(code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'error': message}))
|
||||
return
|
||||
|
||||
def sendJsonResponse(self, data):
|
||||
self.send_response(200)
|
||||
data = json.dumps(data)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', len(data))
|
||||
self.end_headers()
|
||||
# Send the html message
|
||||
self.wfile.write(data)
|
||||
|
||||
|
||||
# parseURL
|
||||
def parseUrl(self):
|
||||
# Very simple path & params splitter
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
|
||||
try:
|
||||
params = dict((v[0], unquote(v[1])) for v in (v.split('=') for v in self.path.split('?')[1].split('&')))
|
||||
except Exception:
|
||||
params = {}
|
||||
|
||||
return (path, params)
|
||||
|
||||
|
||||
def do_GET(self):
|
||||
path, params = self.parseUrl()
|
||||
|
||||
self.sendJsonResponse({'path': path, 'params': params})
|
||||
|
||||
def do_POST(self):
|
||||
path, getParams = self.parseUrl()
|
||||
|
||||
# Now post parameters, that are in JSON format
|
||||
self.sendJsonResponse({'path': path, 'params': getParams})
|
||||
|
||||
def log_error(self, fmt, *args):
|
||||
logger.error('HTTP ' + fmt % args)
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
logger.info('HTTP ' + fmt % args)
|
||||
|
||||
|
||||
class HTTPThreadingServer(ThreadingMixIn, HTTPServer):
|
||||
pass
|
||||
|
||||
class HTTPServerThread(threading.Thread):
|
||||
def __init__(self, address, service):
|
||||
super(self.__class__, self).__init__()
|
||||
|
||||
HTTPServerHandler.service = service
|
||||
|
||||
self.certFile = createSelfSignedCert()
|
||||
self.server = HTTPThreadingServer(address, HTTPServerHandler)
|
||||
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
|
||||
|
||||
logger.info('Initialized HTTPS Server thread on {}'.format(address))
|
||||
|
||||
def getServerUrl(self):
|
||||
return 'https://{}:{}/{}'.format(self.server.server_address[0], self.server.server_address[1], HTTPServerHandler.uuid)
|
||||
|
||||
def stop(self):
|
||||
self.server.shutdown()
|
||||
|
||||
def run(self):
|
||||
self.server.serve_forever()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(
|
||||
filename='/tmp/restserver.log',
|
||||
filemode='w',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
|
||||
thr = HTTPServerThread(('0.0.0.0', 9999), None)
|
||||
print('Server started: {}'.format(thr))
|
||||
thr.run()
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import fileinput
|
||||
|
||||
def update_version():
|
||||
if os.path.isfile ('VERSION'):
|
||||
with open ('VERSION', 'r') as version_file:
|
||||
version = version_file.read().strip()
|
||||
|
||||
pattern = r'[0-9]*\.[0-9]*\.[0-9]*'
|
||||
matches = re.findall (pattern, version)
|
||||
win_version = matches[0]
|
||||
|
||||
with fileinput.FileInput ('about-dialog.ui', inplace=True) as file:
|
||||
for line in file:
|
||||
new = re.sub (r'Version [^<]*', 'Version {}'.format(version), line)
|
||||
print (new, end='')
|
||||
|
||||
with fileinput.FileInput ('opengnsys/__init__.py', inplace=True) as file:
|
||||
for line in file:
|
||||
new = re.sub (r'VERSION=.*', "VERSION='{}'".format(version), line)
|
||||
print (new, end='')
|
||||
|
||||
with open ('../windows/VERSION', 'w') as outfile:
|
||||
outfile.write (win_version + '\n')
|
||||
|
||||
else:
|
||||
print ('VERSION: No such file or directory')
|
||||
sys.exit (1)
|
||||
|
||||
def process_ui():
|
||||
subprocess.run (['pyuic6', 'about-dialog.ui', '-o', 'about_dialog_ui.py', '-x'])
|
||||
subprocess.run (['pyuic6', 'message-dialog.ui', '-o', 'message_dialog_ui.py', '-x'])
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.chdir (os.path.dirname (os.path.abspath (__file__)))
|
||||
update_version()
|
||||
process_ui()
|
|
@ -1,42 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
function process {
|
||||
pyuic4 about-dialog.ui -o about_dialog_ui.py -x
|
||||
pyuic4 message-dialog.ui -o message_dialog_ui.py -x
|
||||
}
|
||||
|
||||
cd $(dirname "$0")
|
||||
[ -r VERSION ] && sed -i "s/Version [^<]*/Version $(cat VERSION)/" about-dialog.ui
|
||||
pyrcc4 -py3 OGAgent.qrc -o OGAgent_rc.py
|
||||
|
||||
|
||||
# process current directory ui's
|
||||
process
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/bash
|
||||
cd "$(dirname "$0")"
|
||||
export WINEARCH=win32
|
||||
export WINEPREFIX=$PWD/wine
|
||||
grep -o "[0-9]*\.[0-9]*\.[0-9]*" ../src/VERSION > VERSION
|
||||
wine cmd /c c:\\ogagent\\build.bat
|
||||
chmod -x ../OGAgentSetup*.exe
|
|
@ -1,6 +0,0 @@
|
|||
C:
|
||||
CD \ogagent\src
|
||||
python setup.py
|
||||
CD ..
|
||||
"C:\Program Files\NSIS\makensis.exe" ogagent.nsi
|
||||
|
|
@ -88,8 +88,8 @@ Section -Main SEC0000
|
|||
SetShellVarContext all
|
||||
SetOutPath $INSTDIR
|
||||
SetOverwrite on
|
||||
File /r bin\*.*
|
||||
File vcredist_x86.exe
|
||||
File /r src\dist\OGAgent\*.*
|
||||
File windows\VC_redist.x64.exe
|
||||
File src\VERSION
|
||||
WriteRegStr HKLM "${REGKEY}\Components" Main 1
|
||||
SectionEnd
|
||||
|
@ -111,7 +111,7 @@ Section -post SEC0001
|
|||
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\vcredist_x86.exe" /passive /norestart'
|
||||
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]
|
||||
|
@ -128,8 +128,6 @@ Section -post SEC0001
|
|||
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
|
||||
# Update recovery options
|
||||
nsExec::Exec /OEM "$INSTDIR\OGAServiceHelper.exe"
|
||||
Exec "net start ogagent"
|
||||
Exec "$INSTDIR\OGAgentUser.exe"
|
||||
SectionEnd
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# We need:
|
||||
# * Wine (32 bit)
|
||||
# * winetricks (in some distributions)
|
||||
|
||||
export WINEARCH=win32 WINEPREFIX=$PWD/wine WINEDEBUG=fixme-all
|
||||
WINE=wine
|
||||
|
||||
download() {
|
||||
mkdir downloads
|
||||
# Get needed software
|
||||
cd downloads
|
||||
wget -nd https://www.python.org/ftp/python/2.7.17/python-2.7.17.msi -O python-2.7.msi
|
||||
wget -nd https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi
|
||||
wget -nd https://bootstrap.pypa.io/get-pip.py
|
||||
wget -nd https://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.4/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe/download -O pyqt-install.exe
|
||||
wget -nd https://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download -O nsis-install.exe
|
||||
wget -nd http://nsis.sourceforge.net/mediawiki/images/d/d7/NSIS_Simple_Firewall_Plugin_1.20.zip
|
||||
cd ..
|
||||
}
|
||||
|
||||
install_python() {
|
||||
if which winetricks &>/dev/null; then
|
||||
echo "Setting up wine prefix (using winetricks)"
|
||||
winetricks
|
||||
fi
|
||||
|
||||
cd downloads
|
||||
echo "Installing python"
|
||||
$WINE msiexec /qn /i python-2.7.msi
|
||||
echo "Installing vc for python"
|
||||
$WINE msiexec /qn /i VCForPython27.msi
|
||||
|
||||
echo "Installing pyqt (needs X)"
|
||||
$WINE pyqt-install.exe
|
||||
echo "Installing nsis (needs X?)"
|
||||
$WINE nsis-install.exe
|
||||
|
||||
cd ..
|
||||
}
|
||||
|
||||
setup_pip() {
|
||||
echo "Seting up pip..."
|
||||
$WINE C:\\Python27\\python -m pip install --upgrade pip
|
||||
}
|
||||
|
||||
install_packages() {
|
||||
echo "Installing pywin32"
|
||||
$WINE C:\\Python27\\python -m pip install pywin32
|
||||
echo "Installing py2exe"
|
||||
$WINE C:\\Python27\\python -m pip install py2exe_py2
|
||||
echo "Installing required packages"
|
||||
$WINE C:\\Python27\\python -m pip install requests six
|
||||
# Using easy_install instead of pip to install pycrypto
|
||||
$WINE C:\\Python27\\Scripts\\easy_install http://www.voidspace.org.uk/python/pycrypto-2.6.1/pycrypto-2.6.1.win32-py2.7.exe
|
||||
# Copy nsis required NSIS_Simple_Firewall_Plugin_1
|
||||
echo "Copying simple firewall plugin for nsis installer"
|
||||
unzip -o downloads/NSIS_Simple_Firewall_Plugin_1.20.zip SimpleFC.dll -d $WINEPREFIX/drive_c/Program\ Files/NSIS/Plugins/x86-ansi/
|
||||
unzip -o downloads/NSIS_Simple_Firewall_Plugin_1.20.zip SimpleFC.dll -d $WINEPREFIX/drive_c/Program\ Files/NSIS/Plugins/x86-unicode/
|
||||
}
|
||||
|
||||
download
|
||||
install_python
|
||||
setup_pip
|
||||
install_packages
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
C:
|
||||
cd \Users\Docker\Downloads
|
||||
mkdir setup
|
||||
|
||||
rem creamos directorio setup, nos bajamos cosas y mas tarde lo borramos
|
||||
rem pero el VC_redist hace falta para el empaquetado, asi que lo descargamos fuera de setup para no borrarlo
|
||||
|
||||
curl https://www.python.org/ftp/python/3.12.3/amd64/core.msi --output setup\core.msi
|
||||
curl https://www.python.org/ftp/python/3.12.3/amd64/dev.msi --output setup\dev.msi
|
||||
curl https://www.python.org/ftp/python/3.12.3/amd64/exe.msi --output setup\exe.msi
|
||||
curl https://www.python.org/ftp/python/3.12.3/amd64/lib.msi --output setup\lib.msi
|
||||
curl https://www.python.org/ftp/python/3.12.3/amd64/path.msi --output setup\path.msi
|
||||
curl https://www.python.org/ftp/python/3.12.3/amd64/pip.msi --output setup\pip.msi
|
||||
curl https://aka.ms/vs/17/release/vc_redist.x64.exe --location --output VC_redist.x64.exe
|
||||
curl https://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download --location --output setup\nsis-install.exe
|
||||
curl http://nsis.sourceforge.net/mediawiki/images/d/d7/NSIS_Simple_Firewall_Plugin_1.20.zip --location --output setup\NSIS_Simple_Firewall_Plugin_1.20.zip
|
||||
|
||||
cd setup
|
||||
msiexec /i core.msi TARGETDIR=C:\Python312
|
||||
msiexec /i dev.msi TARGETDIR=C:\Python312
|
||||
msiexec /i exe.msi TARGETDIR=C:\Python312
|
||||
msiexec /i lib.msi TARGETDIR=C:\Python312
|
||||
msiexec /i path.msi TARGETDIR=C:\Python312
|
||||
msiexec /i pip.msi TARGETDIR=C:\Python312
|
||||
cd ..
|
||||
VC_redist.x64.exe /install /quiet /passive /norestart
|
||||
|
||||
ftype PythonScript=C:\Python312\python.exe "%1" %*
|
||||
assoc .py=PythonScript
|
||||
PATH=C:\Python312;C:\Python312\Scripts;%PATH%
|
||||
setx PATH "C:\Python312;C:\Python312\Scripts;%PATH%"
|
||||
|
||||
python -m pip install --upgrade wheel pip
|
||||
pip install -r F:\src\requirements.txt
|
||||
|
||||
setup\nsis-install.exe /S
|
||||
|
||||
powershell -command "Expand-Archive setup\NSIS_Simple_Firewall_Plugin_1.20.zip nsis-fp"
|
||||
copy nsis-fp\SimpleFC.dll "C:\Program Files (x86)\NSIS\Plugins\x86-ansi\"
|
||||
copy nsis-fp\SimpleFC.dll "C:\Program Files (x86)\NSIS\Plugins\x86-unicode\"
|
||||
rmdir nsis-fp /s /q
|
||||
|
||||
rmdir setup /s /q
|