summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--admin/Sources/Clients/ogagent/linux/Makefile72
-rwxr-xr-xadmin/Sources/Clients/ogagent/linux/build-packages.sh34
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/changelog5
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/compat1
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/control15
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/copyright26
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/docs1
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.init23
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.links2
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst21
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst.debhelper5
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm10
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm.debhelper12
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/ogagent.substvars2
-rwxr-xr-xadmin/Sources/Clients/ogagent/linux/debian/rules44
-rw-r--r--admin/Sources/Clients/ogagent/linux/debian/source/format1
-rw-r--r--admin/Sources/Clients/ogagent/linux/desktop/OGAgentTool.desktop12
-rw-r--r--admin/Sources/Clients/ogagent/linux/ogagent-template.spec65
-rw-r--r--admin/Sources/Clients/ogagent/linux/policy/org.openuds.pkexec.UDSActorConfig.policy20
-rw-r--r--admin/Sources/Clients/ogagent/linux/readme.txt3
-rw-r--r--admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool6
-rw-r--r--admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool-startup10
-rw-r--r--admin/Sources/Clients/ogagent/linux/scripts/ogagent6
-rw-r--r--admin/Sources/Clients/ogagent/notas.txt11
-rw-r--r--admin/Sources/Clients/ogagent/requires.txt3
-rw-r--r--admin/Sources/Clients/ogagent/src/OGAServiceHelper.py57
-rw-r--r--admin/Sources/Clients/ogagent/src/OGAgent.manifest17
-rw-r--r--admin/Sources/Clients/ogagent/src/OGAgent.qrc5
-rw-r--r--admin/Sources/Clients/ogagent/src/OGAgentUser.py338
-rw-r--r--admin/Sources/Clients/ogagent/src/OGAgent_rc.py289
-rw-r--r--admin/Sources/Clients/ogagent/src/about-dialog.ui240
-rw-r--r--admin/Sources/Clients/ogagent/src/about_dialog_ui.py163
-rw-r--r--admin/Sources/Clients/ogagent/src/cfg/ogagent.cfg21
-rw-r--r--admin/Sources/Clients/ogagent/src/cfg/ogclient.cfg11
-rw-r--r--admin/Sources/Clients/ogagent/src/img/oga-48x48.icobin0 -> 9662 bytes
-rw-r--r--admin/Sources/Clients/ogagent/src/img/oga-512.pngbin0 -> 44217 bytes
-rw-r--r--admin/Sources/Clients/ogagent/src/img/oga.icobin0 -> 9662 bytes
-rw-r--r--admin/Sources/Clients/ogagent/src/img/oga.pngbin0 -> 3906 bytes
-rw-r--r--admin/Sources/Clients/ogagent/src/license.txt27
-rw-r--r--admin/Sources/Clients/ogagent/src/message-dialog.ui89
-rw-r--r--admin/Sources/Clients/ogagent/src/message_dialog_ui.py69
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/RESTApi.py160
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/__init__.py57
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/certs.py101
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/config.py59
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/httpserver.py150
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/ipc.py416
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/OGAgentService.py147
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/__init__.py32
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/daemon.py182
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/log.py80
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py271
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/__init__.py61
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/debian.py68
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/opensuse.py66
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/redhat.py74
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/loader.py111
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/log.py103
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/modules/__init__.py0
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/modules/client/OpenGnSys/__init__.py55
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/modules/client/__init__.py0
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py198
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/modules/server/__init__.py0
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/operations.py40
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/scriptThread.py51
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/service.py243
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/utils.py72
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/windows/OGAgentService.py124
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/windows/__init__.py39
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/windows/log.py77
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/windows/operations.py231
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/workers/__init__.py2
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/workers/client_worker.py114
-rw-r--r--admin/Sources/Clients/ogagent/src/opengnsys/workers/server_worker.py183
-rw-r--r--admin/Sources/Clients/ogagent/src/prototypes/threaded_server.py170
-rw-r--r--admin/Sources/Clients/ogagent/src/setup.py132
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/__init__.py0
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/client/Sample1/__init__.py38
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/client/__init__.py0
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/__init__.py2
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample1.py40
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample_pkg/__init__.py36
-rw-r--r--admin/Sources/Clients/ogagent/src/test_modules/server/__init__.py0
-rw-r--r--admin/Sources/Clients/ogagent/src/test_rest_server.py210
-rwxr-xr-xadmin/Sources/Clients/ogagent/src/update.sh40
-rwxr-xr-xadmin/Sources/Clients/ogagent/windows/build-windows.sh4
-rw-r--r--admin/Sources/Clients/ogagent/windows/build.bat6
-rw-r--r--admin/Sources/Clients/ogagent/windows/ogagent.nsi184
-rwxr-xr-xadmin/Sources/Clients/ogagent/windows/py2exe-wine-linux.sh72
-rw-r--r--installer/README.es.txt11
-rwxr-xr-xinstaller/ogagent-devel-installer.sh96
-rw-r--r--installer/vagrant/Vagrantfile-ogagent-vbox2
92 files changed, 6343 insertions, 3 deletions
diff --git a/admin/Sources/Clients/ogagent/linux/Makefile b/admin/Sources/Clients/ogagent/linux/Makefile
new file mode 100644
index 00000000..6ada91f5
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/Makefile
@@ -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)
diff --git a/admin/Sources/Clients/ogagent/linux/build-packages.sh b/admin/Sources/Clients/ogagent/linux/build-packages.sh
new file mode 100755
index 00000000..a42fd1c3
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/build-packages.sh
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/debian/changelog b/admin/Sources/Clients/ogagent/linux/debian/changelog
new file mode 100644
index 00000000..e09ad988
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/changelog
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/debian/compat b/admin/Sources/Clients/ogagent/linux/debian/compat
new file mode 100644
index 00000000..f11c82a4
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/compat
@@ -0,0 +1 @@
+9 \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/linux/debian/control b/admin/Sources/Clients/ogagent/linux/debian/control
new file mode 100644
index 00000000..275b89f8
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/control
@@ -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.
diff --git a/admin/Sources/Clients/ogagent/linux/debian/copyright b/admin/Sources/Clients/ogagent/linux/debian/copyright
new file mode 100644
index 00000000..cef2d43f
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/copyright
@@ -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'. \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/linux/debian/docs b/admin/Sources/Clients/ogagent/linux/debian/docs
new file mode 100644
index 00000000..b2b2a781
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/docs
@@ -0,0 +1 @@
+readme.txt
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.init b/admin/Sources/Clients/ogagent/linux/debian/ogagent.init
new file mode 100644
index 00000000..f78341fe
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.init
@@ -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
+
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.links b/admin/Sources/Clients/ogagent/linux/debian/ogagent.links
new file mode 100644
index 00000000..9b970d7b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.links
@@ -0,0 +1,2 @@
+/usr/share/OGAgent/cfg/ogagent.cfg /etc/ogagent/ogagent.cfg
+/usr/share/OGAgent/cfg/ogclient.cfg /etc/ogagent/ogclient.cfg
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst
new file mode 100644
index 00000000..b59cfa6f
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst.debhelper b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst.debhelper
new file mode 100644
index 00000000..e75924dc
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postinst.debhelper
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm
new file mode 100644
index 00000000..a46fa487
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm
@@ -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
+
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm.debhelper b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm.debhelper
new file mode 100644
index 00000000..3167f1f8
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.postrm.debhelper
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/debian/ogagent.substvars b/admin/Sources/Clients/ogagent/linux/debian/ogagent.substvars
new file mode 100644
index 00000000..978fc8b5
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/ogagent.substvars
@@ -0,0 +1,2 @@
+misc:Depends=
+misc:Pre-Depends=
diff --git a/admin/Sources/Clients/ogagent/linux/debian/rules b/admin/Sources/Clients/ogagent/linux/debian/rules
new file mode 100755
index 00000000..fbe82e67
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/rules
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/debian/source/format b/admin/Sources/Clients/ogagent/linux/debian/source/format
new file mode 100644
index 00000000..9f674278
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/debian/source/format
@@ -0,0 +1 @@
+3.0 (native) \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/linux/desktop/OGAgentTool.desktop b/admin/Sources/Clients/ogagent/linux/desktop/OGAgentTool.desktop
new file mode 100644
index 00000000..28ff0946
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/desktop/OGAgentTool.desktop
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/ogagent-template.spec b/admin/Sources/Clients/ogagent/linux/ogagent-template.spec
new file mode 100644
index 00000000..072955ef
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/ogagent-template.spec
@@ -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
diff --git a/admin/Sources/Clients/ogagent/linux/policy/org.openuds.pkexec.UDSActorConfig.policy b/admin/Sources/Clients/ogagent/linux/policy/org.openuds.pkexec.UDSActorConfig.policy
new file mode 100644
index 00000000..9afd775a
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/policy/org.openuds.pkexec.UDSActorConfig.policy
@@ -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> \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/linux/readme.txt b/admin/Sources/Clients/ogagent/linux/readme.txt
new file mode 100644
index 00000000..85a6443e
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/readme.txt
@@ -0,0 +1,3 @@
+OGAgent is the agent intended for OpengGnSys interaction.
+
+Please, visit http://www.opengnsys.es for more information
diff --git a/admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool b/admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool
new file mode 100644
index 00000000..5b300523
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+FOLDER=/usr/share/OGAgent
+
+cd $FOLDER
+python OGAgentUser.py $@
diff --git a/admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool-startup b/admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool-startup
new file mode 100644
index 00000000..bb3a848e
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/scripts/OGAgentTool-startup
@@ -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 \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/linux/scripts/ogagent b/admin/Sources/Clients/ogagent/linux/scripts/ogagent
new file mode 100644
index 00000000..1bcc29b0
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/linux/scripts/ogagent
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+FOLDER=/usr/share/OGAgent
+
+cd $FOLDER
+python -m opengnsys.linux.OGAgentService $@
diff --git a/admin/Sources/Clients/ogagent/notas.txt b/admin/Sources/Clients/ogagent/notas.txt
new file mode 100644
index 00000000..22118d6e
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/notas.txt
@@ -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?
diff --git a/admin/Sources/Clients/ogagent/requires.txt b/admin/Sources/Clients/ogagent/requires.txt
new file mode 100644
index 00000000..07ce387c
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/requires.txt
@@ -0,0 +1,3 @@
+six
+requests
+
diff --git a/admin/Sources/Clients/ogagent/src/OGAServiceHelper.py b/admin/Sources/Clients/ogagent/src/OGAServiceHelper.py
new file mode 100644
index 00000000..79a6c81d
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/OGAServiceHelper.py
@@ -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)
diff --git a/admin/Sources/Clients/ogagent/src/OGAgent.manifest b/admin/Sources/Clients/ogagent/src/OGAgent.manifest
new file mode 100644
index 00000000..0e5ff974
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/OGAgent.manifest
@@ -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>
diff --git a/admin/Sources/Clients/ogagent/src/OGAgent.qrc b/admin/Sources/Clients/ogagent/src/OGAgent.qrc
new file mode 100644
index 00000000..59177668
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/OGAgent.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="images">
+ <file>img/oga.png</file>
+ </qresource>
+</RCC>
diff --git a/admin/Sources/Clients/ogagent/src/OGAgentUser.py b/admin/Sources/Clients/ogagent/src/OGAgentUser.py
new file mode 100644
index 00000000..501133c3
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/OGAgentUser.py
@@ -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)
diff --git a/admin/Sources/Clients/ogagent/src/OGAgent_rc.py b/admin/Sources/Clients/ogagent/src/OGAgent_rc.py
new file mode 100644
index 00000000..867ca2ab
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/OGAgent_rc.py
@@ -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()
diff --git a/admin/Sources/Clients/ogagent/src/about-dialog.ui b/admin/Sources/Clients/ogagent/src/about-dialog.ui
new file mode 100644
index 00000000..5a6a8d3c
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/about-dialog.ui
@@ -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>
diff --git a/admin/Sources/Clients/ogagent/src/about_dialog_ui.py b/admin/Sources/Clients/ogagent/src/about_dialog_ui.py
new file mode 100644
index 00000000..054cc061
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/about_dialog_ui.py
@@ -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_())
+
diff --git a/admin/Sources/Clients/ogagent/src/cfg/ogagent.cfg b/admin/Sources/Clients/ogagent/src/cfg/ogagent.cfg
new file mode 100644
index 00000000..11b1483e
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/cfg/ogagent.cfg
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/cfg/ogclient.cfg b/admin/Sources/Clients/ogagent/src/cfg/ogclient.cfg
new file mode 100644
index 00000000..e1a9b07b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/cfg/ogclient.cfg
@@ -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 \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/img/oga-48x48.ico b/admin/Sources/Clients/ogagent/src/img/oga-48x48.ico
new file mode 100644
index 00000000..37ded487
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/img/oga-48x48.ico
Binary files differ
diff --git a/admin/Sources/Clients/ogagent/src/img/oga-512.png b/admin/Sources/Clients/ogagent/src/img/oga-512.png
new file mode 100644
index 00000000..37a0dbe4
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/img/oga-512.png
Binary files differ
diff --git a/admin/Sources/Clients/ogagent/src/img/oga.ico b/admin/Sources/Clients/ogagent/src/img/oga.ico
new file mode 100644
index 00000000..37ded487
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/img/oga.ico
Binary files differ
diff --git a/admin/Sources/Clients/ogagent/src/img/oga.png b/admin/Sources/Clients/ogagent/src/img/oga.png
new file mode 100644
index 00000000..70db4564
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/img/oga.png
Binary files differ
diff --git a/admin/Sources/Clients/ogagent/src/license.txt b/admin/Sources/Clients/ogagent/src/license.txt
new file mode 100644
index 00000000..bdb7d25d
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/license.txt
@@ -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.
diff --git a/admin/Sources/Clients/ogagent/src/message-dialog.ui b/admin/Sources/Clients/ogagent/src/message-dialog.ui
new file mode 100644
index 00000000..75137037
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/message-dialog.ui
@@ -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>
diff --git a/admin/Sources/Clients/ogagent/src/message_dialog_ui.py b/admin/Sources/Clients/ogagent/src/message_dialog_ui.py
new file mode 100644
index 00000000..ebd4d34a
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/message_dialog_ui.py
@@ -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_())
+
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/RESTApi.py b/admin/Sources/Clients/ogagent/src/opengnsys/RESTApi.py
new file mode 100644
index 00000000..5caaf8c4
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/RESTApi.py
@@ -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)
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/__init__.py
new file mode 100644
index 00000000..43c033dd
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/__init__.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/certs.py b/admin/Sources/Clients/ogagent/src/opengnsys/certs.py
new file mode 100644
index 00000000..e4c070e8
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/certs.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/config.py b/admin/Sources/Clients/ogagent/src/opengnsys/config.py
new file mode 100644
index 00000000..c86c6979
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/config.py
@@ -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
+ \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/httpserver.py b/admin/Sources/Clients/ogagent/src/opengnsys/httpserver.py
new file mode 100644
index 00000000..4cd95c81
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/httpserver.py
@@ -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()
+
+ \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/ipc.py b/admin/Sources/Clients/ogagent/src/opengnsys/ipc.py
new file mode 100644
index 00000000..de9faf2a
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/ipc.py
@@ -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
+
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/OGAgentService.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/OGAgentService.py
new file mode 100644
index 00000000..29a238da
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/OGAgentService.py
@@ -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()
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/__init__.py
new file mode 100644
index 00000000..3a98c780
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/__init__.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/daemon.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/daemon.py
new file mode 100644
index 00000000..26b2b9e8
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/daemon.py
@@ -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().
+ """
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/log.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/log.py
new file mode 100644
index 00000000..dc54e198
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/log.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py
new file mode 100644
index 00000000..b00c2596
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py
@@ -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']
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/__init__.py
new file mode 100644
index 00000000..27198dcf
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/__init__.py
@@ -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() \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/debian.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/debian.py
new file mode 100644
index 00000000..be55e003
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/debian.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/opensuse.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/opensuse.py
new file mode 100644
index 00000000..a2d29a5c
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/opensuse.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/redhat.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/redhat.py
new file mode 100644
index 00000000..8821a81c
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/renamer/redhat.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/loader.py b/admin/Sources/Clients/ogagent/src/opengnsys/loader.py
new file mode 100644
index 00000000..23988fca
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/loader.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/log.py b/admin/Sources/Clients/ogagent/src/opengnsys/log.py
new file mode 100644
index 00000000..e34c0874
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/log.py
@@ -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()
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/modules/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/__init__.py
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/modules/client/OpenGnSys/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/modules/client/OpenGnSys/__init__.py
new file mode 100644
index 00000000..6e287be6
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/client/OpenGnSys/__init__.py
@@ -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()
+
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/modules/client/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/modules/client/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/client/__init__.py
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py
new file mode 100644
index 00000000..f6f32e34
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py
@@ -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'
+
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/__init__.py
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py
new file mode 100644
index 00000000..fc241ab4
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/scriptThread.py b/admin/Sources/Clients/ogagent/src/opengnsys/scriptThread.py
new file mode 100644
index 00000000..2a6779b4
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/scriptThread.py
@@ -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))
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/service.py b/admin/Sources/Clients/ogagent/src/opengnsys/service.py
new file mode 100644
index 00000000..b937f210
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/service.py
@@ -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()
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/utils.py b/admin/Sources/Clients/ogagent/src/opengnsys/utils.py
new file mode 100644
index 00000000..9480a6ab
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/utils.py
@@ -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
+
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/windows/OGAgentService.py b/admin/Sources/Clients/ogagent/src/opengnsys/windows/OGAgentService.py
new file mode 100644
index 00000000..28736076
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/windows/OGAgentService.py
@@ -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)
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/windows/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/windows/__init__.py
new file mode 100644
index 00000000..e662942e
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/windows/__init__.py
@@ -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]))
+
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/windows/log.py b/admin/Sources/Clients/ogagent/src/opengnsys/windows/log.py
new file mode 100644
index 00000000..79ca877b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/windows/log.py
@@ -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
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/windows/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/windows/operations.py
new file mode 100644
index 00000000..7cfea995
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/windows/operations.py
@@ -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']
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/workers/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/workers/__init__.py
new file mode 100644
index 00000000..f2bcd7d2
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/workers/__init__.py
@@ -0,0 +1,2 @@
+from .server_worker import ServerWorker
+from .client_worker import ClientWorker
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/workers/client_worker.py b/admin/Sources/Clients/ogagent/src/opengnsys/workers/client_worker.py
new file mode 100644
index 00000000..6a083804
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/workers/client_worker.py
@@ -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)
+ \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/workers/server_worker.py b/admin/Sources/Clients/ogagent/src/opengnsys/workers/server_worker.py
new file mode 100644
index 00000000..f15144f1
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/opengnsys/workers/server_worker.py
@@ -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()
diff --git a/admin/Sources/Clients/ogagent/src/prototypes/threaded_server.py b/admin/Sources/Clients/ogagent/src/prototypes/threaded_server.py
new file mode 100644
index 00000000..ed5583bf
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/prototypes/threaded_server.py
@@ -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()
+
+ \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/setup.py b/admin/Sources/Clients/ogagent/src/setup.py
new file mode 100644
index 00000000..7d6e80c5
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/setup.py
@@ -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',
+)
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/__init__.py b/admin/Sources/Clients/ogagent/src/test_modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/__init__.py
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/client/Sample1/__init__.py b/admin/Sources/Clients/ogagent/src/test_modules/client/Sample1/__init__.py
new file mode 100644
index 00000000..db174f0e
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/client/Sample1/__init__.py
@@ -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'
+
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/client/__init__.py b/admin/Sources/Clients/ogagent/src/test_modules/client/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/client/__init__.py
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/__init__.py b/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/__init__.py
new file mode 100644
index 00000000..189957e6
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/__init__.py
@@ -0,0 +1,2 @@
+# Module must be imported on package, so we can initialize and load it
+from sample1 import Sample1 \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample1.py b/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample1.py
new file mode 100644
index 00000000..61e405ec
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample1.py
@@ -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'
+
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample_pkg/__init__.py b/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample_pkg/__init__.py
new file mode 100644
index 00000000..19365721
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/server/Sample1/sample_pkg/__init__.py
@@ -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' \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/test_modules/server/__init__.py b/admin/Sources/Clients/ogagent/src/test_modules/server/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_modules/server/__init__.py
diff --git a/admin/Sources/Clients/ogagent/src/test_rest_server.py b/admin/Sources/Clients/ogagent/src/test_rest_server.py
new file mode 100644
index 00000000..ad1aede6
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/test_rest_server.py
@@ -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()
+ \ No newline at end of file
diff --git a/admin/Sources/Clients/ogagent/src/update.sh b/admin/Sources/Clients/ogagent/src/update.sh
new file mode 100755
index 00000000..78147fab
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/src/update.sh
@@ -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
+
diff --git a/admin/Sources/Clients/ogagent/windows/build-windows.sh b/admin/Sources/Clients/ogagent/windows/build-windows.sh
new file mode 100755
index 00000000..a861b092
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/windows/build-windows.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+export WINEARCH=win32
+export WINEPREFIX=$(realpath $(dirname $0)/wine)
+wine cmd /c c:\\ogagent\\build.bat
diff --git a/admin/Sources/Clients/ogagent/windows/build.bat b/admin/Sources/Clients/ogagent/windows/build.bat
new file mode 100644
index 00000000..2c444741
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/windows/build.bat
@@ -0,0 +1,6 @@
+C:
+CD \ogagent\src
+python setup.py
+CD ..
+"C:\Program Files\NSIS\makensis.exe" ogagent.nsi
+
diff --git a/admin/Sources/Clients/ogagent/windows/ogagent.nsi b/admin/Sources/Clients/ogagent/windows/ogagent.nsi
new file mode 100644
index 00000000..7e7cd71c
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/windows/ogagent.nsi
@@ -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)"
diff --git a/admin/Sources/Clients/ogagent/windows/py2exe-wine-linux.sh b/admin/Sources/Clients/ogagent/windows/py2exe-wine-linux.sh
new file mode 100755
index 00000000..70945500
--- /dev/null
+++ b/admin/Sources/Clients/ogagent/windows/py2exe-wine-linux.sh
@@ -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
+
+
diff --git a/installer/README.es.txt b/installer/README.es.txt
index 1b270926..a7879033 100644
--- a/installer/README.es.txt
+++ b/installer/README.es.txt
@@ -1,9 +1,10 @@
-OpenGnSys Installer README
+
+OpenGnsys Installer README
===============================
Este directorio contiene la documentación y los programas para instalar, actualizar y
-eliminar el Proyecto OpenGnSys.
+eliminar el Proyecto OpenGnsys.
- INSTALL.es.txt información de instalación y desinistalación
@@ -14,5 +15,11 @@ eliminar el Proyecto OpenGnSys.
- opengnsys_uninstall.sh programa de desinstalación del serviidor
- opengnsys_update.sh programa de actualización del serviidor
+- ogagent-devel-installer.sh programa de preparación del entorno de desarrollo y
+ compilación de agentes OGAgent para sistemas operativos
+
+- vagrant directorio con ficheros de configuración para despliegue
+ de entornos de prueba y desarrollo con Vagrant
+
- install_ticket_wolunicast.sh programa de instalación del parche para ejecutar
arranque Wake-On-Lan por IP usando protocolo Unicast
diff --git a/installer/ogagent-devel-installer.sh b/installer/ogagent-devel-installer.sh
new file mode 100755
index 00000000..d83c540b
--- /dev/null
+++ b/installer/ogagent-devel-installer.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+#/**
+#@file ogagent-devel-installer.sh
+#@brief Script to download and prepare the environmnt to compile OGAgent packages.
+#@warning Some operations need "root" privileges.
+#@note This script will make a "ogagent" directory with 1.5 GiB approx.
+#@version 1.0 - Initial version for OpenGnsys 1.1.0.
+#@author Ramón M. Gómez, ETSII Universidad de Sevilla
+#@date 2016-04-07
+#*/ ##
+
+
+# Variables.
+PROGDIR=$PWD/ogagent
+SVNURL=http://opengnsys.es/svn/branches/version1.1/admin/Sources/Clients/ogagent
+
+# Show prerequisites needed to build the environment.
+cat << EOT
+
+OGAgent devoloping environment installation
+
+Prerequisites:
+- Install packages, if needed:
+ - Wine for 32-bit with Winetricks
+ - Python 2.7 with pyqt4-dev-tools
+ - realpath
+ - dpkg-dev
+ - rpmbuild
+- Open a web browser and download Microsoft Visual C++ 2010 Redistributable Package (x86) from: http://www.microsoft.com/en-us/download/details.aspx?id=5555
+- Copy or move "vcredist_x86.exe" file to $PROGDIR directory.
+Press [Enter] key when ready to continue.
+EOT
+read
+
+# Importing OGAgent source code.
+mkdir -p $PROGDIR || exit 1
+svn export --force $SVNURL $PROGDIR || exit 1
+
+# Update PyQt components.
+pushd ogagent/src >/dev/null
+./update.sh
+popd >/dev/null
+
+# Showing instructions to configure Wine.
+cat << EOT
+
+Manual actions:
+- After all downloads, install Gecko for Wine, if needed.
+- Press [Esc] key or "Cancel" button on Winetricks screen.
+- Accept default settings for all other components
+- Uncheck all options on "Completing NSIS Setup" screen.
+Press [Enter] key to init downloads.
+
+EOT
+read
+
+# Downloading and configuring Wine prerequisites.
+pushd ogagent/windows >/dev/null
+./py2exe-wine-linux.sh
+cp -a build.bat ogagent.nsi ..
+ln -s ../../.. wine/drive_c/ogagent
+popd >/dev/null
+
+# Build OGAgent for GNU/Linux.
+pushd $PROGDIR/linux >/dev/null
+sudo ./build-packages.sh
+popd >/dev/null
+
+# Build OGAgent for Windows.
+pushd $PROGDIR/windows >/dev/null
+./build-windows.sh
+popd >/dev/null
+
+# Showing instructions to rebuild OGAgent packages.
+cat << EOT
+
+How to rebuild OGAgent packages
+-------------------------------
+OGAgent project source code is available in $PROGDIR/src directory.
+
+- Commands to update PyQt graphical components for OGAgnet:
+ cd $PROGDIR/src
+ ./update.sh
+
+- Commands to rebuild GNU/Linux packages:
+ cd $PROGDIR/linux
+ sudo ./build-packages.sh
+
+- Commands to rebuild Windows installer:
+ cd $PROGDIR/windows
+ ./build-windows.sh
+
+OGAgent packages will be created into $PROGDIR directory.
+
+EOT
+
diff --git a/installer/vagrant/Vagrantfile-ogagent-vbox b/installer/vagrant/Vagrantfile-ogagent-vbox
index 37f7e587..b41bc487 100644
--- a/installer/vagrant/Vagrantfile-ogagent-vbox
+++ b/installer/vagrant/Vagrantfile-ogagent-vbox
@@ -51,7 +51,7 @@ echo "Icon=/opt/eclipse/icon.xpm" >> /home/vagrant/Escritorio/eclipse.desktop
echo "Terminal=false" >> /home/vagrant/Escritorio/eclipse.desktop
chown -R vagrant.vagrant /home/vagrant/Escritorio
# Download OGAgent environment installer.
-svn export http://opengnsys.es/svn/branches/version1.1-tickets/OGAgent-ticket718/installer/ogagent-devel-installer.sh /home/vagrant
+svn export http://opengnsys.es/svn/branches/version1.1/installer/ogagent-devel-installer.sh /home/vagrant
# Instructions.
echo "Manual operations:"
echo "- Launch desktop: startxfce4 &"