diff options
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><html><head/><body><p><img src=":/images/img/oga.png"/>OpenGnsys Agent Tools</p></body></html></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><html><head/><body><p><span style=" font-family:'Sans Serif'; font-size:9pt; font-weight:600;">OpenGnsys Agent</span></p></body></html></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>&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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;"> +<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> +<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> +<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> +<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> +<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> +<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></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="authorsTab"> + <attribute name="title"> + <string>A&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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;"> +<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></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="licenseTab"> + <attribute name="title"> + <string>&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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;"> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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> +<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></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 <agomez@virtualcable.es></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 "AS IS"</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 Binary files differnew file mode 100644 index 00000000..37ded487 --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/img/oga-48x48.ico diff --git a/admin/Sources/Clients/ogagent/src/img/oga-512.png b/admin/Sources/Clients/ogagent/src/img/oga-512.png Binary files differnew file mode 100644 index 00000000..37a0dbe4 --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/img/oga-512.png diff --git a/admin/Sources/Clients/ogagent/src/img/oga.ico b/admin/Sources/Clients/ogagent/src/img/oga.ico Binary files differnew file mode 100644 index 00000000..37ded487 --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/img/oga.ico diff --git a/admin/Sources/Clients/ogagent/src/img/oga.png b/admin/Sources/Clients/ogagent/src/img/oga.png Binary files differnew file mode 100644 index 00000000..70db4564 --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/img/oga.png 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¶m2=2') + This will generate a GET message to https://example.com/rest/v1/hello?param1=1¶m2=2, and return the deserialized JSON result or an exception + v.sendMessage('hello?param1=1¶m2=2', {'name': 'mario' }) + This will generate a POST message to https://example.com/rest/v1/hello?param1=1¶m2=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 &" |