#718: Integrar código fuente de agente OGAgent en rama de desarrollo.

git-svn-id: https://opengnsys.es/svn/branches/version1.1@4865 a21b9725-9963-47de-94b9-378ad31fedc9
remotes/github/oglive
ramon 2016-04-07 11:27:41 +00:00
commit 11f7a07988
89 changed files with 6237 additions and 0 deletions

72
linux/Makefile 100644
View File

@ -0,0 +1,72 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Directories
SOURCEDIR := ../src
LIBDIR := $(DESTDIR)/usr/share/OGAgent
BINDIR := $(DESTDIR)/usr/bin
SBINDIR = $(DESTDIR)/usr/sbin
APPSDIR := $(DESTDIR)/usr/share/applications
CFGDIR := $(DESTDIR)/etc/ogagent
INITDIR := $(DESTDIR)/etc/init.d
XDGAUTOSTARTDIR := $(DESTDIR)/etc/xdg/autostart
KDEAUTOSTARTDIR := $(DESTDIR)/usr/share/autostart
PYC := $(shell find $(SOURCEDIR) -name '*.py[co]')
CACHES := $(shell find $(SOURCEDIR) -name '__pycache__')
clean:
rm -rf $(PYC) $(CACHES) $(DESTDIR)
install-ogagent:
rm -rf $(DESTDIR)
mkdir -p $(LIBDIR)
mkdir -p $(BINDIR)
mkdir -p $(SBINDIR)
mkdir -p $(APPSDIR)
mkdir -p $(CFGDIR)
mkdir -p $(XDGAUTOSTARTDIR)
mkdir -p $(KDEAUTOSTARTDIR)
mkdir $(LIBDIR)/img
# Cleans up .pyc and cache folders
rm -f $(PYC) $(CACHES)
cp -r $(SOURCEDIR)/opengnsys $(LIBDIR)/opengnsys
cp -r $(SOURCEDIR)/cfg $(LIBDIR)/cfg
cp $(SOURCEDIR)/img/oga.png $(LIBDIR)/img
cp $(SOURCEDIR)/OGAgentUser.py $(LIBDIR)
# QT Dialogs & resources
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
cp $(SOURCEDIR)/OGAgent_rc.py $(LIBDIR)
# Autostart elements for gnome/kde
cp desktop/OGAgentTool.desktop $(XDGAUTOSTARTDIR)
cp desktop/OGAgentTool.desktop $(KDEAUTOSTARTDIR)
# scripts
cp scripts/ogagent $(BINDIR)
cp scripts/OGAgentTool-startup $(BINDIR)
cp scripts/OGAgentTool $(BINDIR)
# Fix permissions
chmod 755 $(BINDIR)/ogagent
chmod 755 $(BINDIR)/OGAgentTool-startup
chmod 755 $(LIBDIR)/OGAgentUser.py
chmod 600 $(LIBDIR)/cfg/ogagent.cfg
# If for red hat based, copy init.d
ifeq ($(DISTRO),rh)
mkdir -p $(INITDIR)
cp debian/ogagent.init $(INITDIR)/ogagent
chmod +x $(INITDIR)/ogagent
ln -fs /usr/share/OGAgent/cfg/ogagent.cfg $(CFGDIR)
ln -fs /usr/share/OGAgent/cfg/ogclient.cfg $(CFGDIR)
endif
# chmod 0755 $(BINDIR)/ogagent
uninstall:
rm -rf $(LIBDIR)
# rm -f $(BINDIR)/ogagent
rm -rf $(CFGDIR)

View File

@ -0,0 +1,34 @@
#!/bin/bash
VERSION=1.0.0
RELEASE=1
top=`pwd`
# Debian based
dpkg-buildpackage -b -d
cat ogagent-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > ogagent-$VERSION.spec
# Now fix dependencies for opensuse
cat ogagent-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"name ogagent"/"name ogagent-opensuse"/g |
sed -e s/"PyQt4"/"python-qt4"/g |
sed -e s/"libXScrnSaver"/"libXss1"/g > 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

View File

@ -0,0 +1,5 @@
ogagent (1.0.0) stable; urgency=medium
* Initial release for OpenGnSys Agent
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 18 Jul 2015 03:18:22 +0200

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1,15 @@
Source: ogagent
Section: admin
Priority: optional
Maintainer: Adolfo Gómez García <agomez@virtualcable.es>
Build-Depends: debhelper (>= 7), po-debconf
Standards-Version: 3.9.2
Homepage: http://www.opengnsys.es
Package: ogagent
Section: admin
Priority: optional
Architecture: all
Depends: policykit-1(>=0.100), python-requests (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python-prctl(>=1.1.1), python (>=2.7), libxss1, ${misc:Depends}
Description: Agent for OpenGnSys
This package provides the required components to allow this machine to work on an environment managed by OpenGnSys.

View File

@ -0,0 +1,26 @@
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
Name: udsactor
Maintainer: Adolfo Gómez García
Source: http://www.udsenterprise.com/
Copyright: 2014 Virtual Cable S.L.U.
License: BSD-3-clause
License: GPL-2+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
`/usr/share/common-licenses/GPL-2'.

View File

@ -0,0 +1 @@
readme.txt

View File

@ -0,0 +1,23 @@
#!/bin/sh -e
### BEGIN INIT INFO
# Provides: ogagent
# Required-Start: $local_fs $remote_fs $network $syslog $named
# Required-Stop: $local_fs $remote_fs $network $syslog $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: OpenGnSys Agent Service
### END INIT INFO
#
# . /lib/lsb/init-functions
case "$1" in
start|stop|restart)
/usr/bin/ogagent $1
;;
force-reload)
/usr/bin/ogagent restart
;;
*) echo "Usage: $0 {start|stop|restart|force-reload}" >&2; exit 1 ;;
esac

View File

@ -0,0 +1,2 @@
/usr/share/OGAgent/cfg/ogagent.cfg /etc/ogagent/ogagent.cfg
/usr/share/OGAgent/cfg/ogclient.cfg /etc/ogagent/ogclient.cfg

View File

@ -0,0 +1,21 @@
#!/bin/sh
. /usr/share/debconf/confmodule
set -e
case "$1" in
configure)
chmod 600 /usr/share/OGAgent/cfg/ogagent.cfg
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0

View File

@ -0,0 +1,5 @@
# 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

View File

@ -0,0 +1,10 @@
#!/bin/sh -e
. /usr/share/debconf/confmodule
set -e
if [ "$1" = "purge" ] ; then
rm -rf /usr/share/OGAgent || true > /dev/null 2>&1
fi

View File

@ -0,0 +1,12 @@
# 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

View File

@ -0,0 +1,2 @@
misc:Depends=
misc:Pre-Depends=

44
linux/debian/rules 100755
View File

@ -0,0 +1,44 @@
#!/usr/bin/make -f
# -*- makefile -*-
configure: configure-stamp
configure-stamp:
dh_testdir
touch configure-stamp
build: build-arch build-indep
build-arch: build-stamp
build-indep: build-stamp
build-stamp: configure-stamp
dh_testdir
$(MAKE)
touch $@
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
dh_clean
install: build
dh_testdir
dh_testroot
dh_prep
dh_installdirs
$(MAKE) DESTDIR=$(CURDIR)/debian/ogagent install-ogagent
binary-arch: build install
# emptyness
binary-indep: build install
dh_testdir
dh_testroot
dh_installchangelogs
dh_installdocs
dh_installdebconf
dh_installinit --no-start
dh_python2=python
dh_compress
dh_link
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep
.PHONY: build clean binary-indep binary install configure

View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -0,0 +1,12 @@
[Desktop Entry]
Name=OpenGnSys Agent Tools
Comment=OpenGnSys Userspace tools
Exec=/usr/bin/OGAgentTool-startup
Icon=/usr/share/OGAgent/img/oga.png
Terminal=false
Type=Application
NoDisplay=true
X-KDE-autostart-after=panel
X-KDE-StartupNotify=false
X-DBUS-StartupType=Unique
X-KDE-UniqueApplet=true

View File

@ -0,0 +1,65 @@
%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 & tools
License: BSD3
Group: Admin
Requires: python-six python-requests PyQt4 libXScrnSaver
Vendor: Virtual Cable S.L.U.
URL: http://www.udsenterprise.com
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/UDSActor
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

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<action id="org.freedesktop.policykit.pkexec.run-UDSActorConfig">
<description>Run UDS Actor Configuration Program</description>
<message>Authentication is required to run UDS Actor Configuration</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/UDSActorConfig</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">TRUE</annotate>
</action>
</policyconfig>

3
linux/readme.txt 100644
View File

@ -0,0 +1,3 @@
OGAgent is the agent intended for OpengGnSys interaction.
Please, visit http://www.opengnsys.es for more information

View File

@ -0,0 +1,6 @@
#!/bin/sh
FOLDER=/usr/share/OGAgent
cd $FOLDER
python OGAgentUser.py $@

View File

@ -0,0 +1,10 @@
#!/bin/sh
# Simple hack to wait for systray to be present
# Exec tool if not already runned by session manager
ps -ef | grep "$USER" | grep -v grep | grep -v OGAgentTool-startup | grep 'OGAgentTool' -q
# If not already running
if [ $? -eq 1 ]; then
sleep 5
exec /usr/bin/OGAgentTool
fi

View File

@ -0,0 +1,6 @@
#!/bin/sh
FOLDER=/usr/share/OGAgent
cd $FOLDER
python -m opengnsys.linux.OGAgentService $@

11
notas.txt 100644
View File

@ -0,0 +1,11 @@
* ¿Como sabemos la direccion del servidor?
* Seguridad. Como hacer el agente seguro (registro con OpenGnsys). Mensajes bidireccionales
* Mensajes. (Moderm)
- Definir mensajes de cliente --> servidor
- Definir mensajes servidor --> cliente
* Logotipo OpenGnsys
* Licencia BSD (relicenciamiento basado en OpenUDS)
* Clientes Windows y Linux inicialmente, parte usuarios?
* "Modern" vs "Legacy" agents
* Particularidades
* ¿Posibles extensiones para "consola" og?

3
requires.txt 100644
View File

@ -0,0 +1,3 @@
six
requests

View File

@ -0,0 +1,57 @@
#!/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)

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="UDSActorService"
version="1.6.0.0"
processorArchitecture="x86"
/>
<description>Description</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

5
src/OGAgent.qrc 100644
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="images">
<file>img/oga.png</file>
</qresource>
</RCC>

338
src/OGAgentUser.py 100644
View File

@ -0,0 +1,338 @@
#!/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 sys
from PyQt4 import QtGui
from PyQt4 import QtCore
import time
import signal
import json
import six
from opengnsys import ipc
from opengnsys import utils
from opengnsys.log import logger
from opengnsys.service import IPC_PORT
from opengnsys import operations
from about_dialog_ui import Ui_OGAAboutDialog
from message_dialog_ui import Ui_OGAMessageDialog
from opengnsys.scriptThread import ScriptExecutorThread
from opengnsys import VERSION
from opengnsys.config import readConfig
from opengnsys.loader import loadModules
trayIcon = None
def sigTerm(sigNo, stackFrame):
if trayIcon:
trayIcon.quit()
# About dialog
class OGAAboutDialog(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.ui = Ui_OGAAboutDialog()
self.ui.setupUi(self)
self.ui.VersionLabel.setText("Version " + VERSION)
def closeDialog(self):
self.hide()
class OGAMessageDialog(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.ui = Ui_OGAMessageDialog()
self.ui.setupUi(self)
def message(self, message):
self.ui.message.setText(message)
self.show()
def closeDialog(self):
self.hide()
class MessagesProcessor(QtCore.QThread):
logoff = QtCore.pyqtSignal(name='logoff')
message = QtCore.pyqtSignal(tuple, name='message')
script = QtCore.pyqtSignal(QtCore.QString, name='script')
exit = QtCore.pyqtSignal(name='exit')
def __init__(self, port):
super(self.__class__, self).__init__()
# Retries connection for a while
for _ in range(10):
try:
self.ipc = ipc.ClientIPC(port)
self.ipc.start()
break
except Exception:
logger.debug('IPC Server is not reachable')
self.ipc = None
time.sleep(2)
self.running = False
def stop(self):
self.running = False
if self.ipc:
self.ipc.stop()
def isAlive(self):
return self.ipc is not None
def sendLogin(self, userName):
if self.ipc:
self.ipc.sendLogin(userName)
def sendLogout(self, userName):
if self.ipc:
self.ipc.sendLogout(userName)
def run(self):
if self.ipc is None:
return
self.running = True
# Wait a bit so we ensure IPC thread is running...
time.sleep(2)
while self.running and self.ipc.running:
try:
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')
self.message.emit((module, message, data))
elif msgId == ipc.MSG_LOGOFF:
self.logoff.emit()
elif msgId == ipc.MSG_SCRIPT:
self.script.emit(QtCore.QString.fromUtf8(data))
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??)')
if self.ipc.running is False and self.running is True:
logger.warn('Lost connection with Service, closing program')
self.exit.emit()
class OGASystemTray(QtGui.QSystemTrayIcon):
def __init__(self, app_, parent=None):
self.app = app_
self.config = readConfig(client=True)
# 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')
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
self.menu = QtGui.QMenu(parent)
exitAction = self.menu.addAction("About")
exitAction.triggered.connect(self.about)
self.setContextMenu(self.menu)
self.ipc = MessagesProcessor(self.ipcport)
if self.ipc.isAlive() is False:
raise Exception('No connection to service, exiting.')
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.timerFnc)
self.stopped = False
self.ipc.message.connect(self.message)
self.ipc.exit.connect(self.quit)
self.ipc.script.connect(self.executeScript)
self.ipc.logoff.connect(self.logoff)
self.aboutDlg = OGAAboutDialog()
self.msgDlg = OGAMessageDialog()
self.timer.start(1000) # Launch idle checking every 1 seconds
self.ipc.start()
def initialize(self):
# Load modules and activate them
# Also, sends "login" event to service
self.modules = loadModules(self, client=True)
logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
# Send init to all modules
validMods = []
for mod in self.modules:
try:
logger.debug('Activating module {}'.format(mod.name))
mod.activate()
validMods.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
# If this is running, it's because he have logged in, inform service of this fact
self.ipc.sendLogin(operations.getCurrentUser())
def deinitialize(self):
for mod in reversed(self.modules): # Deinitialize reversed of initialization
try:
logger.debug('Deactivating module {}'.format(mod.name))
mod.deactivate()
except Exception as e:
logger.exception()
logger.error("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
def timerFnc(self):
pass
def message(self, msg):
'''
Processes the message sent asynchronously, msg is an QString
'''
try:
logger.debug('msg: {}, {}'.format(type(msg), msg))
module, message, data = msg
except Exception as e:
logger.error('Got exception {} processing message {}'.format(e, msg))
return
for v in self.modules:
if v.name == module: # Case Sensitive!!!!
try:
logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
v.processMessage(message, json.loads(data))
return
except Exception as e:
logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
logger.error('Module {} not found, messsage {} not sent'.format(module, message))
def executeScript(self, script):
logger.debug('Executing script')
script = six.text_type(script.toUtf8()).decode('base64')
th = ScriptExecutorThread(script)
th.start()
def logoff(self):
logger.debug('Logoff invoked')
operations.logoff() # Invoke log off
def about(self):
self.aboutDlg.exec_()
def quit(self):
logger.debug('Quit invoked')
if self.stopped is False:
self.stopped = True
try:
self.deinitialize()
except Exception:
logger.exception()
logger.error('Got exception deinitializing modules')
try:
# If we close Client, send Logoff to Broker
self.ipc.sendLogout(operations.getCurrentUser())
self.timer.stop()
self.ipc.stop()
except Exception:
# May we have lost connection with server, simply exit in that case
pass
try:
# operations.logoff() # Uncomment this after testing to logoff user
pass
except Exception:
pass
self.app.quit()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
if not QtGui.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)
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(utils.exceptionToMessage(e)))
sys.exit(1)
try:
trayIcon.initialize() # Initialize modules, etc..
except Exception as e:
logger.exception()
logger.error('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
trayIcon.quit()
sys.exit(1)
trayIcon.show()
# Catch kill and logout user :)
signal.signal(signal.SIGTERM, sigTerm)
res = app.exec_()
logger.debug('Exiting')
trayIcon.quit()
sys.exit(res)

289
src/OGAgent_rc.py 100644
View File

@ -0,0 +1,289 @@
# -*- 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()

240
src/about-dialog.ui 100644
View File

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OGAAboutDialog</class>
<widget class="QDialog" name="OGAAboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>466</width>
<height>402</height>
</rect>
</property>
<property name="font">
<font>
<family>Verdana</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>About OGAgent</string>
</property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="LogoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/images/img/oga.png&quot;/&gt;OpenGnsys Agent Tools&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="TitleLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:600;&quot;&gt;OpenGnsys Agent&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="VersionLabel">
<property name="text">
<string>Version 1.0.0</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="aboutTab">
<attribute name="title">
<string>&amp;About</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QTextBrowser" name="aboutBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif'; font-weight:600;&quot;&gt;(c) 2014, Virtual Cable S.L.U.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-style:italic;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://www.udsenterprise.com&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.opengnsys.es&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://www.openuds.org&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.udsenterprise.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="authorsTab">
<attribute name="title">
<string>A&amp;uthors</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QTextBrowser" name="authorsBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif';&quot;&gt;Adolfo Gómez García &amp;lt;agomez@virtualcable.es&amp;gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="licenseTab">
<attribute name="title">
<string>&amp;License Agreement</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QTextBrowser" name="licenseBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;Copyright (c) 2014 Virtual Cable S.L.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without modification,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; * Redistributions of source code must retain the above copyright notice,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; this list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; * Redistributions in binary form must reproduce the above copyright notice,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; this list of conditions and the following disclaimer in the documentation&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; and/or other materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; * Neither the name of Virtual Cable S.L. nor the names of its contributors&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; may be used to endorse or promote products derived from this software&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="OGAgent.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>clicked(QAbstractButton*)</signal>
<receiver>OGAAboutDialog</receiver>
<slot>closeDialog()</slot>
<hints>
<hint type="sourcelabel">
<x>432</x>
<y>381</y>
</hint>
<hint type="destinationlabel">
<x>282</x>
<y>362</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>closeDialog()</slot>
</slots>
</ui>

View File

@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'about-dialog.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_OGAAboutDialog(object):
def setupUi(self, OGAAboutDialog):
OGAAboutDialog.setObjectName(_fromUtf8("OGAAboutDialog"))
OGAAboutDialog.resize(466, 402)
font = QtGui.QFont()
font.setFamily(_fromUtf8("Verdana"))
font.setPointSize(9)
OGAAboutDialog.setFont(font)
OGAAboutDialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
OGAAboutDialog.setModal(True)
self.vboxlayout = QtGui.QVBoxLayout(OGAAboutDialog)
self.vboxlayout.setMargin(9)
self.vboxlayout.setSpacing(9)
self.vboxlayout.setObjectName(_fromUtf8("vboxlayout"))
self.LogoLabel = QtGui.QLabel(OGAAboutDialog)
self.LogoLabel.setObjectName(_fromUtf8("LogoLabel"))
self.vboxlayout.addWidget(self.LogoLabel)
spacerItem = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
self.vboxlayout.addItem(spacerItem)
self.TitleLabel = QtGui.QLabel(OGAAboutDialog)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.vboxlayout.addWidget(self.TitleLabel)
self.VersionLabel = QtGui.QLabel(OGAAboutDialog)
self.VersionLabel.setObjectName(_fromUtf8("VersionLabel"))
self.vboxlayout.addWidget(self.VersionLabel)
spacerItem1 = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
self.vboxlayout.addItem(spacerItem1)
self.tabWidget = QtGui.QTabWidget(OGAAboutDialog)
self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
self.aboutTab = QtGui.QWidget()
self.aboutTab.setObjectName(_fromUtf8("aboutTab"))
self.vboxlayout1 = QtGui.QVBoxLayout(self.aboutTab)
self.vboxlayout1.setMargin(9)
self.vboxlayout1.setSpacing(6)
self.vboxlayout1.setObjectName(_fromUtf8("vboxlayout1"))
self.aboutBrowser = QtGui.QTextBrowser(self.aboutTab)
self.aboutBrowser.setOpenExternalLinks(True)
self.aboutBrowser.setObjectName(_fromUtf8("aboutBrowser"))
self.vboxlayout1.addWidget(self.aboutBrowser)
self.tabWidget.addTab(self.aboutTab, _fromUtf8(""))
self.authorsTab = QtGui.QWidget()
self.authorsTab.setObjectName(_fromUtf8("authorsTab"))
self.vboxlayout2 = QtGui.QVBoxLayout(self.authorsTab)
self.vboxlayout2.setMargin(9)
self.vboxlayout2.setSpacing(6)
self.vboxlayout2.setObjectName(_fromUtf8("vboxlayout2"))
self.authorsBrowser = QtGui.QTextBrowser(self.authorsTab)
self.authorsBrowser.setOpenExternalLinks(True)
self.authorsBrowser.setObjectName(_fromUtf8("authorsBrowser"))
self.vboxlayout2.addWidget(self.authorsBrowser)
self.tabWidget.addTab(self.authorsTab, _fromUtf8(""))
self.licenseTab = QtGui.QWidget()
self.licenseTab.setObjectName(_fromUtf8("licenseTab"))
self.vboxlayout3 = QtGui.QVBoxLayout(self.licenseTab)
self.vboxlayout3.setMargin(9)
self.vboxlayout3.setSpacing(6)
self.vboxlayout3.setObjectName(_fromUtf8("vboxlayout3"))
self.licenseBrowser = QtGui.QTextBrowser(self.licenseTab)
self.licenseBrowser.setObjectName(_fromUtf8("licenseBrowser"))
self.vboxlayout3.addWidget(self.licenseBrowser)
self.tabWidget.addTab(self.licenseTab, _fromUtf8(""))
self.vboxlayout.addWidget(self.tabWidget)
self.buttonBox = QtGui.QDialogButtonBox(OGAAboutDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.vboxlayout.addWidget(self.buttonBox)
self.retranslateUi(OGAAboutDialog)
self.tabWidget.setCurrentIndex(0)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("clicked(QAbstractButton*)")), OGAAboutDialog.closeDialog)
QtCore.QMetaObject.connectSlotsByName(OGAAboutDialog)
def retranslateUi(self, OGAAboutDialog):
OGAAboutDialog.setWindowTitle(_translate("OGAAboutDialog", "About OGAgent", None))
self.LogoLabel.setText(_translate("OGAAboutDialog", "<html><head/><body><p><img src=\":/images/img/oga.png\"/>OpenGnsys Agent Tools</p></body></html>", None))
self.TitleLabel.setText(_translate("OGAAboutDialog", "<html><head/><body><p><span style=\" font-family:\'Sans Serif\'; font-size:9pt; font-weight:600;\">OpenGnsys Agent</span></p></body></html>", None))
self.VersionLabel.setText(_translate("OGAAboutDialog", "Version 1.0.0", None))
self.aboutBrowser.setHtml(_translate("OGAAboutDialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Verdana\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'Sans Serif\';\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'Sans Serif\'; font-weight:600;\">(c) 2014, Virtual Cable S.L.U.</span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'Sans Serif\'; font-style:italic;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"http://www.udsenterprise.com\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt; text-decoration: underline; color:#0000ff;\">http://www.opengnsys.es</span></a></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"http://www.openuds.org\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt; text-decoration: underline; color:#0000ff;\">http://www.udsenterprise.com</span></a></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'Sans Serif\';\"><br /></p></body></html>", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.aboutTab), _translate("OGAAboutDialog", "&About", None))
self.authorsBrowser.setHtml(_translate("OGAAboutDialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Verdana\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'Sans Serif\';\">Adolfo Gómez García &lt;agomez@virtualcable.es&gt;</span></p></body></html>", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.authorsTab), _translate("OGAAboutDialog", "A&uthors", None))
self.licenseBrowser.setHtml(_translate("OGAAboutDialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Verdana\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">Copyright (c) 2014 Virtual Cable S.L.</span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">All rights reserved.</span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">Redistribution and use in source and binary forms, with or without modification,</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">are permitted provided that the following conditions are met:</span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> * Redistributions of source code must retain the above copyright notice,</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> this list of conditions and the following disclaimer.</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> * Redistributions in binary form must reproduce the above copyright notice,</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> this list of conditions and the following disclaimer in the documentation</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> and/or other materials provided with the distribution.</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> * Neither the name of Virtual Cable S.L. nor the names of its contributors</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> may be used to endorse or promote products derived from this software</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"> without specific prior written permission.</span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt;\">OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"><br /></p></body></html>", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.licenseTab), _translate("OGAAboutDialog", "&License Agreement", None))
import OGAgent_rc
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
OGAAboutDialog = QtGui.QDialog()
ui = Ui_OGAAboutDialog()
ui.setupUi(OGAAboutDialog)
OGAAboutDialog.show()
sys.exit(app.exec_())

View File

@ -0,0 +1,21 @@
[opengnsys]
# Listen address & port of REST
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
# Remote OpenGnsys Service
remote=https://192.168.2.10/opengnsys/rest
# Log Level, if ommited, will be set to INFO
log=DEBUG
# 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

View File

@ -0,0 +1,11 @@
[opengnsys]
# Log Level, if ommited, will be set to INFO
log=DEBUG
# 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
src/img/oga-512.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
src/img/oga.ico 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
src/img/oga.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

27
src/license.txt 100644
View File

@ -0,0 +1,27 @@
Copyright (c) 2012-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.

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OGAMessageDialog</class>
<widget class="QDialog" name="OGAMessageDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>339</width>
<height>188</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Verdana</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="windowTitle">
<string>UDS Actor</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>321</width>
<height>171</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextBrowser" name="message"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>15</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>clicked(QAbstractButton*)</signal>
<receiver>OGAMessageDialog</receiver>
<slot>closeDialog()</slot>
<hints>
<hint type="sourcelabel">
<x>203</x>
<y>161</y>
</hint>
<hint type="destinationlabel">
<x>337</x>
<y>125</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>closeDialog()</slot>
</slots>
</ui>

View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'message-dialog.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_OGAMessageDialog(object):
def setupUi(self, OGAMessageDialog):
OGAMessageDialog.setObjectName(_fromUtf8("OGAMessageDialog"))
OGAMessageDialog.resize(339, 188)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(OGAMessageDialog.sizePolicy().hasHeightForWidth())
OGAMessageDialog.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setFamily(_fromUtf8("Verdana"))
font.setPointSize(10)
OGAMessageDialog.setFont(font)
self.verticalLayoutWidget = QtGui.QWidget(OGAMessageDialog)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 321, 171))
self.verticalLayoutWidget.setObjectName(_fromUtf8("verticalLayoutWidget"))
self.verticalLayout = QtGui.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.message = QtGui.QTextBrowser(self.verticalLayoutWidget)
self.message.setObjectName(_fromUtf8("message"))
self.verticalLayout.addWidget(self.message)
spacerItem = QtGui.QSpacerItem(20, 15, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtGui.QDialogButtonBox(self.verticalLayoutWidget)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(OGAMessageDialog)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("clicked(QAbstractButton*)")), OGAMessageDialog.closeDialog)
QtCore.QMetaObject.connectSlotsByName(OGAMessageDialog)
def retranslateUi(self, OGAMessageDialog):
OGAMessageDialog.setWindowTitle(_translate("OGAMessageDialog", "UDS Actor", None))
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
OGAMessageDialog = QtGui.QDialog()
ui = Ui_OGAMessageDialog()
ui.setupUi(OGAMessageDialog)
OGAMessageDialog.show()
sys.exit(app.exec_())

View File

@ -0,0 +1,160 @@
# -*- 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 __future__ import unicode_literals
import requests
import logging
import json
import warnings
from .log import logger
from .utils import exceptionToMessage
VERIFY_CERT = False
class RESTError(Exception):
ERRCODE = 0
class ConnectionError(RESTError):
ERRCODE = -1
# Disable warnings log messages
try:
import urllib3 # @UnusedImport
except Exception:
from requests.packages import urllib3 # @Reimport
try:
urllib3.disable_warnings() # @UndefinedVariable
warnings.simplefilter("ignore")
except Exception:
pass # In fact, isn't too important, but wil log warns to logging file
class REST(object):
'''
Simple interface to remote REST apis.
The constructor expects the "base url" as parameter, that is, the url that will be common on all REST requests
Remember that this is a helper for "easy of use". You can provide your owns using requests lib for example.
Examples:
v = REST('https://example.com/rest/v1/') (Can omit trailing / if desired)
v.sendMessage('hello?param1=1&param2=2')
This will generate a GET message to https://example.com/rest/v1/hello?param1=1&param2=2, and return the deserialized JSON result or an exception
v.sendMessage('hello?param1=1&param2=2', {'name': 'mario' })
This will generate a POST message to https://example.com/rest/v1/hello?param1=1&param2=2, with json encoded body {'name': 'mario' }, and also returns
the deserialized JSON result or raises an exception in case of error
'''
def __init__(self, url):
'''
Initializes the REST helper
url is the full url of the REST API Base, as for example "https://example.com/rest/v1".
@param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired.
'''
self.endpoint = url
if self.endpoint[-1] != '/':
self.endpoint += '/'
# Some OSs ships very old python requests lib implementations, workaround them...
try:
self.newerRequestLib = requests.__version__.split('.')[0] >= '1'
except Exception:
self.newerRequestLib = False # I no version, guess this must be an old requests
# Disable logging requests messages except for errors, ...
logging.getLogger("requests").setLevel(logging.CRITICAL)
# Tries to disable all warnings
try:
warnings.simplefilter("ignore") # Disables all warnings
except Exception:
pass
def _getUrl(self, method):
'''
Internal method
Composes the URL based on "method"
@param method: Method to append to base url for composition
'''
url = self.endpoint + method
return url
def _request(self, url, data=None):
'''
Launches the request
@param url: The url to obtain
@param data: if None, the request will be sent as a GET request. If != None, the request will be sent as a POST, with data serialized as JSON in the body.
'''
try:
if data is None:
logger.debug('Requesting using GET (no data provided) {}'.format(url))
# Old requests version does not support verify, but they do not checks ssl certificate by default
if self.newerRequestLib:
r = requests.get(url, verify=VERIFY_CERT)
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)
else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'})
r = json.loads(r.content) # Using instead of r.json() to make compatible with oooold rquests lib versions
except requests.exceptions.RequestException as e:
raise ConnectionError(e)
except Exception as e:
raise ConnectionError(exceptionToMessage(e))
return r
def sendMessage(self, msg, data=None, processData=True):
'''
Sends a message to remote REST server
@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))
if processData and data is not None:
data = json.dumps(data)
url = self._getUrl(msg)
logger.debug('Requesting {}'.format(url))
return self._request(url, data)

View File

@ -0,0 +1,57 @@
# -*- 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
# On centos, old six release does not includes byte2int, nor six.PY2
import six
import modules
from RESTApi import REST, RESTError
VERSION = '1.0.0'
__title__ = 'OpenGnSys Agent'
__version__ = VERSION
__build__ = 0x010750
__author__ = 'Adolfo Gómez'
__license__ = "BSD 3-clause"
__copyright__ = "Copyright VirtualCable S.L.U."
if not hasattr(six, 'byte2int'):
if six.PY3:
import operator
six.byte2int = operator.itemgetter(0)
else:
def _byte2int(bs):
return ord(bs[0])
six.byte2int = _byte2int

View File

@ -0,0 +1,101 @@
# -*- 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 tempfile import gettempdir
from os.path import exists, join
CERTFILE = 'OGAgent.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

View File

@ -0,0 +1,59 @@
# -*- 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
'''
# pylint: disable=unused-wildcard-import, wildcard-import
from __future__ import unicode_literals
from ConfigParser import SafeConfigParser
from .log import logger
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()
if client is True:
fname = 'ogclient.cfg'
else:
fname = 'ogagent.cfg'
if len(cfg.read('cfg/{}'.format(fname))) == 0:
# No configuration found
return None
return cfg

View File

@ -0,0 +1,150 @@
# -*- 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
import six
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
class HTTPServerHandler(BaseHTTPRequestHandler):
service = None
protocol_version = 'HTTP/1.0'
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 = {}
for v in self.service.modules:
if v.name == path[0]: # Case Sensitive!!!!
return (v, path[1:], params)
return (None, path, params)
def notifyMessage(self, module, path, getParams, postParams):
'''
Locates witch module will process the message based on path (first folder on url path)
'''
try:
data = module.processServerMessage(path, getParams, postParams)
self.sendJsonResponse(data)
except Exception as e:
logger.exception()
self.sendJsonError(500, exceptionToMessage(e))
def do_GET(self):
module, path, params = self.parseUrl()
self.notifyMessage(module, path, params, None)
def do_POST(self):
module, path, getParams = self.parseUrl()
# Tries to get json content
try:
length = int(self.headers.getheader('content-length'))
content = self.rfile.read(length)
logger.debug('length: {}, content >>{}<<'.format(length, content))
postParams = json.loads(content)
except Exception as e:
self.sendJsonError(500, exceptionToMessage(e))
self.notifyMessage(module, path, getParams, postParams)
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 # 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)
logger.debug('Initialized HTTPS Server thread on {}'.format(address))
def getServerUrl(self):
return 'https://{}:{}/'.format(self.server.server_address[0], self.server.server_address[1])
def stop(self):
self.server.shutdown()
def run(self):
self.server.serve_forever()

View File

@ -0,0 +1,416 @@
# -*- 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 socket
import threading
import six
import traceback
import json
from opengnsys.utils import toUnicode
from opengnsys.log import logger
# The IPC Server will wait for connections from clients
# Clients will open socket, and wait for data from server
# The messages sent (from server) will be the following (subject to future changes):
# Message_id Data Action
# ------------ -------- --------------------------
# MSG_LOGOFF None Logout user from session
# MSG_MESSAGE message,level Display a message with level (INFO, WARN, ERROR, FATAL) # TODO: Include level, right now only has message
# MSG_SCRIPT python script Execute an specific python script INSIDE CLIENT environment (this messages is not sent right now)
# The messages received (sent from client) will be the following:
# Message_id Data Action
# ------------ -------- --------------------------
# REQ_LOGOUT Logout user from session
# REQ_INFORMATION None Request information from ipc server (maybe configuration parameters in a near future)
# REQ_LOGIN python script Execute an specific python script INSIDE CLIENT environment (this messages is not sent right now)
#
# All messages are in the form:
# 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
# Client messages
MSG_LOGOFF = 0xA1 # Request log off from an user
MSG_MESSAGE = 0xB2
MSG_SCRIPT = 0xC3
# Request messages
REQ_MESSAGE = 0xD4
REQ_LOGIN = 0xE5
REQ_LOGOUT = 0xF6
# Reverse msgs dict for debugging
REV_DICT = {
MSG_LOGOFF: 'MSG_LOGOFF',
MSG_MESSAGE: 'MSG_MESSAGE',
MSG_SCRIPT: 'MSG_SCRIPT',
REQ_LOGIN: 'REQ_LOGIN',
REQ_LOGOUT: 'REQ_LOGOUT',
REQ_MESSAGE: 'REQ_MESSAGE'
}
MAGIC = b'\x4F\x47\x41\x00' # OGA in hexa with a padded 0 to the right
# States for client processor
ST_SECOND_BYTE = 0x01
ST_RECEIVING = 0x02
ST_PROCESS_MESSAGE = 0x02
class ClientProcessor(threading.Thread):
def __init__(self, parent, clientSocket):
super(self.__class__, self).__init__()
self.parent = parent
self.clientSocket = clientSocket
self.running = False
self.messages = six.moves.queue.Queue(32) # @UndefinedVariable
def stop(self):
logger.debug('Stoping client processor')
self.running = False
def processRequest(self, msg, data):
logger.debug('Got Client message {}={}'.format(msg, REV_DICT.get(msg)))
if self.parent.clientMessageProcessor is not None:
self.parent.clientMessageProcessor(msg, data)
def run(self):
self.running = True
self.clientSocket.setblocking(0)
state = None
recv_msg = None
recv_data = None
while self.running:
try:
counter = 1024
while counter > 0: # So we process at least the incoming queue every XX bytes readed
counter -= 1
b = self.clientSocket.recv(1)
if b == b'':
# Client disconnected
self.running = False
break
buf = six.byte2int(b) # 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))
state = buf
recv_msg = buf
continue # Get next byte
else:
logger.debug('Got unexpected data {}'.format(buf))
elif state in (REQ_MESSAGE, REQ_LOGIN, REQ_LOGOUT):
logger.debug('First length byte is {}'.format(buf))
msg_len = buf
state = ST_SECOND_BYTE
continue
elif state == ST_SECOND_BYTE:
msg_len += buf << 8
logger.debug('Second length byte is {}, len is {}'.format(buf, msg_len))
if msg_len == 0:
self.processRequest(recv_msg, None)
state = None
break
state = ST_RECEIVING
recv_data = b''
continue
elif state == ST_RECEIVING:
recv_data += six.int2byte(buf)
msg_len -= 1
if msg_len == 0:
self.processRequest(recv_msg, recv_data)
recv_data = None
state = None
break
else:
logger.debug('Got invalid message from request: {}, state: {}'.format(buf, state))
except socket.error as e:
# If no data is present, no problem at all, pass to check messages
pass
except Exception as e:
tb = traceback.format_exc()
logger.error('Error: {}, trace: {}'.format(e, tb))
if self.running is False:
break
try:
msg = self.messages.get(block=True, timeout=1)
except six.moves.queue.Empty: # No message got in time @UndefinedVariable
continue
logger.debug('Got message {}={}'.format(msg, REV_DICT.get(msg[0])))
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
try:
self.clientSocket.sendall(data)
except socket.error as e:
# Send data error
logger.debug('Socket connection is no more available: {}'.format(e.args))
self.running = False
except Exception as e:
logger.error('Invalid message in queue: {}'.format(e))
logger.debug('Client processor stopped')
try:
self.clientSocket.close()
except Exception:
pass # If can't close, nothing happens, just end thread
class ServerIPC(threading.Thread):
def __init__(self, listenPort, clientMessageProcessor=None):
super(self.__class__, self).__init__()
self.port = listenPort
self.running = False
self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.threads = []
self.clientMessageProcessor = clientMessageProcessor
def stop(self):
logger.debug('Stopping Server IPC')
self.running = False
for t in self.threads:
t.stop()
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect(('localhost', self.port))
self.serverSocket.close()
for t in self.threads:
t.join()
def sendMessage(self, msgId, msgData):
'''
Notify message to all listening threads
'''
logger.debug('Sending message {}({}),{} to all clients'.format(msgId, REV_DICT.get(msgId), msgData))
# Convert to bytes so length is correctly calculated
if isinstance(msgData, six.text_type):
msgData = msgData.encode('utf8')
for t in self.threads:
if t.isAlive():
logger.debug('Sending to {}'.format(t))
t.messages.put((msgId, msgData))
def sendLoggofMessage(self):
self.sendMessage(MSG_LOGOFF, '')
def sendMessageMessage(self, message):
self.sendMessage(MSG_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():
logger.debug('Thread {} is alive'.format(t))
aliveThreads.append(t)
self.threads[:] = aliveThreads
def run(self):
self.running = True
self.serverSocket.bind(('localhost', self.port))
self.serverSocket.setblocking(1)
self.serverSocket.listen(4)
while True:
try:
(clientSocket, address) = self.serverSocket.accept()
# Stop processing if thread is mean to stop
if self.running is False:
break
logger.debug('Got connection from {}'.format(address))
self.cleanupFinishedThreads() # House keeping
logger.debug('Starting new thread, current: {}'.format(self.threads))
t = ClientProcessor(self, clientSocket)
self.threads.append(t)
t.start()
except Exception as e:
logger.error('Got an exception on Server ipc thread: {}'.format(e))
class ClientIPC(threading.Thread):
def __init__(self, listenPort):
super(ClientIPC, self).__init__()
self.port = listenPort
self.running = False
self.clientSocket = None
self.messages = six.moves.queue.Queue(32) # @UndefinedVariable
self.connect()
def stop(self):
self.running = False
def getMessage(self):
while self.running:
try:
return self.messages.get(timeout=1)
except six.moves.queue.Empty: # @UndefinedVariable
continue
return None
def sendRequestMessage(self, msg, data=None):
logger.debug('Sending request for msg: {}({}), {}'.format(msg, REV_DICT.get(msg), data))
if data is None:
data = b''
if isinstance(data, six.text_type): # Convert to bytes if necessary
data = data.encode('utf-8')
l = len(data)
msg = six.int2byte(msg) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + data
self.clientSocket.sendall(msg)
def sendLogin(self, username):
self.sendRequestMessage(REQ_LOGIN, username)
def sendLogout(self, username):
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):
msg = b''
while self.running and len(msg) < number:
try:
buf = self.clientSocket.recv(number - len(msg))
if buf == b'':
logger.debug('Buf {}, msg {}({})'.format(buf, msg, REV_DICT.get(msg)))
self.running = False
break
msg += buf
except socket.timeout:
pass
if self.running is False:
logger.debug('Not running, returning None')
return None
return msg
def connect(self):
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.connect(('localhost', self.port))
self.clientSocket.settimeout(2) # Static, custom socket timeout of 2 seconds for local connection (no network)
def run(self):
self.running = True
while self.running:
try:
msg = b''
# We look for magic message header
while self.running: # Wait for MAGIC
try:
buf = self.clientSocket.recv(len(MAGIC) - len(msg))
if buf == b'':
self.running = False
break
msg += buf
if len(msg) != len(MAGIC):
continue # Do not have message
if msg != MAGIC: # Skip first byte an continue searchong
msg = msg[1:]
continue
break
except socket.timeout: # Timeout is here so we can get stop thread
continue
if self.running is False:
break
# Now we get message basic data (msg + datalen)
msg = bytearray(self.receiveBytes(3))
# We have the magic header, here comes the message itself
if msg is None:
continue
msgId = msg[0]
dataLen = msg[1] + (msg[2] << 8)
if msgId not in (MSG_LOGOFF, MSG_MESSAGE, MSG_SCRIPT):
raise Exception('Invalid message id: {}'.format(msgId))
data = self.receiveBytes(dataLen)
if data is None:
continue
self.messages.put((msgId, data))
self.messageReceived()
except socket.error as e:
logger.error('Communication with server got an error: {}'.format(toUnicode(e.strerror)))
self.running = False
return
except Exception as e:
tb = traceback.format_exc()
logger.error('Error: {}, trace: {}'.format(e, tb))
try:
self.clientSocket.close()
except Exception:
pass # If can't close, nothing happens, just end thread

View File

@ -0,0 +1,147 @@
# -*- 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.service import CommonService
from opengnsys.service import IPC_PORT
from opengnsys import ipc
from opengnsys.log import logger
from opengnsys.linux.daemon import Daemon
import sys
import signal
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
def set_proctitle(_):
pass
class OGAgentSvc(Daemon, CommonService):
def __init__(self, args=None):
Daemon.__init__(self, '/var/run/opengnsys-agent.pid')
CommonService.__init__(self)
def run(self):
logger.debug('** Running Daemon **')
set_proctitle('OGAgent')
self.initialize()
# Call modules initialization
# They are called in sequence, no threading is done at this point, so ensure modules onActivate always returns
# *********************
# * Main Service loop *
# *********************
# Counter used to check ip changes only once every 10 seconds, for
# example
try:
while self.isAlive:
# In milliseconds, will break
self.doWait(1000)
except (KeyboardInterrupt, SystemExit) as e:
logger.error('Requested exit of main loop')
except Exception as e:
logger.exception()
logger.error('Caught exception on main loop: {}'.format(e))
self.terminate()
self.notifyStop()
def signal_handler(self, signal, frame):
self.isAlive = False
sys.stderr.write("signal handler: {}".format(signal))
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')
if len(sys.argv) == 5 and sys.argv[1] == 'message':
logger.debug('Running client opengnsys')
client = None
try:
client = ipc.ClientIPC(IPC_PORT)
client.sendMessage(sys.argv[2], sys.argv[3], json.loads(sys.argv[4]))
sys.exit(0)
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
try:
client = ipc.ClientIPC(IPC_PORT)
if 'login' == sys.argv[1]:
client.sendLogin(sys.argv[2])
sys.exit(0)
elif 'logout' == sys.argv[1]:
client.sendLogout(sys.argv[2])
sys.exit(0)
else:
usage()
except Exception as e:
logger.error(e)
elif len(sys.argv) != 2:
usage()
logger.debug('Executing actor')
daemon = OGAgentSvc()
signal.signal(signal.SIGTERM, daemon.signal_handler)
signal.signal(signal.SIGINT, daemon.signal_handler)
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.stop()
elif 'restart' == sys.argv[1]:
daemon.restart()
elif 'fg' == sys.argv[1]:
daemon.run()
else:
usage()
sys.exit(0)
else:
usage()

View File

@ -0,0 +1,32 @@
# -*- 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

View File

@ -0,0 +1,182 @@
# -*- 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: : 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
from opengnsys.log import logger
from signal import SIGTERM
class Daemon:
"""
A generic daemon class.
Usage: subclass the Daemon class and override the run() method
"""
def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def daemonize(self):
"""
do the UNIX double-fork magic, see Stevens' "Advanced
Programming in the UNIX Environment" for details (ISBN 0201563177)
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
"""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as e:
logger.error("fork #1 error: {}".format(e))
sys.stderr.write("fork #1 failed: {}\n".format(e))
sys.exit(1)
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as e:
logger.error("fork #2 error: {}".format(e))
sys.stderr.write("fork #2 failed: {}\n".format(e))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(self.stdin, 'r')
so = open(self.stdout, 'a+')
se = open(self.stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
with open(self.pidfile, 'w+') as f:
f.write("{}\n".format(pid))
def delpid(self):
try:
os.remove(self.pidfile)
except Exception:
# Not found/not permissions or whatever...
pass
def start(self):
"""
Start the daemon
"""
logger.debug('Starting daemon')
# Check for a pidfile to see if the daemon already runs
try:
pf = open(self.pidfile, 'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if pid:
message = "pidfile {} already exist. Daemon already running?\n".format(pid)
logger.error(message)
sys.stderr.write(message)
sys.exit(1)
# Start the daemon
self.daemonize()
try:
self.run()
except Exception as e:
logger.error('Exception running process: {}'.format(e))
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
def stop(self):
"""
Stop the daemon
"""
# Get the pid from the pidfile
try:
pf = open(self.pidfile, 'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if pid is None:
message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile)
logger.info(message)
# sys.stderr.write(message)
return # not an error in a restart
# Try killing the daemon process
try:
while True:
os.kill(pid, SIGTERM)
time.sleep(1)
except OSError as err:
if err.errno == 3: # No such process
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
sys.stderr.write(err)
sys.exit(1)
def restart(self):
"""
Restart the daemon
"""
self.stop()
self.start()
# Overridables
def run(self):
"""
You should override this method when you subclass Daemon. It will be called after the process has been
daemonized by start() or restart().
"""

View File

@ -0,0 +1,80 @@
# -*- 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 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
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
# 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
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)
return
except Exception:
pass
# Logger can't be set
self.logger = None
def log(self, level, message):
# Debug messages are logged to a file
# 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)
def isWindows(self):
return False
def isLinux(self):
return True

View File

@ -0,0 +1,271 @@
# -*- 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 socket
import platform
import fcntl
import os
import ctypes # @UnusedImport
import ctypes.util
import subprocess
import struct
import array
import six
from opengnsys import utils
from .renamer import rename
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:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15])))
return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1])
except Exception:
return None
def _getIpAddr(ifname):
'''
Returns the ip address of an interface
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:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return six.text_type(socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack(str('256s'), ifname[:15])
)[20:24]))
except Exception:
return None
def _getInterfaces():
'''
Returns a list of interfaces names coded in utf-8
'''
max_possible = 128 # arbitrary. raise if needed.
space = max_possible * 16
if platform.architecture()[0] == '32bit':
offset, length = 32, 32
elif platform.architecture()[0] == '64bit':
offset, length = 16, 40
else:
raise OSError('Unknown arquitecture {0}'.format(platform.architecture()[0]))
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
names = array.array(str('B'), b'\0' * space)
outbytes = struct.unpack(str('iL'), fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack(str('iL'), space, names.buffer_info()[0])
))[0]
namestr = names.tostring()
# return namestr, outbytes
return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)]
def _getIpAndMac(ifname):
ip, mac = _getIpAddr(ifname), _getMacAddr(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
@return: A "generator" of elements, that are dict-as-object, with this elements:
name: Name of the interface
mac: mac of the interface
ip: ip of the interface
'''
for ifname in _getInterfaces():
ip, mac = _getIpAndMac(ifname)
if mac != '00:00:00:00:00:00': # Skips local interfaces
yield utils.Bunch(name=ifname, mac=mac, ip=ip)
def getDomainName():
return ''
def getLinuxVersion():
lv = platform.linux_distribution()
return lv[0] + ', ' + lv[1]
def reboot(flags=0):
'''
Simple reboot using os command
'''
# Workaround for dummy thread
if six.PY3 is False:
import threading
threading._DummyThread._Thread__stop = lambda x: 42
# Check for OpenGnsys Client or GNU/Linux distribution.
if os.path.exists('/scripts/oginit'):
subprocess.call('source /opt/opengnsys/etc/preinit/loadenviron.sh; /opt/opengnsys/scripts/reboot', shell=True)
else:
subprocess.call(['/sbin/reboot'])
def poweroff(flags=0):
'''
Simple poweroff using os command
'''
# Workaround for dummy thread
if six.PY3 is False:
import threading
threading._DummyThread._Thread__stop = lambda x: 42
# Check for OpenGnsys Client or GNU/Linux distribution.
if os.path.exists('/scripts/oginit'):
subprocess.call('source /opt/opengnsys/etc/preinit/loadenviron.sh; /opt/opengnsys/scripts/poweroff', shell=True)
else:
subprocess.call(['/sbin/poweroff'])
def logoff():
'''
Kills all curent user processes, which must send a logogof
caveat: If the user has other sessions, will also disconnect from them
'''
# Workaround for dummy thread
if six.PY3 is False:
import threading
threading._DummyThread._Thread__stop = lambda x: 42
subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']])
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
'''
return os.environ['USER']

View File

@ -0,0 +1,61 @@
# -*- 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()

View File

@ -0,0 +1,68 @@
# -*- 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

View File

@ -0,0 +1,66 @@
# -*- 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

View File

@ -0,0 +1,74 @@
# -*- 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

View File

@ -0,0 +1,111 @@
# -*- 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
'''
# pylint: disable=unused-wildcard-import,wildcard-import
# 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 pkgutil
import os.path
from opengnsys.workers import ServerWorker
from opengnsys.workers import ClientWorker
from .log import logger
def loadModules(controller, client=False):
'''
Load own provided modules plus the modules that are in the configuration path.
The loading order is not defined (they are loaded as found, because modules MUST be "standalone" modules
@param service: The service that:
* Holds the configuration
* Will be used to initialize modules.
'''
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
def addCls(cls):
logger.debug('Found module class {}'.format(cls))
try:
if cls.name is None:
# Error, cls has no name
# Log the issue and
logger.error('Class {} has no name attribute'.format(cls))
return
ogModules.append(cls(controller))
except Exception as e:
logger.error('Error loading module {}'.format(e))
def recursiveAdd(p):
subcls = p.__subclasses__()
if len(subcls) == 0:
addCls(p)
else:
for c in subcls:
recursiveAdd(c)
def doLoad(paths):
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 controller.config.has_option('opengnsys', 'path') is True:
paths = tuple(os.path.abspath(v) for v in controller.config.get('opengnsys', 'path').split(','))
else:
paths = ()
# paths += (os.path.dirname(sys.modules[modPath].__file__),)
logger.debug('Loading modules from {}'.format(paths))
# Load modules
doLoad(paths)
# Add to list of available modules
recursiveAdd(modType)
return ogModules

View File

@ -0,0 +1,103 @@
# -*- 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 traceback
import sys
import six
if sys.platform == 'win32':
from opengnsys.windows.log import LocalLogger # @UnusedImport
else:
from opengnsys.linux.log import LocalLogger # @Reimport
# 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
_levelName = {
'OTHER': OTHER,
'DEBUG': DEBUG,
'INFO': INFO,
'WARN': WARN,
'ERROR': ERROR,
'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):
level = _levelName.get(level, INFO)
self.logLevel = level # Ensures level is an integer or fails
def log(self, level, message):
if level < self.logLevel: # Skip not wanted messages
return
self.logger.log(level, message)
def debug(self, message):
self.log(DEBUG, message)
def warn(self, message):
self.log(WARN, message)
def info(self, message):
self.log(INFO, message)
def error(self, message):
self.log(ERROR, message)
def fatal(self, message):
self.log(FATAL, message)
def exception(self):
try:
tb = traceback.format_exc()
except Exception:
tb = '(could not get traceback!)'
self.log(DEBUG, tb)
def flush(self):
pass
logger = Logger()

View File

View File

@ -0,0 +1,55 @@
# -*- 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
from opengnsys import operations
from opengnsys.log import logger
class OpenGnSysWorker(ClientWorker):
name = 'opengnsys'
def onActivation(self):
logger.debug('Activate invoked')
def onDeactivation(self):
logger.debug('Deactivate invoked')
# Processes message "doit" (sample)
def process_doit(self, jsonParams):
logger.debug('Processed message doit with params {}'.format(jsonParams))
self.sendServerMessage('doit', {'data':1})
def process_logoff(self, jsonParams):
logger.debug('Processed logoff message with params {}'.format(jsonParams))
operations.logoff()

View File

@ -0,0 +1,198 @@
# -*- 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 opengnsys import REST, RESTError
from opengnsys import operations
from opengnsys.log import logger
from opengnsys.scriptThread import ScriptExecutorThread
import subprocess
import threading
import thread
import os
import platform
import time
# Error handler decorator.
def catchBackgroundError(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
class OpenGnSysWorker(ServerWorker):
name = 'opengnsys'
interface = None # Binded interface for OpenGnsys
loggedin = False #
locked = {}
def onActivation(self):
self.cmd = None
# Ensure cfg has required configuration variables or an exception will be thrown
self.REST = REST(self.service.config.get('opengnsys', 'remote'))
# Get network interfaces
self.interface = list(operations.getNetworkInfo())[0] # Get first network interface
# Send an initialize message
#self.REST.sendMessage('initialize/{}/{}'.format(self.interface.mac, self.interface.ip))
# Send an POST message
self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip})
def onDeactivation(self):
#self.REST.sendMessage('deinitialize/{}/{}'.format(self.interface.mac, self.interface.ip))
logger.debug('onDeactivation')
self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip})
# Processes message "doit" (sample)
#def process_doit(self, path, getParams, postParams):
# # Send a sample message to client
# logger.debug('Processing doit')
# self.sendClientMessage('doit', {'param1': 'test', 'param2': 'test2'})
# return 'Processed message for {}, {}, {}'.format(path, getParams, postParams)
def process_script(self, path, getParams, postParams):
'''
Processes an script execution (script is encoded in base64)
'''
logger.debug('Processing script request')
script = postParams.get('script')
if postParams.get('client', 'false') == 'false':
thr = ScriptExecutorThread(script=script.decode('base64'))
thr.start()
else:
self.sendScriptMessage(script)
return 'ok'
def processClientMessage(self, message, data):
logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data))
def process_client_doit(self, params):
self.REST.sendMessage('doit_done', params)
def onLogin(self, user):
logger.debug('Received login for {}'.format(user))
self.loggedin = True
self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, "user": user})
def onLogout(self, user):
logger.debug('Received logout for {}'.format(user))
self.loggedin = False
self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, "user": user})
def process_ogclient(self, path, getParams, postParams):
'''
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:
Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z
The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this:
module.processMessage(["mazinger","Z"], getParams, postParams)
This method will process "mazinger", and look for a "self" method that is called "process_mazinger", and invoke it this way:
return self.process_mazinger(["Z"], getParams, postParams)
In the case path is empty (that is, the path is composed only by the module name, like in "http://example.com/Sample", the "process" method
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 len(path) == 0:
return "ok"
try:
operation = getattr(self, 'ogclient_' + path[0])
except Exception:
raise Exception('Message processor for "{}" not found'.format(path[0]))
return operation(path[1:], getParams, postParams)
###### EN PRUEBAS ######
def process_status(self, path, getParams, postParams):
'''
Returns client status.
'''
res = { 'status': '', 'loggedin': self.loggedin }
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'
return res
def process_reboot(self, path, getParams, postParams):
'''
Launches a system reboot operation.
'''
logger.debug('Received reboot operation')
def rebt():
operations.reboot()
threading.Thread(target=rebt).start()
return {'op': 'launched'}
def process_poweroff(self, path, getParams, postParams):
'''
Launches a system power off operation.
'''
logger.debug('Received poweroff operation')
def pwoff():
time.sleep(2)
operations.poweroff()
threading.Thread(target=pwoff).start()
return {'op': 'launched'}
def process_logoff(self, path, getParams, postParams):
'''
Closes user session.
'''
logger.debug('Received logoff operation')
self.sendClientMessage('logoff', {})
return 'Logoff operation was sended to client'

View File

@ -0,0 +1,40 @@
# -*- 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
'''
# pylint: disable=unused-wildcard-import,wildcard-import
from __future__ import unicode_literals
import sys
if sys.platform == 'win32':
from .windows.operations import * # @UnusedWildImport
else:
from .linux.operations import * # @UnusedWildImport

View File

@ -0,0 +1,51 @@
# -*- 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))

View File

@ -0,0 +1,243 @@
# -*- 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 .log import logger
from .config import readConfig
from .utils import exceptionToMessage
from . import ipc
from . import httpserver
from .loader import loadModules
import socket
import time
import json
import six
IPC_PORT = 10398
class CommonService(object):
isAlive = True
ipc = None
httpServer = None
modules = None
def __init__(self):
logger.info('----------------------------------------')
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
self.address = (cfg.get('address', '0.0.0.0'), int(cfg.get('port', '10997')))
self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
self.timeout = int(cfg.get('timeout', '20'))
logger.debug('Socket timeout: {}'.format(self.timeout))
socket.setdefaulttimeout(self.timeout)
# Now load modules
self.modules = loadModules(self)
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):
for v in self.modules:
try:
logger.debug('Notifying login of user {} to module {}'.format(username, v.name))
v.onLogin(username)
except Exception as e:
logger.error('Got exception {} processing login message on {}'.format(e, v.name))
def notifyLogout(self, username):
for v in self.modules:
try:
logger.debug('Notifying logout of user {} to module {}'.format(username, v.name))
v.onLogout(username)
except Exception as e:
logger.error('Got exception {} processing logout message on {}'.format(e, v.name))
def notifyMessage(self, data):
module, message, data = data.split('\0')
for v in self.modules:
if v.name == module: # Case Sensitive!!!!
try:
logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
v.processClientMessage(message, json.loads(data))
return
except Exception as e:
logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
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:
self.notifyLogin(data)
elif msg == ipc.REQ_LOGOUT:
self.notifyLogout(data)
elif msg == ipc.REQ_MESSAGE:
self.notifyMessage(data)
def initialize(self):
# ******************************************
# * Initialize listeners, modules, etc...
# ******************************************
if six.PY3 is False:
import threading
threading._DummyThread._Thread__stop = lambda x: 42
logger.debug('Starting IPC listener at {}'.format(IPC_PORT))
self.ipc = ipc.ServerIPC(self.ipcport, clientMessageProcessor=self.clientMessageProcessor)
self.ipc.start()
# And http threaded server
self.httpServer = httpserver.HTTPServerThread(self.address, self)
self.httpServer.start()
# And lastly invoke modules activation
validMods = []
for mod in self.modules:
try:
logger.debug('Activating module {}'.format(mod.name))
mod.activate()
validMods.append(mod)
except Exception as e:
logger.exception()
logger.error("Activation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
self.modules[:] = validMods # copy instead of assignment
logger.debug('Modules after activation: {}'.format(list(v.name for v in self.modules)))
def terminate(self):
# First invoke deactivate on modules
for mod in reversed(self.modules):
try:
logger.debug('Deactivating module {}'.format(mod.name))
mod.deactivate()
except Exception as e:
logger.exception()
logger.error("Deactivation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
# Remove IPC threads
if self.ipc is not None:
try:
self.ipc.stop()
except Exception:
logger.error('Couln\'t stop ipc server')
if self.httpServer is not None:
try:
self.httpServer.stop()
except Exception:
logger.error('Couln\'t stop RESTApi server')
self.notifyStop()
# ****************************************
# 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
@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()

View File

@ -0,0 +1,72 @@
# -*- 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 sys
import six
if sys.platform == 'win32':
_fromEncoding = 'windows-1250'
else:
_fromEncoding = 'utf-8'
def toUnicode(msg):
try:
if not isinstance(msg, six.text_type):
if isinstance(msg, six.binary_type):
return msg.decode(_fromEncoding, 'ignore')
return six.text_type(msg)
else:
return msg
except Exception:
try:
return six.text_type(msg)
except Exception:
return ''
def exceptionToMessage(e):
msg = ''
for arg in e.args:
if isinstance(arg, Exception):
msg = msg + exceptionToMessage(arg)
else:
msg = msg + toUnicode(arg) + '. '
return msg
class Bunch(dict):
def __init__(self, **kw):
dict.__init__(self, kw)
self.__dict__ = self

View File

@ -0,0 +1,124 @@
# -*- 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
# pylint: disable=unused-wildcard-import, wildcard-import
import win32serviceutil # @UnresolvedImport, pylint: disable=import-error
import win32service # @UnresolvedImport, pylint: disable=import-error
import win32security # @UnresolvedImport, pylint: disable=import-error
import win32net # @UnresolvedImport, pylint: disable=import-error
import win32event # @UnresolvedImport, pylint: disable=import-error
import win32com.client # @UnresolvedImport, @UnusedImport, pylint: disable=import-error
import pythoncom # @UnresolvedImport, pylint: disable=import-error
import servicemanager # @UnresolvedImport, pylint: disable=import-error
import os
from opengnsys import operations
from opengnsys.service import CommonService
from opengnsys.log import logger
class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService):
'''
This class represents a Windows Service for managing actor interactions
with UDS Broker and Machine
'''
_svc_name_ = "OGAgent"
_svc_display_name_ = "OpenGnSys Agent Service"
_svc_description_ = "OpenGnSys Agent for machines"
# 'System Event Notification' is the SENS service
_svc_deps_ = ['EventLog']
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
CommonService.__init__(self)
self.hWaitStop = win32event.CreateEvent(None, 1, 0, None)
self._user = None
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.isAlive = False
win32event.SetEvent(self.hWaitStop)
SvcShutdown = SvcStop
def notifyStop(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
def doWait(self, miliseconds):
win32event.WaitForSingleObject(self.hWaitStop, miliseconds)
def SvcDoRun(self):
'''
Main service loop
'''
try:
logger.debug('running SvcDoRun')
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
# call the CoInitialize to allow the registration to run in an other
# thread
logger.debug('Initializing com...')
pythoncom.CoInitialize()
# Initialize remaining service data
self.initialize()
except Exception: # Any init exception wil be caught, service must be then restarted
logger.exception()
logger.debug('Exiting service with failure status')
os._exit(-1) # pylint: disable=protected-access
# *********************
# * Main Service loop *
# *********************
try:
while self.isAlive:
# Pumps & processes any waiting messages
pythoncom.PumpWaitingMessages()
win32event.WaitForSingleObject(self.hWaitStop, 1000)
except Exception as e:
logger.error('Caught exception on main loop: {}'.format(e))
logger.debug('Exited main loop, deregistering SENS')
self.terminate() # Ends IPC servers
self.notifyStop()
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(OGAgentSvc)

View File

@ -0,0 +1,39 @@
# -*- 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 os
import sys
# Change to application directory.
os.chdir(os.path.dirname(sys.argv[0]))

View File

@ -0,0 +1,77 @@
# -*- 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 servicemanager # @UnresolvedImport, pylint: disable=import-error
import logging
import os
import tempfile
# Valid logging levels, from UDS Broker (uds.core.utils.log)
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in xrange(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
)
self.logger = logging.getLogger('opengnsys')
self.serviceLogger = False
def log(self, level, message):
# Debug messages are logged to a file
# our loglevels are 10000 (other), 20000 (debug), ....
# logging levels are 10 (debug), 20 (info)
# OTHER = logging.NOTSET
self.logger.log(level / 1000 - 10, message)
if level < INFO or self.serviceLogger is False: # Only information and above will be on event log
return
if level < WARN: # Info
servicemanager.LogInfoMsg(message)
elif level < ERROR: # WARN
servicemanager.LogWarningMsg(message)
else: # Error & Fatal
servicemanager.LogErrorMsg(message)
def isWindows(self):
return True
def isLinux(self):
return False

View File

@ -0,0 +1,231 @@
# -*- 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 win32com.client # @UnresolvedImport, pylint: disable=import-error
import win32net # @UnresolvedImport, pylint: disable=import-error
import win32security # @UnresolvedImport, pylint: disable=import-error
import win32api # @UnresolvedImport, pylint: disable=import-error
import win32con # @UnresolvedImport, pylint: disable=import-error
import ctypes
from ctypes.wintypes import DWORD, LPCWSTR
import os
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
@return: A "generator" of elements, that are dict-as-object, with this elements:
name: Name of the interface
mac: mac of the interface
ip: ip of the interface
'''
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
wmobj = obj.ConnectServer("localhost", "root\cimv2")
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
try:
for obj in adapters:
for ip in obj.IPAddress:
if ':' in ip: # Is IPV6, skip this
continue
if ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.'): # If single link ip, or no ip
continue
# logger.debug('Net config found: {}=({}, {})'.format(obj.Caption, obj.MACAddress, ip))
yield utils.Bunch(name=obj.Caption, mac=obj.MACAddress, ip=ip)
except Exception:
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():
return win32api.GetVersionEx()
EWX_LOGOFF = 0x00000000
EWX_SHUTDOWN = 0x00000001
EWX_REBOOT = 0x00000002
EWX_FORCE = 0x00000004
EWX_POWEROFF = 0x00000008
EWX_FORCEIFHUNG = 0x00000010
def reboot(flags=EWX_FORCEIFHUNG | EWX_REBOOT):
hproc = win32api.GetCurrentProcess()
htok = win32security.OpenProcessToken(hproc, win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
privs = ((win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), win32security.SE_PRIVILEGE_ENABLED),)
win32security.AdjustTokenPrivileges(htok, 0, privs)
win32api.ExitWindowsEx(flags, 0)
def poweroff(flags=0):
'''
Simple poweroff command.
'''
reboot(flags=EWX_FORCEIFHUNG | EWX_POWEROFF)
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 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
'''
return os.environ['USERNAME']

View File

@ -0,0 +1,2 @@
from .server_worker import ServerWorker
from .client_worker import ClientWorker

View File

@ -0,0 +1,114 @@
# -*- 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
'''
# 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
* You must provide a module name (override name on your class), so we can identify the module by a "valid" name.
A valid name is like a valid python variable (do not use spaces, etc...)
* The name of the module is used as REST message destination id:
https://sampleserver:8888/[name]/....
Remember that module names and REST path are case sensitive!!!
'''
name = None
service = None
def __init__(self, service):
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):
'''
This method can be overriden to provide your own message proccessor, or better you can
implement a method that is called "process_" + message and this default processMessage will invoke it
* Example:
We got a message from OGAgent "Mazinger", with json params
module.processMessage("mazinger", jsonParams)
This method will process "mazinguer", and look for a "self" method that is called "process_mazinger", and invoke it this way:
return self.process_mazinger(jsonParams)
The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are)
'''
try:
operation = getattr(self, 'process_' + message)
except Exception:
raise Exception('Message processor for "{}" not found'.format(message))
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)

View File

@ -0,0 +1,183 @@
# -*- 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
'''
# 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
* You must provide a module name (override name on your class), so we can identify the module by a "valid" name.
A valid name is like a valid python variable (do not use spaces, etc...)
* The name of the module is used as REST message destination id:
https://sampleserver:8888/[name]/....
Remember that module names and REST path are case sensitive!!!
'''
name = None
service = None
locked = False
def __init__(self, service):
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):
'''
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):
'''
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:
Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z
The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this:
module.processMessage(["mazinger","Z"], getParams, postParams)
This method will process "mazinguer", and look for a "self" method that is called "process_mazinger", and invoke it this way:
return self.process_mazinger(["Z"], getParams, postParams)
In the case path is empty (that is, the path is composed only by the module name, like in "http://example.com/Sample", the "process" method
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')
if len(path) == 0:
return self.process(getParams, postParams)
try:
operation = getattr(self, 'process_' + path[0])
except Exception:
raise Exception('Message processor for "{}" not found'.format(path[0]))
return operation(path[1:], getParams, postParams)
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
implement a method that is called exactly "process_client_" + message (module name has been removed from path) and this default processMessage will invoke it
* Example:
We got a message from OGAgent "Mazinger", with json params
module.processClientMessage("mazinger", jsonParams)
This method will process "mazinguer", and look for a "self" method that is called "process_client_mazinger", and invoke it this way:
self.process_client_mazinger(jsonParams)
The methods returns nothing (client communications are done asynchronously)
'''
try:
operation = getattr(self, 'process_client_' + message)
except Exception:
raise Exception('Message processor for "{}" not found'.format(message))
operation(data)
# raise NotImplementedError('Got a client message but no proccessor is implemented')
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):
self.service.sendScriptMessage(script)
def sendLogoffMessage(self):
self.service.sendLogoffMessage()

View File

@ -0,0 +1,170 @@
'''
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()

132
src/setup.py 100644
View File

@ -0,0 +1,132 @@
# -*- 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
'''
VERSION = '1.0.0'
# 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, 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
from distutils.core import setup
import py2exe
import sys
import os
sys.argv.append('py2exe')
def get_requests_cert_file():
"""Add Python requests .pem file for installers."""
import requests
f = os.path.join(os.path.dirname(requests.__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 = 'http://www.opengnsys.es'
self.company_name = "VirtualCable S.L.U."
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'] + 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',
)

View File

View File

@ -0,0 +1,38 @@
#!/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'

View File

@ -0,0 +1,2 @@
# Module must be imported on package, so we can initialize and load it
from sample1 import Sample1

View File

@ -0,0 +1,40 @@
#!/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'

View File

@ -0,0 +1,36 @@
#!/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'

View File

@ -0,0 +1,210 @@
# -*- 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()

40
src/update.sh 100755
View File

@ -0,0 +1,40 @@
#!/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
}
pyrcc4 -py3 OGAgent.qrc -o OGAgent_rc.py
# process current directory ui's
process

View File

@ -0,0 +1,4 @@
#!/bin/bash
export WINEARCH=win32
export WINEPREFIX=$(realpath $(dirname $0)/wine)
wine cmd /c c:\\ogagent\\build.bat

View File

@ -0,0 +1,6 @@
C:
CD \ogagent\src
python setup.py
CD ..
"C:\Program Files\NSIS\makensis.exe" ogagent.nsi

184
windows/ogagent.nsi 100644
View File

@ -0,0 +1,184 @@
# We need http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin
# Copy inside the two x86_xxxxx folders inside nsis plugins folder
Name "OpenGnSys Agent"
# OpenGnsys Actor version
!define OGA_VERSION 1.0.0
# General Symbol Definitions
!define REGKEY "SOFTWARE\OGAgent"
!define VERSION ${OGA_VERSION}.0
!define COMPANY "Virtual Cable S.L.U."
!define URL http://www.udsenterprise.com
# MultiUser Symbol Definitions
!define MULTIUSER_EXECUTIONLEVEL Admin
#!define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
!define MULTIUSER_INSTALLMODE_COMMANDLINE
!define MULTIUSER_INSTALLMODE_INSTDIR OGAgent
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "${REGKEY}"
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUE "Path"
# MUI Symbol Definitions
#!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico"
!define MUI_ICON "src\img\oga.ico"
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_UNICON "src\img\oga.ico"
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
!define MUI_LANGDLL_REGISTRY_ROOT HKLM
!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY}
!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage
# Included files
!include MultiUser.nsh
!include Sections.nsh
!include MUI2.nsh
# Reserved Files
!insertmacro MUI_RESERVEFILE_LANGDLL
# Variables
Var StartMenuGroup
# Installer pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE src\license.txt
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
# Installer languages
!insertmacro MUI_LANGUAGE English
!insertmacro MUI_LANGUAGE Spanish
!insertmacro MUI_LANGUAGE French
!insertmacro MUI_LANGUAGE German
# Installer attributes
BrandingText "OpenGnSys"
OutFile OGAgentSetup-${OGA_VERSION}.exe
InstallDir OGAgent
CRCCheck on
XPStyle on
ShowInstDetails hide
VIProductVersion "${VERSION}.0.0"
VIAddVersionKey /LANG=${LANG_ENGLISH} ProductName "OGAgent"
VIAddVersionKey /LANG=${LANG_ENGLISH} ProductVersion "${VERSION}"
VIAddVersionKey /LANG=${LANG_ENGLISH} CompanyName "${COMPANY}"
VIAddVersionKey /LANG=${LANG_ENGLISH} CompanyWebsite "${URL}"
VIAddVersionKey /LANG=${LANG_ENGLISH} FileVersion "${VERSION}"
VIAddVersionKey /LANG=${LANG_ENGLISH} FileDescription "OpenGnSys Agent installer"
VIAddVersionKey /LANG=${LANG_ENGLISH} LegalCopyright "(c) 2015 Virtual Cable S.L.U."
InstallDirRegKey HKLM "${REGKEY}" Path
ShowUninstDetails show
# Installer sections
Section -Main SEC0000
SetShellVarContext all
SetOutPath $INSTDIR
SetOverwrite on
File /r bin\*.*
File vcredist_x86.exe
WriteRegStr HKLM "${REGKEY}\Components" Main 1
SectionEnd
Section -post SEC0001
SetShellVarContext current
WriteRegStr HKLM "${REGKEY}" Path $INSTDIR
SetOutPath $INSTDIR
WriteUninstaller $INSTDIR\OGAgentUninstaller.exe
SetOutPath $SMPROGRAMS\$StartMenuGroup
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^UninstallLink).lnk" $INSTDIR\OGAgentUninstaller.exe
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\OGAgentUninstaller.exe
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\OGAgentUninstaller.exe
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" OGAgentTool $INSTDIR\OGAgentUser.exe
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
ExecWait '"$INSTDIR\vcredist_x86.exe" /passive /norestart'
# Add the application to the firewall exception list - All Networks - All IP Version - Enabled
# SimpleFC::AddApplication "OpenGnSys Agent Service" "$INSTDIR\OGAgentService.exe" 0 2 "" 1
# SimpleFC::AdvAddRule [name] [description] [protocol] [direction]
# [status] [profile] [action] [application] [service_name] [icmp_types_and_codes]
# [group] [local_ports] [remote_ports] [local_address] [remote_address]
#
SimpleFC::AdvAddRule "OpenGnSys Agent Firewall rules" "Firewall rules for OpenGnSys Agent interaction with broker." "6" "1" \
"1" "7" "1" "$INSTDIR\OGAgentService.exe" "" "" \
"" "" "" "" ""
Pop $0 ; return error(1)/success(0)
# Install service
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe --startup auto install" # Add service after installation
# Update recovery options
nsExec::Exec /OEM "$INSTDIR\OGAServiceHelper.exe"
SectionEnd
# Macro for selecting uninstaller sections
!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
Push $R0
ReadRegStr $R0 HKLM "${REGKEY}\Components" "${SECTION_NAME}"
StrCmp $R0 1 0 next${UNSECTION_ID}
!insertmacro SelectSection "${UNSECTION_ID}"
GoTo done${UNSECTION_ID}
next${UNSECTION_ID}:
!insertmacro UnselectSection "${UNSECTION_ID}"
done${UNSECTION_ID}:
Pop $R0
!macroend
# Uninstaller sections
Section /o -un.Main UNSEC0000
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe stop" # Stops the service prior uninstall
nsExec::Exec /OEM "$INSTDIR\OGAgentService.exe remove" # Removes the service prior uninstall
Delete /REBOOTOK "$INSTDIR\*.*"
DeleteRegValue HKLM "${REGKEY}\Components" Main
DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" OGAgentTool
SectionEnd
Section -un.post UNSEC0001
# Remove application from the firewall exception list
# SimpleFC::RemoveApplication "$INSTDIR\OGAgentService.exe"
SimpleFC::AdvRemoveRule "OpenGnSys Agent Firewall rules"
Pop $0 ; return error(1)/success(0)
SetShellVarContext current
StrCpy $StartMenuGroup "OpenGnSys Agent"
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^UninstallLink).lnk"
Delete /REBOOTOK $INSTDIR\OGAgentUninstaller.exe
DeleteRegValue HKLM "${REGKEY}" Path
DeleteRegKey /IfEmpty HKLM "${REGKEY}\Components"
DeleteRegKey /IfEmpty HKLM "${REGKEY}"
RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup
SetShellVarContext all
RmDir /REBOOTOK $INSTDIR
SetRebootFlag true
MessageBox MB_OK|MB_USERICON "Your system needs to reboot to complete uninstallation."
Reboot # Reboot is needed after uninstalling, so new installs works fine
SectionEnd
# Installer functions
Function .onInit
InitPluginsDir
StrCpy $StartMenuGroup "OpenGnSys Agent"
!insertmacro MUI_LANGDLL_DISPLAY
!insertmacro MULTIUSER_INIT
FunctionEnd
# Uninstaller functions
Function un.onInit
StrCpy $StartMenuGroup "OpenGnSys Agent"
!insertmacro MUI_UNGETLANGUAGE
!insertmacro MULTIUSER_UNINIT
!insertmacro SELECT_UNSECTION Main ${UNSEC0000}
FunctionEnd
# Installer Language Strings
LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall $(^Name)"
LangString ^UninstallLink ${LANG_SPANISH} "Desinstalar $(^Name)"
LangString ^UninstallLink ${LANG_FRENCH} "D<>sinstaller $(^Name)"
LangString ^UninstallLink ${LANG_GERMAN} "deinstallieren $(^Name)"

View File

@ -0,0 +1,72 @@
#!/bin/sh
# We need:
# * Wine (32 bit)
# * winetricks
export WINEARCH=win32
WINE=wine
download() {
mkdir downloads
# Get needed software
cd downloads
wget -nd https://www.python.org/ftp/python/2.7.10/python-2.7.10.msi
wget -nd http://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 http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/pywin32-219.win32-py2.7.exe/download -O pywin32-install.exe
wget -nd http://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/py2exe-0.6.9.win32-py2.7.exe/download -O py2exe-install.exe
wget -nd http://prdownloads.sourceforge.net/nsis/nsis-3.0b1-setup.exe?download -O nsis-install.exe
wget -nd http://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 http://nsis.sourceforge.net/mediawiki/images/d/d7/NSIS_Simple_Firewall_Plugin_1.20.zip
cd ..
}
install_python() {
WINEPREFIX=`pwd`/wine
export WINEPREFIX
echo "Setting up wine prefix (using winetricks)"
winetricks
cd downloads
echo "Installing python"
$WINE msiexec /qn /i python-2.7.10.msi
echo "Installing vc for python"
$WINE msiexec /qn /i VCForPython27.msi
echo "Installing pywin32 (needs X)"
$WINE pywin32-install.exe
echo "Installing py2exe (needs X)"
$WINE py2exe-install.exe
echo "Installing pyqt"
$WINE pyqt-install.exe
echo "Installing nsis (needs X?)"
$WINE nsis-install.exe
cd ..
}
setup_pip() {
echo "Seting up pip..."
#mkdir $WINEPREFIX/drive_c/temp
#cp downloads/get-pip.py $WINEPREFIX/drive_c/temp
#cd $WINEPREFIX/drive_c/temp
#$WINE c:\\Python27\\python.exe get-pip.py
wine c:\\Python27\\python -m pip install --upgrade pip
}
install_packages() {
echo "Installing required packages"
wine c:\\Python27\\python -m pip install requests pycrypto six
# 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