From 412401803144c83698840c51c301d38cc1c5c928 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Mon, 18 Jun 2018 13:54:44 +0200 Subject: #750: Process to build OGAgent for ogLive package. --- admin/Sources/Clients/ogagent/oglive/Makefile | 57 +++++ .../Clients/ogagent/oglive/build-packages.sh | 11 + .../Clients/ogagent/oglive/debian/changelog | 6 + admin/Sources/Clients/ogagent/oglive/debian/compat | 1 + .../Sources/Clients/ogagent/oglive/debian/control | 15 ++ .../Clients/ogagent/oglive/debian/copyright | 26 +++ admin/Sources/Clients/ogagent/oglive/debian/docs | 1 + .../ogagent/oglive/debian/ogagent-oglive.links | 2 + .../ogagent/oglive/debian/ogagent-oglive.postinst | 21 ++ .../debian/ogagent-oglive.postinst.debhelper | 5 + .../ogagent/oglive/debian/ogagent-oglive.postrm | 10 + .../oglive/debian/ogagent-oglive.postrm.debhelper | 12 ++ .../ogagent/oglive/debian/ogagent-oglive.substvars | 2 + .../Clients/ogagent/oglive/debian/ogagent.init | 23 +++ admin/Sources/Clients/ogagent/oglive/debian/rules | 44 ++++ .../Clients/ogagent/oglive/debian/source/format | 1 + admin/Sources/Clients/ogagent/oglive/readme.txt | 3 + .../Clients/ogagent/oglive/scripts/OGAgentTool | 6 + .../ogagent/oglive/scripts/OGAgentTool-startup | 10 + .../Sources/Clients/ogagent/oglive/scripts/ogagent | 6 + .../ogagent/src/opengnsys/linux/operations.py | 12 +- .../ogagent/src/opengnsys/oglive/__init__.py | 32 +++ .../Clients/ogagent/src/opengnsys/oglive/daemon.py | 182 ++++++++++++++++ .../ogagent/src/opengnsys/oglive/operations.py | 230 +++++++++++++++++++++ .../Clients/ogagent/src/opengnsys/operations.py | 13 +- 25 files changed, 718 insertions(+), 13 deletions(-) create mode 100644 admin/Sources/Clients/ogagent/oglive/Makefile create mode 100755 admin/Sources/Clients/ogagent/oglive/build-packages.sh create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/changelog create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/compat create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/control create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/copyright create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/docs create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.links create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postinst create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postinst.debhelper create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postrm create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postrm.debhelper create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.substvars create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/ogagent.init create mode 100755 admin/Sources/Clients/ogagent/oglive/debian/rules create mode 100644 admin/Sources/Clients/ogagent/oglive/debian/source/format create mode 100644 admin/Sources/Clients/ogagent/oglive/readme.txt create mode 100644 admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool create mode 100644 admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool-startup create mode 100644 admin/Sources/Clients/ogagent/oglive/scripts/ogagent create mode 100644 admin/Sources/Clients/ogagent/src/opengnsys/oglive/__init__.py create mode 100644 admin/Sources/Clients/ogagent/src/opengnsys/oglive/daemon.py create mode 100644 admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py diff --git a/admin/Sources/Clients/ogagent/oglive/Makefile b/admin/Sources/Clients/ogagent/oglive/Makefile new file mode 100644 index 00000000..832e2dc7 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/Makefile @@ -0,0 +1,57 @@ +#!/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 + +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 $(LIBDIR)/img + + # Cleans up .pyc and cache folders + rm -f $(PYC) $(CACHES) + + cp -r $(SOURCEDIR)/opengnsys $(LIBDIR)/opengnsys + cp -r $(SOURCEDIR)/cfg $(LIBDIR)/cfg + + # 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 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/oglive/build-packages.sh b/admin/Sources/Clients/ogagent/oglive/build-packages.sh new file mode 100755 index 00000000..4d5eb8d2 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/build-packages.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +cd $(dirname "$0") +top=`pwd` + +[ -r ../src/VERSION ] && VERSION="$(cat ../src/VERSION)" || VERSION="1.1.0" +RELEASE="1" + +# Debian based +dpkg-buildpackage -b -d + diff --git a/admin/Sources/Clients/ogagent/oglive/debian/changelog b/admin/Sources/Clients/ogagent/oglive/debian/changelog new file mode 100644 index 00000000..57a05aba --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/changelog @@ -0,0 +1,6 @@ +ogagent-oglive (1.1.1) unstable; urgency=medium + + * Initial release. + + -- Ramón M. Gómez Mon, 18 Jun 2018 13:00:00 +0200 + diff --git a/admin/Sources/Clients/ogagent/oglive/debian/compat b/admin/Sources/Clients/ogagent/oglive/debian/compat new file mode 100644 index 00000000..f11c82a4 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/compat @@ -0,0 +1 @@ +9 \ No newline at end of file diff --git a/admin/Sources/Clients/ogagent/oglive/debian/control b/admin/Sources/Clients/ogagent/oglive/debian/control new file mode 100644 index 00000000..374bc554 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/control @@ -0,0 +1,15 @@ +Source: ogagent-oglive +Section: admin +Priority: optional +Maintainer: Ramón M. Gómez +Build-Depends: debhelper (>= 7), po-debconf +Standards-Version: 3.9.2 +Homepage: https://opengnsys.es + +Package: ogagent-oglive +Section: admin +Priority: optional +Architecture: all +Depends: python-requests (>=0.8.2), python-six(>=1.1), python-prctl(>=1.1.1), python (>=2.7), libxss1, ${misc:Depends} +Description: OpenGnsys Agent for ogLive client + 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/oglive/debian/copyright b/admin/Sources/Clients/ogagent/oglive/debian/copyright new file mode 100644 index 00000000..7b6ef31b --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/copyright @@ -0,0 +1,26 @@ +Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 +Name: ogagent +Maintainer: Ramón M. Gómez +Source: https://opengnsys.es + +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'. diff --git a/admin/Sources/Clients/ogagent/oglive/debian/docs b/admin/Sources/Clients/ogagent/oglive/debian/docs new file mode 100644 index 00000000..b2b2a781 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/docs @@ -0,0 +1 @@ +readme.txt diff --git a/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.links b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.links new file mode 100644 index 00000000..9b970d7b --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.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/oglive/debian/ogagent-oglive.postinst b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postinst new file mode 100644 index 00000000..b59cfa6f --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.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/oglive/debian/ogagent-oglive.postinst.debhelper b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postinst.debhelper new file mode 100644 index 00000000..e75924dc --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.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/oglive/debian/ogagent-oglive.postrm b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postrm new file mode 100644 index 00000000..a46fa487 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.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/oglive/debian/ogagent-oglive.postrm.debhelper b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.postrm.debhelper new file mode 100644 index 00000000..3167f1f8 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.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/oglive/debian/ogagent-oglive.substvars b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.substvars new file mode 100644 index 00000000..978fc8b5 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/admin/Sources/Clients/ogagent/oglive/debian/ogagent.init b/admin/Sources/Clients/ogagent/oglive/debian/ogagent.init new file mode 100644 index 00000000..3eee35c1 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/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/oglive/debian/rules b/admin/Sources/Clients/ogagent/oglive/debian/rules new file mode 100755 index 00000000..0a5c3e5c --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/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-oglive 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/oglive/debian/source/format b/admin/Sources/Clients/ogagent/oglive/debian/source/format new file mode 100644 index 00000000..9f674278 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/debian/source/format @@ -0,0 +1 @@ +3.0 (native) \ No newline at end of file diff --git a/admin/Sources/Clients/ogagent/oglive/readme.txt b/admin/Sources/Clients/ogagent/oglive/readme.txt new file mode 100644 index 00000000..a2771def --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/readme.txt @@ -0,0 +1,3 @@ +OGAgent is the agent intended for OpengGnsys interaction. + +Please, visit https://opengnsys.es for more information diff --git a/admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool b/admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool new file mode 100644 index 00000000..5b300523 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool @@ -0,0 +1,6 @@ +#!/bin/sh + +FOLDER=/usr/share/OGAgent + +cd $FOLDER +python OGAgentUser.py $@ diff --git a/admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool-startup b/admin/Sources/Clients/ogagent/oglive/scripts/OGAgentTool-startup new file mode 100644 index 00000000..bb3a848e --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/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/oglive/scripts/ogagent b/admin/Sources/Clients/ogagent/oglive/scripts/ogagent new file mode 100644 index 00000000..1bcc29b0 --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/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/src/opengnsys/linux/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py index f9534d6a..cd6f4b97 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/linux/operations.py @@ -152,11 +152,7 @@ def reboot(flags=0): 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']) + subprocess.call(['/sbin/reboot']) def poweroff(flags=0): @@ -168,11 +164,7 @@ def poweroff(flags=0): 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']) + subprocess.call(['/sbin/poweroff']) def logoff(): diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/__init__.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/__init__.py new file mode 100644 index 00000000..3a98c780 --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/__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/oglive/daemon.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/daemon.py new file mode 100644 index 00000000..3753808a --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/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: + for i in range(10): + 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/oglive/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py new file mode 100644 index 00000000..ded43791 --- /dev/null +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py @@ -0,0 +1,230 @@ +# -*- 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: Ramón M. Gómez, ramongomez at us dot es +''' +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 + + +def checkLockedPartition(sync=False): + ''' + Decorator to check if a partition is locked + ''' + def outer(fnc): + def wrapper(*args, **kwargs): + partId = 'None' + try: + this, path, getParams, postParams = args # @UnusedVariable + partId = postParams['disk'] + postParams['part'] + if this.locked.get(partId, False): + this.locked[partId] = True + fnc(*args, **kwargs) + else: + return 'partition locked' + except Exception as e: + this.locked[partId] = False + return 'error {}'.format(e) + finally: + if sync is True: + this.locked[partId] = False + logger.debug('Lock status: {} {}'.format(fnc, this.locked)) + return wrapper + return outer + + +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 _exec_ogcommand(self, ogcmd): + ''' + Loads OpenGnsys environment variables, executes the command and returns the result + ''' + ret = subprocess.check_output('source /opt/opengnsys/etc/preinit/loadenviron.sh >/dev/null; {}'.format(ogcmd), shell=True) + return ret + + +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 getOgliveVersion(): + lv = platform.linux_distribution() + return lv[0] + ', ' + lv[1] + + +def reboot(): + ''' + Simple reboot using OpenGnsys script + ''' + # Workaround for dummy thread + if six.PY3 is False: + import threading + threading._DummyThread._Thread__stop = lambda x: 42 + + _exec_ogcommand('/opt/opengnsys/scripts/reboot', shell=True) + + +def poweroff(): + ''' + Simple poweroff using OpenGnsys script + ''' + # Workaround for dummy thread + if six.PY3 is False: + import threading + threading._DummyThread._Thread__stop = lambda x: 42 + + _exec_ogcommand('/opt/opengnsys/scripts/poweroff', shell=True) + + +def logoff(): + pass + + +def renameComputer(newName): + pass + + +def joinDomain(domain, ou, account, password, executeInOneStep=False): + pass + + +def changeUserPassword(user, oldPassword, newPassword): + pass + + +def diskconfig(): + ''' + Returns disk configuration. + Warning: this operation may take some time. + ''' + try: + _exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration') + # Returns content of configuration file. + cfgdata = open('/tmp/getconfig', 'r').read() + except IOError: + cfgdata = '' + return cfgdata + diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py index dcfa40cb..9015cbec 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py @@ -34,6 +34,8 @@ from __future__ import unicode_literals import sys +import os + # Importing platform operations and getting operating system data. if sys.platform == 'win32': from .windows.operations import * # @UnusedWildImport @@ -45,6 +47,11 @@ else: osType = 'MacOS' osVersion = getMacosVersion().replace(',','') else: - from .linux.operations import * # @UnusedWildImport - osType = 'Linux' - osVersion = getLinuxVersion().replace(',','') + if os.path.exists('/scripts/oginit'): + from .oglive.operations import * # @UnusedWildImport + osType = 'ogLive' + osVersion = getOgliveVersion().replace(',','') + else: + from .linux.operations import * # @UnusedWildImport + osType = 'Linux' + osVersion = getLinuxVersion().replace(',','') -- cgit v1.2.3-18-g5258 From 1246429a3955e5530842f02d5c06d679a3f3e58e Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Mon, 18 Jun 2018 20:47:35 +0200 Subject: #750: Using PEP 8 Style Guide for Python in new module; implementing basic {{{getconfig}}} operation. --- .../Clients/ogagent/oglive/build-package.sh | 7 + .../Clients/ogagent/oglive/build-packages.sh | 11 -- .../opengnsys/modules/server/OpenGnSys/__init__.py | 162 +++++++++++++-------- .../ogagent/src/opengnsys/oglive/operations.py | 113 +++++--------- .../Clients/ogagent/src/opengnsys/operations.py | 22 +-- installer/vagrant/Vagrantfile-ogagent-vbox | 2 +- 6 files changed, 156 insertions(+), 161 deletions(-) create mode 100755 admin/Sources/Clients/ogagent/oglive/build-package.sh delete mode 100755 admin/Sources/Clients/ogagent/oglive/build-packages.sh diff --git a/admin/Sources/Clients/ogagent/oglive/build-package.sh b/admin/Sources/Clients/ogagent/oglive/build-package.sh new file mode 100755 index 00000000..c0e03c5f --- /dev/null +++ b/admin/Sources/Clients/ogagent/oglive/build-package.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd $(dirname "$0") + +# Build package +dpkg-buildpackage -b -d + diff --git a/admin/Sources/Clients/ogagent/oglive/build-packages.sh b/admin/Sources/Clients/ogagent/oglive/build-packages.sh deleted file mode 100755 index 4d5eb8d2..00000000 --- a/admin/Sources/Clients/ogagent/oglive/build-packages.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -cd $(dirname "$0") -top=`pwd` - -[ -r ../src/VERSION ] && VERSION="$(cat ../src/VERSION)" || VERSION="1.1.0" -RELEASE="1" - -# Debian based -dpkg-buildpackage -b -d - 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 index 1438e469..01fa1072 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -25,29 +25,27 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' +""" @author: Ramón M. Gómez, ramongomez at us dot es -''' +""" from __future__ import unicode_literals -import subprocess -import threading -import thread import os -import platform -import time import random import string +import threading +import time import urllib -from opengnsys.workers import ServerWorker -from opengnsys import REST, RESTError +from opengnsys import REST from opengnsys import operations from opengnsys.log import logger from opengnsys.scriptThread import ScriptExecutorThread +from opengnsys.workers import ServerWorker + # Error handler decorator. -def catchBackgroundError(fnc): +def catch_background_error(fnc): def wrapper(*args, **kwargs): this = args[0] try: @@ -56,18 +54,45 @@ def catchBackgroundError(fnc): this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(e)}) return wrapper + +def check_locked_partition(sync=False): + """ + Decorator to check if a partition is locked + """ + def outer(fnc): + def wrapper(*args, **kwargs): + part_id = 'None' + try: + this, path, get_params, post_params = args # @UnusedVariable + part_id = post_params['disk'] + post_params['part'] + if this.locked.get(part_id, False): + this.locked[part_id] = True + fnc(*args, **kwargs) + else: + return 'partition locked' + except Exception as e: + this.locked[part_id] = False + return 'error {}'.format(e) + finally: + if sync is True: + this.locked[part_id] = False + logger.debug('Lock status: {} {}'.format(fnc, this.locked)) + return wrapper + return outer + + class OpenGnSysWorker(ServerWorker): name = 'opengnsys' interface = None # Binded interface for OpenGnsys loggedin = False # User session flag - locked = {} + locked = {} # Locked partitions random = None # Random string for secure connections length = 32 # Random string length def checkSecret(self, server): - ''' + """ Checks for received secret key and raise exception if it isn't valid. - ''' + """ try: if self.random != server.headers['Authorization']: raise Exception('Unauthorized operation') @@ -76,9 +101,9 @@ class OpenGnSysWorker(ServerWorker): raise Exception(e) def onActivation(self): - ''' + """ Sends OGAgent activation notification to OpenGnsys server - ''' + """ self.cmd = None # Ensure cfg has required configuration variables or an exception will be thrown self.REST = REST(self.service.config.get('opengnsys', 'remote')) @@ -107,52 +132,59 @@ class OpenGnSysWorker(ServerWorker): # Generate random secret to send on activation self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length)) # Send initalization message - self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip, 'secret': self.random, 'ostype': operations.osType, 'osversion': operations.osVersion}) + self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip, + 'secret': self.random, 'ostype': operations.os_type, + 'osversion': operations.os_version}) def onDeactivation(self): - ''' + """ Sends OGAgent stopping notification to OpenGnsys server - ''' + """ logger.debug('onDeactivation') - self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip, 'ostype': operations.osType, 'osversion': operations.osVersion}) + self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip, + 'ostype': operations.os_type, 'osversion': operations.os_version}) def processClientMessage(self, message, data): logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data)) def onLogin(self, userData): - ''' + """ Sends session login notification to OpenGnsys server - ''' + """ user, sep, language = userData.partition(',') logger.debug('Received login for {} with language {}'.format(user, language)) self.loggedin = True - self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, 'user': user, 'language': language, 'ostype': operations.osType, 'osversion': operations.osVersion}) + self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, 'user': user, 'language': language, + 'ostype': operations.os_type, 'osversion': operations.os_version}) def onLogout(self, user): - ''' + """ Sends session logout notification to OpenGnsys server - ''' + """ 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, server): - ''' - This method can be overriden to provide your own message proccessor, or better you can - implement a method that is called exactly as "process_" + path[0] (module name has been removed from path array) and this default processMessage will invoke it + """ + his 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 patharray) 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: + 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 + 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) - ''' + The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, + basic type are) + """ if not path: return "ok" try: @@ -162,34 +194,27 @@ class OpenGnSysWorker(ServerWorker): return operation(path[1:], getParams, postParams) def process_status(self, path, getParams, postParams, server): - ''' + """ 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' + """ + st = {'linux': 'LNX', 'macos': 'OSX', 'oglive': 'OPG', 'windows': 'WIN'} + res = {'loggedin': self.loggedin} + try: + res['status'] = st[operations.os_type.lower()] + except KeyError: + res['status'] = '' + # Check if OpenGnsys Client is busy + if res['status'] == 'OPG' and self.locked: + res['status'] = 'BSY' return res def process_reboot(self, path, getParams, postParams, server): - ''' + """ Launches a system reboot operation. - ''' + """ logger.debug('Received reboot operation') self.checkSecret(server) + # Rebooting thread. def rebt(): operations.reboot() @@ -197,11 +222,12 @@ class OpenGnSysWorker(ServerWorker): return {'op': 'launched'} def process_poweroff(self, path, getParams, postParams, server): - ''' + """ Launches a system power off operation. - ''' + """ logger.debug('Received poweroff operation') self.checkSecret(server) + # Powering off thread. def pwoff(): time.sleep(2) @@ -210,9 +236,9 @@ class OpenGnSysWorker(ServerWorker): return {'op': 'launched'} def process_script(self, path, getParams, postParams, server): - ''' + """ Processes an script execution (script should be encoded in base64) - ''' + """ logger.debug('Processing script request') self.checkSecret(server) # Decoding script. @@ -227,9 +253,9 @@ class OpenGnSysWorker(ServerWorker): return {'op': 'launched'} def process_logoff(self, path, getParams, postParams, server): - ''' + """ Closes user session. - ''' + """ logger.debug('Received logoff operation') self.checkSecret(server) # Sending log off message to OGAgent client. @@ -237,9 +263,9 @@ class OpenGnSysWorker(ServerWorker): return {'op': 'sended to client'} def process_popup(self, path, getParams, postParams, server): - ''' + """ Shows a message popup on the user's session. - ''' + """ logger.debug('Received message operation') self.checkSecret(server) # Sending popup message to OGAgent client. @@ -248,3 +274,17 @@ class OpenGnSysWorker(ServerWorker): def process_client_popup(self, params): self.REST.sendMessage('popup_done', params) + + def process_getconfig(self, path, get_params, post_params, server): + """ + Returns client configuration + :param path: + :param get_params: + :param post_params: + :param server: + :return: object + """ + logger.debug('Recieved getconfig operation') + self.checkSecret(server) + # Returns raw data + return {'config': operations.get_disk_config()} diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py index ded43791..6c67421b 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py @@ -26,17 +26,14 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' +""" @author: Ramón M. Gómez, ramongomez at us dot es -''' +""" 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 @@ -44,37 +41,11 @@ import six from opengnsys import utils -def checkLockedPartition(sync=False): - ''' - Decorator to check if a partition is locked - ''' - def outer(fnc): - def wrapper(*args, **kwargs): - partId = 'None' - try: - this, path, getParams, postParams = args # @UnusedVariable - partId = postParams['disk'] + postParams['part'] - if this.locked.get(partId, False): - this.locked[partId] = True - fnc(*args, **kwargs) - else: - return 'partition locked' - except Exception as e: - this.locked[partId] = False - return 'error {}'.format(e) - finally: - if sync is True: - this.locked[partId] = False - logger.debug('Lock status: {} {}'.format(fnc, this.locked)) - return wrapper - return outer - - 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): @@ -88,10 +59,10 @@ def _getMacAddr(ifname): 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): @@ -108,9 +79,9 @@ def _getIpAddr(ifname): 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': @@ -134,32 +105,32 @@ def _getInterfaces(): def _getIpAndMac(ifname): ip, mac = _getIpAddr(ifname), _getMacAddr(ifname) - return (ip, mac) + return ip, mac -def _exec_ogcommand(self, ogcmd): - ''' +def _exec_ogcommand(ogcmd): + """ Loads OpenGnsys environment variables, executes the command and returns the result - ''' - ret = subprocess.check_output('source /opt/opengnsys/etc/preinit/loadenviron.sh >/dev/null; {}'.format(ogcmd), shell=True) + """ + ret = subprocess.check_output(ogcmd, shell=True) return ret 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: + :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 @@ -170,56 +141,44 @@ def getDomainName(): return '' -def getOgliveVersion(): - lv = platform.linux_distribution() - return lv[0] + ', ' + lv[1] +def get_oglive_version(): + """ + Returns ogLive Kernel version and architecture + :return: kernel version + """ + kv = platform.os.uname() + return kv[2] + ', ' + kv[4] def reboot(): - ''' + """ Simple reboot using OpenGnsys script - ''' + """ # Workaround for dummy thread if six.PY3 is False: import threading threading._DummyThread._Thread__stop = lambda x: 42 - _exec_ogcommand('/opt/opengnsys/scripts/reboot', shell=True) + _exec_ogcommand('/opt/opengnsys/scripts/reboot') def poweroff(): - ''' + """ Simple poweroff using OpenGnsys script - ''' + """ # Workaround for dummy thread if six.PY3 is False: import threading threading._DummyThread._Thread__stop = lambda x: 42 - _exec_ogcommand('/opt/opengnsys/scripts/poweroff', shell=True) - - -def logoff(): - pass - - -def renameComputer(newName): - pass - - -def joinDomain(domain, ou, account, password, executeInOneStep=False): - pass - - -def changeUserPassword(user, oldPassword, newPassword): - pass + _exec_ogcommand('/opt/opengnsys/scripts/poweroff') -def diskconfig(): - ''' - Returns disk configuration. - Warning: this operation may take some time. - ''' +def get_disk_config(): + """ + Returns disk configuration + Warning: this operation may take some time + """ try: _exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration') # Returns content of configuration file. diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py index 9015cbec..926e5a2c 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py @@ -26,9 +26,9 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' +""" +@author: Ramón M. Gómez, ramongomez at us dot es +""" # pylint: disable=unused-wildcard-import,wildcard-import from __future__ import unicode_literals @@ -39,19 +39,19 @@ import os # Importing platform operations and getting operating system data. if sys.platform == 'win32': from .windows.operations import * # @UnusedWildImport - osType = 'Windows' - osVersion = getWindowsVersion() + os_type = 'Windows' + os_version = getWindowsVersion() else: if sys.platform == 'darwin': from .macos.operations import * # @UnusedWildImport - osType = 'MacOS' - osVersion = getMacosVersion().replace(',','') + os_type = 'MacOS' + os_version = getMacosVersion().replace(',', '') else: if os.path.exists('/scripts/oginit'): from .oglive.operations import * # @UnusedWildImport - osType = 'ogLive' - osVersion = getOgliveVersion().replace(',','') + os_type = 'ogLive' + os_version = get_oglive_version() else: from .linux.operations import * # @UnusedWildImport - osType = 'Linux' - osVersion = getLinuxVersion().replace(',','') + os_type = 'Linux' + os_version = getLinuxVersion().replace(',', '') diff --git a/installer/vagrant/Vagrantfile-ogagent-vbox b/installer/vagrant/Vagrantfile-ogagent-vbox index 1bf48faa..31760d65 100644 --- a/installer/vagrant/Vagrantfile-ogagent-vbox +++ b/installer/vagrant/Vagrantfile-ogagent-vbox @@ -21,7 +21,7 @@ localectl set-x11-keymap ${LANG%_*} # Update repositories. dnf install -y http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm # Install main dependencies. -dnf install -y gcc-c++ debhelper dpkg-dev pyqt4-devel rpm-build subversion samba-winbind wine.i686 mingw32-wine-gecko wine-mono cabextract xar +dnf install -y gcc-c++ perl-Digest-SHA debhelper dpkg-dev pyqt4-devel rpm-build subversion samba-winbind wine.i686 mingw32-wine-gecko wine-mono cabextract xar setsebool -P wine_mmap_zero_ignore=on mmap_low_allowed=on # Install desktop (XFCE) and GUI utils. dnf install -y @xfce-desktop-environment firefox VirtualBox-guest kmod-VirtualBox akmod-VirtualBox akmods -- cgit v1.2.3-18-g5258 From f916902b7ae0cc723f1547bb8922f3fe13068735 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Wed, 20 Jun 2018 19:48:49 +0200 Subject: #750: OGAgent for ogLive looks for {{{oglive}}} environ variable; route {{{GET /getconfig}}} returns data in JSON format. --- .../opengnsys/modules/server/OpenGnSys/__init__.py | 43 ++++++++++++++++++++-- .../ogagent/src/opengnsys/oglive/operations.py | 5 +-- 2 files changed, 42 insertions(+), 6 deletions(-) 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 index 01fa1072..70c09c20 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -42,6 +42,7 @@ from opengnsys import operations from opengnsys.log import logger from opengnsys.scriptThread import ScriptExecutorThread from opengnsys.workers import ServerWorker +from six.moves.urllib import parse # Error handler decorator. @@ -106,7 +107,12 @@ class OpenGnSysWorker(ServerWorker): """ self.cmd = None # Ensure cfg has required configuration variables or an exception will be thrown - self.REST = REST(self.service.config.get('opengnsys', 'remote')) + url = self.service.config.get('opengnsys', 'remote') + if operations.os_type == 'ogLive' and 'oglive' in os.environ: + # Replacing server IP if its running on ogLive clinet + logger.debug('Activating on ogLive client, new server is {}'.format(os.environ['oglive'])) + url = parse.urlsplit(url)._replace(netloc=os.environ['oglive']).geturl() + self.REST = REST(url) # Get network interfaces until they are active or timeout (5 minutes) for t in range(0, 300): try: @@ -284,7 +290,38 @@ class OpenGnSysWorker(ServerWorker): :param server: :return: object """ + serialno = '' # Serial number + storage = [] # Storage configuration + warnings = 0 # Number of warnings logger.debug('Recieved getconfig operation') self.checkSecret(server) - # Returns raw data - return {'config': operations.get_disk_config()} + # Processing data + for row in operations.get_disk_config().strip().split(';'): + cols = row.split(':') + if len(cols) == 1: + if cols[0] != '': + # Serial number + serialno = cols[0] + else: + # Skip blank rows + pass + elif len(cols) == 7: + disk, npart, tpart, fs, os, size, usage = cols + try: + if int(npart) == 0: + # Disk information + storage.append({'disk': int(disk), 'parttable': int(tpart), 'size': int(size)}) + else: + # Partition information + storage.append({'disk': int(disk), 'partition': int(npart), 'parttype': tpart, + 'filesystem': fs, 'operatingsystem': os, 'size': int(size), + 'usage': int(usage)}) + except ValueError: + logger.warn('Configuration parameter error: {}'.format(cols)) + warnings += 1 + else: + # Logging warnings + logger.warn('Configuration data error: {}'.format(cols)) + warnings += 1 + # Returning configuration data and count of warnings + return {'serialno': serialno, 'storage': storage, 'warnings': warnings} diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py index 6c67421b..e24e0b91 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py @@ -181,9 +181,8 @@ def get_disk_config(): """ try: _exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration') - # Returns content of configuration file. - cfgdata = open('/tmp/getconfig', 'r').read() + # Returns content of configuration file + cfgdata = open('/tmp/getconfig', 'r').read().strip() except IOError: cfgdata = '' return cfgdata - -- cgit v1.2.3-18-g5258 From 1c9cb7e66a14b88bcf0ca9b5365bebe1422841d7 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Wed, 20 Jun 2018 20:25:30 +0200 Subject: #750: New route {{{GET /hardware}}}. --- .../src/opengnsys/modules/server/OpenGnSys/__init__.py | 15 ++++++++++++++- .../Clients/ogagent/src/opengnsys/oglive/operations.py | 18 ++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) 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 index 70c09c20..70c8973a 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -296,7 +296,7 @@ class OpenGnSysWorker(ServerWorker): logger.debug('Recieved getconfig operation') self.checkSecret(server) # Processing data - for row in operations.get_disk_config().strip().split(';'): + for row in operations.get_configuration().split(';'): cols = row.split(':') if len(cols) == 1: if cols[0] != '': @@ -325,3 +325,16 @@ class OpenGnSysWorker(ServerWorker): warnings += 1 # Returning configuration data and count of warnings return {'serialno': serialno, 'storage': storage, 'warnings': warnings} + + def process_hardware(self, path, get_params, post_params, server): + """ + Returns client's hardware profile + :param path: + :param get_params: + :param post_params: + :param server: + """ + logger.debug('Recieved {} operation'.format(path)) + self.checkSecret(server) + # Returning raw data + return operations.get_hardware() diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py index e24e0b91..b22a6079 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py @@ -174,9 +174,9 @@ def poweroff(): _exec_ogcommand('/opt/opengnsys/scripts/poweroff') -def get_disk_config(): +def get_configuration(): """ - Returns disk configuration + Returns client's configuration Warning: this operation may take some time """ try: @@ -186,3 +186,17 @@ def get_disk_config(): except IOError: cfgdata = '' return cfgdata + + +def get_hardware(): + """ + Returns client's hardware list + """ + try: + filepath = _exec_ogcommand('/opt/opengnsys/scripts/listHardwareInfo').strip() + # Returns content of configuration file, skipping the header line and newline characters + with open(filepath, 'r') as f: + harddata = map(str.strip, f.readlines()[1:]) + except IOError: + harddata = '' + return harddata -- cgit v1.2.3-18-g5258 From 44fb992f4ad77a44aef8b2c31d6788be400a9106 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Thu, 21 Jun 2018 19:23:10 +0200 Subject: #750: Route {{{GET /getconfig}}} renamed as {{{GET /config}}}; route {{{GET /hardware}}} returns data in JSON format; new basic route {{{GET /software?disk=NDisk&part=NPart}}} --- .../opengnsys/modules/server/OpenGnSys/__init__.py | 27 ++++++++++++++++++---- .../ogagent/src/opengnsys/oglive/operations.py | 21 ++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) 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 index 70c8973a..91824721 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -281,7 +281,7 @@ class OpenGnSysWorker(ServerWorker): def process_client_popup(self, params): self.REST.sendMessage('popup_done', params) - def process_getconfig(self, path, get_params, post_params, server): + def process_config(self, path, get_params, post_params, server): """ Returns client configuration :param path: @@ -334,7 +334,26 @@ class OpenGnSysWorker(ServerWorker): :param post_params: :param server: """ - logger.debug('Recieved {} operation'.format(path)) + data = [] + logger.debug('Recieved hardware operation') self.checkSecret(server) - # Returning raw data - return operations.get_hardware() + # Processing data + try: + for comp in operations.get_hardware(): + data.append({'component': comp.split('=')[0], 'value': comp.split('=')[1]}) + except: + pass + # Return list of hardware components + return data + + def process_software(self, path, get_params, post_params, server): + """ + Returns software profile installed on an operating system + :param path: + :param get_params: + :param post_params: + :param server: + :return: + """ + logger.debug('Recieved software operation with params: {}'.format(post_params)) + return operations.get_software(post_params['disk'], post_params['part']) diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py index b22a6079..55417805 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py @@ -164,7 +164,7 @@ def reboot(): def poweroff(): """ - Simple poweroff using OpenGnsys script + Simple power off using OpenGnsys script """ # Workaround for dummy thread if six.PY3 is False: @@ -178,6 +178,7 @@ def get_configuration(): """ Returns client's configuration Warning: this operation may take some time + :return: """ try: _exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration') @@ -191,6 +192,7 @@ def get_configuration(): def get_hardware(): """ Returns client's hardware list + :return: """ try: filepath = _exec_ogcommand('/opt/opengnsys/scripts/listHardwareInfo').strip() @@ -200,3 +202,20 @@ def get_hardware(): except IOError: harddata = '' return harddata + + +def get_software(disk, part): + """ + Returns software list installed on an operating system + :param disk: + :param part: + :return: + """ + try: + filepath = _exec_ogcommand('/opt/opengnsys/scripts/listSoftwareInfo {} {}'.format(disk, part)).strip() + # Returns content of configuration file, skipping the header line and newline characters + with open(filepath, 'r') as f: + softdata = map(str.strip, f.readlines()) + except IOError: + softdata = '' + return softdata -- cgit v1.2.3-18-g5258 From 443f262f861be1655f9b4556d449aa7c5b834830 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Sat, 30 Jun 2018 17:13:45 +0200 Subject: #750: Using more descriptive status; new route {{{POST /command}}} to launch command/script in a callback thread that returns all data to a server route. --- .../opengnsys/modules/server/OpenGnSys/__init__.py | 49 ++++++++++++++++++---- .../ogagent/src/opengnsys/oglive/operations.py | 15 +++++++ admin/Sources/Clients/ogagent/src/setup.py | 2 +- 3 files changed, 56 insertions(+), 10 deletions(-) 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 index 91824721..da1410dc 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -64,7 +64,7 @@ def check_locked_partition(sync=False): def wrapper(*args, **kwargs): part_id = 'None' try: - this, path, get_params, post_params = args # @UnusedVariable + this, path, get_params, post_params, server = args # @UnusedVariable part_id = post_params['disk'] + post_params['part'] if this.locked.get(part_id, False): this.locked[part_id] = True @@ -201,17 +201,16 @@ class OpenGnSysWorker(ServerWorker): def process_status(self, path, getParams, postParams, server): """ - Returns client status. + Returns client status (OS type and login status). """ - st = {'linux': 'LNX', 'macos': 'OSX', 'oglive': 'OPG', 'windows': 'WIN'} res = {'loggedin': self.loggedin} try: - res['status'] = st[operations.os_type.lower()] + res['status'] = operations.os_type.lower() except KeyError: res['status'] = '' # Check if OpenGnsys Client is busy - if res['status'] == 'OPG' and self.locked: - res['status'] = 'BSY' + if res['status'] == 'oglive' and self.locked: + res['status'] = 'busy' return res def process_reboot(self, path, getParams, postParams, server): @@ -306,7 +305,7 @@ class OpenGnSysWorker(ServerWorker): # Skip blank rows pass elif len(cols) == 7: - disk, npart, tpart, fs, os, size, usage = cols + disk, npart, tpart, fs, opsys, size, usage = cols try: if int(npart) == 0: # Disk information @@ -314,7 +313,7 @@ class OpenGnSysWorker(ServerWorker): else: # Partition information storage.append({'disk': int(disk), 'partition': int(npart), 'parttype': tpart, - 'filesystem': fs, 'operatingsystem': os, 'size': int(size), + 'filesystem': fs, 'operatingsystem': opsys, 'size': int(size), 'usage': int(usage)}) except ValueError: logger.warn('Configuration parameter error: {}'.format(cols)) @@ -326,6 +325,38 @@ class OpenGnSysWorker(ServerWorker): # Returning configuration data and count of warnings return {'serialno': serialno, 'storage': storage, 'warnings': warnings} + def task_command(self, code, route): + """ + Task to execute a command + :param code: Code to execute + :param route: server REST route to return results (including its parameters) + """ + (stat, out, err) = operations.exec_command(code) + self.REST.sendMessage(route, {'status': stat, 'output': out, 'error': err}) + + def process_command(self, path, get_params, post_params, server): + """ + Launches a thread to executing a command + :param path: ignored + :param get_params: ignored + :param post_params: object with format {"id": OperationId, "code": "Code", url: "ReturnURL"} + :param server: ignored + :rtype: object with launching status + """ + logger.debug('Recieved command operation with params: {}'.format(post_params)) + self.checkSecret(server) + # Processing data + try: + code = post_params.get('code') + cmd_id = post_params.get('id') + route = '{}?id={}'.format(post_params.get('route'), cmd_id) + # Launching new thread + threading.Thread(target=self.task_command, args=(code, route)).start() + except Exception as e: + logger.error('Got exception {}'.format(e)) + return {'error': e} + return {'op': 'launched'} + def process_hardware(self, path, get_params, post_params, server): """ Returns client's hardware profile @@ -356,4 +387,4 @@ class OpenGnSysWorker(ServerWorker): :return: """ logger.debug('Recieved software operation with params: {}'.format(post_params)) - return operations.get_software(post_params['disk'], post_params['part']) + return operations.get_software(post_params.get('disk'), post_params.get('part')) diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py index 55417805..342cfb06 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/oglive/operations.py @@ -189,6 +189,21 @@ def get_configuration(): return cfgdata +def exec_command(cmd): + """ + Executing a shell command + :param cmd: + :return: object with components: + output: standard output + error: error output + exit: exit code + """ + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (out, err) = proc.communicate() + stat = proc.returncode + return stat, out, err + + def get_hardware(): """ Returns client's hardware list diff --git a/admin/Sources/Clients/ogagent/src/setup.py b/admin/Sources/Clients/ogagent/src/setup.py index 15254f4f..617d5186 100644 --- a/admin/Sources/Clients/ogagent/src/setup.py +++ b/admin/Sources/Clients/ogagent/src/setup.py @@ -139,5 +139,5 @@ setup( description='OpenGnsys Agent', author='Adolfo Gomez', author_email='agomez@virtualcable.es', - zipfile='OGAgent.zip', + zipfile='OGAgent.zip', requires=['six'] ) -- cgit v1.2.3-18-g5258 From aabdca9d63e0195da4f4ec8424b18f452cb75fd7 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Mon, 2 Jul 2018 11:24:58 +0200 Subject: #750: Example route to processing outputs after OGAgent executes a command. --- admin/WebConsole/rest/ogagent.php | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/admin/WebConsole/rest/ogagent.php b/admin/WebConsole/rest/ogagent.php index 6b87b098..445ee239 100644 --- a/admin/WebConsole/rest/ogagent.php +++ b/admin/WebConsole/rest/ogagent.php @@ -269,4 +269,37 @@ EOD; } ); -?> +// Processing command results (TESTING). +$app->post('/ogagent/done', + // 'validateClient', + function() use ($app) { + global $cmd; + + try { + // Reading parameters. + $input = json_decode($app->request()->getBody()); + $status = htmlspecialchars($input->status); + $output = htmlspecialchars($input->output); + $error = htmlspecialchars($input->error); + // (... read "id" as GET parameter ...) + // ... + $client = $_SERVER['REMOTE_ADDR']; + // Check sender agent type. + if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { + throw new Exception("Bad OGAgent: sender=".$client.", agent=".$_SERVER['HTTP_USER_AGENT']); + } + // TODO: truncating outputs. + if ($status == 0) { + writeLog($app->request()->getResourceUri().": Operation OK: client=$client, id=??, output=$output"); + } else { + writeLog($app->request()->getResourceUri().": Operation ERROR: client=$client, id=??, status=$status, error=$error"); + } + } catch (Exception $e) { + // Communication error. + $response["message"] = $e->getMessage(); + writeLog($app->request()->getResourceUri().": ERROR: ".$response["message"]); + jsonResponse(400, $response); + } + } +); + -- cgit v1.2.3-18-g5258 From c9729a047e6d940e1378c6cc9d86a04c7b0bdc98 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Tue, 3 Jul 2018 17:55:25 +0200 Subject: #750: Simple REST route to get the list of running commands. --- .../opengnsys/modules/server/OpenGnSys/__init__.py | 55 +++++++++++++++++----- .../Clients/ogagent/src/opengnsys/operations.py | 2 +- 2 files changed, 45 insertions(+), 12 deletions(-) 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 index da1410dc..7a2ac6a3 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -87,6 +87,7 @@ class OpenGnSysWorker(ServerWorker): interface = None # Binded interface for OpenGnsys loggedin = False # User session flag locked = {} # Locked partitions + commands = [] # Running commands random = None # Random string for secure connections length = 32 # Random string length @@ -292,7 +293,7 @@ class OpenGnSysWorker(ServerWorker): serialno = '' # Serial number storage = [] # Storage configuration warnings = 0 # Number of warnings - logger.debug('Recieved getconfig operation') + logger.debug('Received getconfig operation') self.checkSecret(server) # Processing data for row in operations.get_configuration().split(';'): @@ -325,38 +326,70 @@ class OpenGnSysWorker(ServerWorker): # Returning configuration data and count of warnings return {'serialno': serialno, 'storage': storage, 'warnings': warnings} - def task_command(self, code, route): + def task_command(self, code, route, op_id): """ Task to execute a command :param code: Code to execute - :param route: server REST route to return results (including its parameters) + :param route: server callback REST route to return results + :param op_id: operation id. """ + # Executing command (stat, out, err) = operations.exec_command(code) - self.REST.sendMessage(route, {'status': stat, 'output': out, 'error': err}) + # Removing command from the list + for c in self.commands: + if c.has_key('id') and c['id'] == op_id: + self.commands.remove(c) + # Sending results + self.REST.sendMessage(route, {'id': op_id, 'status': stat, 'output': out, 'error': err}) def process_command(self, path, get_params, post_params, server): """ Launches a thread to executing a command :param path: ignored :param get_params: ignored - :param post_params: object with format {"id": OperationId, "code": "Code", url: "ReturnURL"} - :param server: ignored + :param post_params: object with format: + id: operation id. + code: command code + route: callback URL + :param server: headers data :rtype: object with launching status """ - logger.debug('Recieved command operation with params: {}'.format(post_params)) + logger.debug('Received command operation with params: {}'.format(post_params)) self.checkSecret(server) # Processing data try: code = post_params.get('code') - cmd_id = post_params.get('id') - route = '{}?id={}'.format(post_params.get('route'), cmd_id) - # Launching new thread - threading.Thread(target=self.task_command, args=(code, route)).start() + op_id = post_params.get('id') + route = post_params.get('route') + # Checking if the thread id. exists + for c in self.commands: + if c.has_key('id') and c['id'] == op_id: + raise Exception('Task id. already exists: {}'.format(op_id)) + # Launching a new thread + thr = threading.Thread(name=op_id, target=self.task_command, args=(code, route, op_id)) + thr.start() + self.commands.append({'id': op_id, 'code': code}) except Exception as e: logger.error('Got exception {}'.format(e)) return {'error': e} return {'op': 'launched'} + def process_execinfo(self, path, get_params, post_params, server): + """ + Returns running commands information + :param path: + :param get_params: + :param post_params: + :param server: + :return: object + """ + #data = [] + #for c in self.commands: + # if c.is_alive(): + # data.append({'name': c.getName(), 'code': c.__dict__['_Thread__args']}) + #return data + return self.commands + def process_hardware(self, path, get_params, post_params, server): """ Returns client's hardware profile diff --git a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py index 926e5a2c..a81d0c0d 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/operations.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/operations.py @@ -50,7 +50,7 @@ else: if os.path.exists('/scripts/oginit'): from .oglive.operations import * # @UnusedWildImport os_type = 'ogLive' - os_version = get_oglive_version() + os_version = get_oglive_version().replace(',', '') else: from .linux.operations import * # @UnusedWildImport os_type = 'Linux' -- cgit v1.2.3-18-g5258 From f0c2f785d8f3f1b652d8306f300543b9c4bc33a9 Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Wed, 4 Jul 2018 12:15:50 +0200 Subject: #750: Adapting route {{GET /command}}} parameters. --- .../opengnsys/modules/server/OpenGnSys/__init__.py | 39 ++++++++++++---------- 1 file changed, 21 insertions(+), 18 deletions(-) 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 index 7a2ac6a3..1832652e 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -85,6 +85,7 @@ def check_locked_partition(sync=False): class OpenGnSysWorker(ServerWorker): name = 'opengnsys' interface = None # Binded interface for OpenGnsys + REST = None # REST object loggedin = False # User session flag locked = {} # Locked partitions commands = [] # Running commands @@ -106,7 +107,6 @@ class OpenGnSysWorker(ServerWorker): """ Sends OGAgent activation notification to OpenGnsys server """ - self.cmd = None # Ensure cfg has required configuration variables or an exception will be thrown url = self.service.config.get('opengnsys', 'remote') if operations.os_type == 'ogLive' and 'oglive' in os.environ: @@ -337,10 +337,11 @@ class OpenGnSysWorker(ServerWorker): (stat, out, err) = operations.exec_command(code) # Removing command from the list for c in self.commands: - if c.has_key('id') and c['id'] == op_id: + if c.getName() == op_id: self.commands.remove(c) # Sending results - self.REST.sendMessage(route, {'id': op_id, 'status': stat, 'output': out, 'error': err}) + self.REST.sendMessage(route, {'client': self.interface.ip, 'trace': op_id, 'status': stat, 'output': out, + 'error': err}) def process_command(self, path, get_params, post_params, server): """ @@ -349,8 +350,8 @@ class OpenGnSysWorker(ServerWorker): :param get_params: ignored :param post_params: object with format: id: operation id. - code: command code - route: callback URL + script: command code + redirect_url: callback REST route :param server: headers data :rtype: object with launching status """ @@ -358,17 +359,17 @@ class OpenGnSysWorker(ServerWorker): self.checkSecret(server) # Processing data try: - code = post_params.get('code') + script = post_params.get('script') op_id = post_params.get('id') - route = post_params.get('route') + route = post_params.get('redirect_url') # Checking if the thread id. exists for c in self.commands: - if c.has_key('id') and c['id'] == op_id: + if c.getName() == op_id: raise Exception('Task id. already exists: {}'.format(op_id)) # Launching a new thread - thr = threading.Thread(name=op_id, target=self.task_command, args=(code, route, op_id)) + thr = threading.Thread(name=op_id, target=self.task_command, args=(script, route, op_id)) thr.start() - self.commands.append({'id': op_id, 'code': code}) + self.commands.append(thr) except Exception as e: logger.error('Got exception {}'.format(e)) return {'error': e} @@ -383,12 +384,14 @@ class OpenGnSysWorker(ServerWorker): :param server: :return: object """ - #data = [] - #for c in self.commands: - # if c.is_alive(): - # data.append({'name': c.getName(), 'code': c.__dict__['_Thread__args']}) - #return data - return self.commands + data = [] + logger.debug('Received execinfo operation') + self.checkSecret(server) + # Returning the arguments of all running threads + for c in self.commands: + if c.is_alive(): + data.append(c.__dict__['_Thread__args']) + return data def process_hardware(self, path, get_params, post_params, server): """ @@ -399,7 +402,7 @@ class OpenGnSysWorker(ServerWorker): :param server: """ data = [] - logger.debug('Recieved hardware operation') + logger.debug('Received hardware operation') self.checkSecret(server) # Processing data try: @@ -419,5 +422,5 @@ class OpenGnSysWorker(ServerWorker): :param server: :return: """ - logger.debug('Recieved software operation with params: {}'.format(post_params)) + logger.debug('Received software operation with params: {}'.format(post_params)) return operations.get_software(post_params.get('disk'), post_params.get('part')) -- cgit v1.2.3-18-g5258 From de6abb6c186b0689bfcc7e520ebef28ed157943e Mon Sep 17 00:00:00 2001 From: "Ramón M. Gómez" Date: Thu, 5 Jul 2018 13:50:17 +0200 Subject: #750: Renaming server REST route {{{GET /done}}} to {{{GET /command_done}}} to log commands executed on clients. --- .../opengnsys/modules/server/OpenGnSys/__init__.py | 12 +++++- admin/WebConsole/rest/ogagent.php | 48 +++++++++++----------- 2 files changed, 36 insertions(+), 24 deletions(-) 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 index 1832652e..abeeaae6 100644 --- a/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py +++ b/admin/Sources/Clients/ogagent/src/opengnsys/modules/server/OpenGnSys/__init__.py @@ -364,7 +364,7 @@ class OpenGnSysWorker(ServerWorker): route = post_params.get('redirect_url') # Checking if the thread id. exists for c in self.commands: - if c.getName() == op_id: + if c.getName() == str(op_id): raise Exception('Task id. already exists: {}'.format(op_id)) # Launching a new thread thr = threading.Thread(name=op_id, target=self.task_command, args=(script, route, op_id)) @@ -393,6 +393,16 @@ class OpenGnSysWorker(ServerWorker): data.append(c.__dict__['_Thread__args']) return data + def process_stopcmd(self, path, get_params, post_params, server): + logger.debug('Received stopcmd operation with params {}:'.format(post_params)) + self.checkSecret(server) + op_id = post_params.get('trace') + for c in self.commands: + if c.is_alive() and c.getName() == str(op_id): + c._Thread__stop() + return {"stopped": op_id} + return {} + def process_hardware(self, path, get_params, post_params, server): """ Returns client's hardware profile diff --git a/admin/WebConsole/rest/ogagent.php b/admin/WebConsole/rest/ogagent.php index 445ee239..b250c6ac 100644 --- a/admin/WebConsole/rest/ogagent.php +++ b/admin/WebConsole/rest/ogagent.php @@ -270,35 +270,37 @@ EOD; ); // Processing command results (TESTING). -$app->post('/ogagent/done', +$app->post('/ogagent/command_done', // 'validateClient', function() use ($app) { global $cmd; try { - // Reading parameters. - $input = json_decode($app->request()->getBody()); - $status = htmlspecialchars($input->status); - $output = htmlspecialchars($input->output); - $error = htmlspecialchars($input->error); - // (... read "id" as GET parameter ...) - // ... - $client = $_SERVER['REMOTE_ADDR']; - // Check sender agent type. - if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT']))) { - throw new Exception("Bad OGAgent: sender=".$client.", agent=".$_SERVER['HTTP_USER_AGENT']); - } - // TODO: truncating outputs. - if ($status == 0) { - writeLog($app->request()->getResourceUri().": Operation OK: client=$client, id=??, output=$output"); - } else { - writeLog($app->request()->getResourceUri().": Operation ERROR: client=$client, id=??, status=$status, error=$error"); - } + // Reading parameters. + $input = json_decode($app->request()->getBody()); + $client = htmlspecialchars($input->client); + $id = htmlspecialchars($input->trace); + $status = htmlspecialchars($input->status); + $output = htmlspecialchars($input->output); + $error = htmlspecialchars($input->error); + $client = $_SERVER['REMOTE_ADDR']; + // Check sender agent type. + if (empty(preg_match('/^python-requests\//', $_SERVER['HTTP_USER_AGENT'])) or $client !== $_SERVER['REMOTE_ADDR']) { + throw new Exception("Bad OGAgent: client=$client, sender=".$_SERVER['REMOTE_ADDR'].", agent=".$_SERVER['HTTP_USER_AGENT']); + } + // TODO: truncating outputs. + if ($status == 0) { + writeLog($app->request()->getResourceUri().": Operation OK: client=$client, id=$id, output=$output"); + } else { + writeLog($app->request()->getResourceUri().": Operation ERROR: client=$client, id=$id, status=$status, error=$error"); + } + $response = ""; + jsonResponse(200, $response); } catch (Exception $e) { - // Communication error. - $response["message"] = $e->getMessage(); - writeLog($app->request()->getResourceUri().": ERROR: ".$response["message"]); - jsonResponse(400, $response); + // Communication error. + $response["message"] = $e->getMessage(); + writeLog($app->request()->getResourceUri().": ERROR: ".$response["message"]); + jsonResponse(400, $response); } } ); -- cgit v1.2.3-18-g5258