#908: Fast-forward branch.
commit
eb2c5f8768
|
@ -68,6 +68,4 @@ ifeq ($(DISTRO),rh)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -rf $(LIBDIR)
|
rm -rf $(LIBDIR) $(CFGDIR) $(BINDIR)/ogagent $(INITDIR)/ogagent
|
||||||
# rm -f $(BINDIR)/ogagent
|
|
||||||
rm -rf $(CFGDIR)
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/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
|
||||||
|
ln -fs $(LIBDIR)/cfg/ogagent.cfg $(CFGDIR)
|
||||||
|
ln -fs $(LIBDIR)/cfg/ogclient.cfg $(CFGDIR)
|
||||||
|
|
||||||
|
cp scripts/ogagent $(BINDIR)
|
||||||
|
chmod 755 $(BINDIR)/ogagent
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -rf $(LIBDIR)
|
||||||
|
rm -f $(BINDIR)/ogagent
|
||||||
|
rm -rf $(CFGDIR)
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd $(dirname "$0")
|
||||||
|
|
||||||
|
# Build package
|
||||||
|
dpkg-buildpackage -b -d
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
ogagent-oglive (1.1.1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
-- Ramón M. Gómez <ramongomez@us.es> Mon, 18 Jun 2018 13:00:00 +0200
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
9
|
|
@ -0,0 +1,15 @@
|
||||||
|
Source: ogagent-oglive
|
||||||
|
Section: admin
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Ramón M. Gómez <ramongomez@us.es>
|
||||||
|
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.
|
|
@ -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'.
|
|
@ -0,0 +1 @@
|
||||||
|
readme.txt
|
|
@ -0,0 +1,2 @@
|
||||||
|
/usr/share/OGAgent/cfg/ogagent.cfg /etc/ogagent/ogagent.cfg
|
||||||
|
/usr/share/OGAgent/cfg/ogclient.cfg /etc/ogagent/ogclient.cfg
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
misc:Depends=
|
||||||
|
misc:Pre-Depends=
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
3.0 (native)
|
|
@ -0,0 +1,3 @@
|
||||||
|
OGAgent is the agent intended for OpengGnsys interaction.
|
||||||
|
|
||||||
|
Please, visit https://opengnsys.es for more information
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
FOLDER=/usr/share/OGAgent
|
||||||
|
|
||||||
|
cd $FOLDER
|
||||||
|
python -m opengnsys.linux.OGAgentService $@
|
|
@ -1,9 +1,10 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
<assemblyIdentity
|
<assemblyIdentity
|
||||||
type="win32"
|
type="win32"
|
||||||
name="UDSActorService"
|
name="OGAgentService"
|
||||||
version="1.6.0.0"
|
version="1.1.2.0"
|
||||||
processorArchitecture="x86"
|
processorArchitecture="x86"
|
||||||
/>
|
/>
|
||||||
<description>Description</description>
|
<description>Description</description>
|
||||||
|
|
|
@ -155,11 +155,7 @@ def reboot(flags=0):
|
||||||
import threading
|
import threading
|
||||||
threading._DummyThread._Thread__stop = lambda x: 42
|
threading._DummyThread._Thread__stop = lambda x: 42
|
||||||
|
|
||||||
# Check for OpenGnsys Client or GNU/Linux distribution.
|
subprocess.call(['/sbin/reboot'])
|
||||||
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):
|
def poweroff(flags=0):
|
||||||
|
@ -171,11 +167,7 @@ def poweroff(flags=0):
|
||||||
import threading
|
import threading
|
||||||
threading._DummyThread._Thread__stop = lambda x: 42
|
threading._DummyThread._Thread__stop = lambda x: 42
|
||||||
|
|
||||||
# Check for OpenGnsys Client or GNU/Linux distribution.
|
subprocess.call(['/sbin/poweroff'])
|
||||||
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():
|
def logoff():
|
||||||
|
|
|
@ -30,20 +30,21 @@
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import threading
|
|
||||||
import os
|
import os
|
||||||
import platform
|
|
||||||
import time
|
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
|
import signal
|
||||||
import string
|
import string
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
from opengnsys.workers import ServerWorker
|
from opengnsys import REST
|
||||||
from opengnsys import REST, RESTError
|
|
||||||
from opengnsys import operations
|
from opengnsys import operations
|
||||||
from opengnsys.log import logger
|
from opengnsys.log import logger
|
||||||
from opengnsys.scriptThread import ScriptExecutorThread
|
from opengnsys.workers import ServerWorker
|
||||||
|
from six.moves.urllib import parse
|
||||||
|
|
||||||
|
|
||||||
# Check authorization header decorator
|
# Check authorization header decorator
|
||||||
|
@ -51,6 +52,7 @@ def check_secret(fnc):
|
||||||
"""
|
"""
|
||||||
Decorator to check for received secret key and raise exception if it isn't valid.
|
Decorator to check for received secret key and raise exception if it isn't valid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
this, path, get_params, post_params, server = args # @UnusedVariable
|
this, path, get_params, post_params, server = args # @UnusedVariable
|
||||||
|
@ -73,18 +75,100 @@ def catch_background_error(fnc):
|
||||||
fnc(*args, **kwargs)
|
fnc(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(e)})
|
this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(e)})
|
||||||
|
|
||||||
return wrapper
|
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, server = 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):
|
class OpenGnSysWorker(ServerWorker):
|
||||||
name = 'opengnsys'
|
name = 'opengnsys'
|
||||||
interface = None # Bound interface for OpenGnsys
|
interface = None # Bound interface for OpenGnsys
|
||||||
REST = None # REST object
|
REST = None # REST object
|
||||||
logged_in = False # User session flag
|
logged_in = False # User session flag
|
||||||
locked = {}
|
browser = {} # Browser info
|
||||||
|
commands = [] # Running commands
|
||||||
random = None # Random string for secure connections
|
random = None # Random string for secure connections
|
||||||
length = 32 # Random string length
|
length = 32 # Random string length
|
||||||
|
|
||||||
|
def _launch_browser(self, url):
|
||||||
|
"""
|
||||||
|
Launches the Browser with specified URL
|
||||||
|
:param url: URL to show
|
||||||
|
"""
|
||||||
|
logger.debug('Launching browser with URL: {}'.format(url))
|
||||||
|
# Trying to kill an old browser
|
||||||
|
try:
|
||||||
|
os.kill(self.browser['process'].pid, signal.SIGKILL)
|
||||||
|
except OSError:
|
||||||
|
logger.warn('Cannot kill the old browser process')
|
||||||
|
except KeyError:
|
||||||
|
# There is no previous browser
|
||||||
|
pass
|
||||||
|
self.browser['url'] = url
|
||||||
|
self.browser['process'] = subprocess.Popen(['browser', '-qws', url])
|
||||||
|
|
||||||
|
def _task_command(self, route, code, op_id, send_config=False):
|
||||||
|
"""
|
||||||
|
Task to execute a command and return results to a server URI
|
||||||
|
:param route: server callback REST route to return results
|
||||||
|
:param code: code to execute
|
||||||
|
:param op_id: operation id.
|
||||||
|
:param send_config: indicate if client will send configuration data after command execution
|
||||||
|
"""
|
||||||
|
menu_url = ''
|
||||||
|
# Show execution tacking log, if OGAgent runs on ogLive
|
||||||
|
os_type = operations.os_type.lower()
|
||||||
|
if os_type == 'oglive':
|
||||||
|
menu_url = self.browser['url']
|
||||||
|
self._launch_browser('http://localhost/cgi-bin/httpd-log.sh')
|
||||||
|
# Execute the code
|
||||||
|
(stat, out, err) = operations.exec_command(code)
|
||||||
|
# Remove command from the list
|
||||||
|
for c in self.commands:
|
||||||
|
if c.getName() == op_id:
|
||||||
|
self.commands.remove(c)
|
||||||
|
# Remove the REST API prefix, if needed
|
||||||
|
if route.startswith(self.REST.endpoint):
|
||||||
|
route = route[len(self.REST.endpoint):]
|
||||||
|
# Send back exit status and outputs (base64-encoded)
|
||||||
|
self.REST.sendMessage(route, {'mac': self.interface.mac, 'ip': self.interface.ip, 'trace': op_id,
|
||||||
|
'status': stat, 'output': out.encode('base64'), 'error': err.encode('base64')})
|
||||||
|
# Show latest menu, if OGAgent runs on ogLive
|
||||||
|
if os_type == 'oglive':
|
||||||
|
# Send configuration data, if needed
|
||||||
|
if send_config:
|
||||||
|
self.REST.sendMessage('ogagent/config', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||||
|
'config': operations.get_configuration()})
|
||||||
|
self._launch_browser(menu_url)
|
||||||
|
|
||||||
def onActivation(self):
|
def onActivation(self):
|
||||||
"""
|
"""
|
||||||
Sends OGAgent activation notification to OpenGnsys server
|
Sends OGAgent activation notification to OpenGnsys server
|
||||||
|
@ -94,6 +178,12 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length))
|
||||||
# Ensure cfg has required configuration variables or an exception will be thrown
|
# Ensure cfg has required configuration variables or an exception will be thrown
|
||||||
url = 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 it's running on ogLive client
|
||||||
|
logger.debug('Activating on ogLive client, new server is {}'.format(os.environ['oglive']))
|
||||||
|
url = parse.urlsplit(url)._replace(netloc=os.environ['oglive']).geturl()
|
||||||
|
if not url.endswith(os.path.sep):
|
||||||
|
url += os.path.sep
|
||||||
self.REST = REST(url)
|
self.REST = REST(url)
|
||||||
# Get network interfaces until they are active or timeout (5 minutes)
|
# Get network interfaces until they are active or timeout (5 minutes)
|
||||||
for t in range(0, 300):
|
for t in range(0, 300):
|
||||||
|
@ -134,18 +224,61 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
logger.debug('Successful connection after {} tries'.format(t))
|
logger.debug('Successful connection after {} tries'.format(t))
|
||||||
elif t == 100:
|
elif t == 100:
|
||||||
raise Exception('Initialization error: Cannot connect to remote server')
|
raise Exception('Initialization error: Cannot connect to remote server')
|
||||||
# Delete marking files
|
# Completing OGAgent initialization process
|
||||||
for f in ['ogboot.me', 'ogboot.firstboot', 'ogboot.secondboot']:
|
os_type = operations.os_type.lower()
|
||||||
try:
|
if os_type == 'oglive':
|
||||||
os.remove(os.sep + f)
|
# # Following code may be separated into a different function to launch the browser while getting the disk
|
||||||
except OSError:
|
# # configuration
|
||||||
pass
|
message = """
|
||||||
# Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists
|
<html>
|
||||||
# (used in "exam mode" from the University of Seville)
|
<head></head>
|
||||||
hosts_file = os.path.join(operations.get_etc_path(), 'hosts')
|
<style>
|
||||||
new_hosts_file = hosts_file + '.' + self.interface.ip.split('.')[0]
|
#bar { width: 20px; height: 10px; position: relative; background: darkslategrey; }
|
||||||
if os.path.isfile(new_hosts_file):
|
</style>
|
||||||
shutil.copyfile(new_hosts_file, hosts_file)
|
<body>
|
||||||
|
<h1 style="margin: 5em 0 0 5em; font-size: 250%; color: darkslategrey;">
|
||||||
|
<span id="opengnsys"><span style="font-weight: lighter;">Open</span>Gnsys 3</div>
|
||||||
|
<div id="bar"></span>
|
||||||
|
</h1>
|
||||||
|
<script>
|
||||||
|
var elem = document.getElementById("bar");
|
||||||
|
var max = document.getElementById("opengnsys").offsetWidth;
|
||||||
|
var pos = 0;
|
||||||
|
var inc = true;
|
||||||
|
var id = setInterval(frame, 5);
|
||||||
|
function frame() {
|
||||||
|
if (inc) {
|
||||||
|
if (pos == max - 20) { inc = false; } else { pos++; }
|
||||||
|
} else {
|
||||||
|
if (pos == 0) { inc = true; } else { pos--; }
|
||||||
|
}
|
||||||
|
elem.style.left = pos + 'px';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
f = open('/tmp/init.html', 'w')
|
||||||
|
f.write(message)
|
||||||
|
f.close()
|
||||||
|
# Launching the Browser
|
||||||
|
self._launch_browser('/tmp/init.html')
|
||||||
|
config = operations.get_configuration()
|
||||||
|
self.REST.sendMessage('ogagent/config', {'mac': self.interface.mac, 'ip': self.interface.ip,
|
||||||
|
'config': config})
|
||||||
|
else:
|
||||||
|
# Delete marking files
|
||||||
|
for f in ['ogboot.me', 'ogboot.firstboot', 'ogboot.secondboot']:
|
||||||
|
try:
|
||||||
|
os.remove(os.sep + f)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
# Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists
|
||||||
|
# (used in "exam mode" from the University of Seville)
|
||||||
|
hosts_file = os.path.join(operations.get_etc_path(), 'hosts')
|
||||||
|
new_hosts_file = hosts_file + '.' + self.interface.ip.split('.')[0]
|
||||||
|
if os.path.isfile(new_hosts_file):
|
||||||
|
shutil.copyfile(new_hosts_file, hosts_file)
|
||||||
|
|
||||||
def onDeactivation(self):
|
def onDeactivation(self):
|
||||||
"""
|
"""
|
||||||
|
@ -213,23 +346,14 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
:param server:
|
:param server:
|
||||||
:return: JSON object {"status": "status_code", "loggedin": boolean}
|
:return: JSON object {"status": "status_code", "loggedin": boolean}
|
||||||
"""
|
"""
|
||||||
res = {'status': '', 'loggedin': self.logged_in}
|
res = {'loggedin': self.logged_in}
|
||||||
if platform.system() == 'Linux': # GNU/Linux
|
try:
|
||||||
# Check if it's OpenGnsys Client.
|
res['status'] = operations.os_type.lower()
|
||||||
if os.path.exists('/scripts/oginit'):
|
except KeyError:
|
||||||
# Check if OpenGnsys Client is busy.
|
res['status'] = ''
|
||||||
if self.locked:
|
# Check if OpenGnsys Client is busy
|
||||||
res['status'] = 'BSY'
|
if res['status'] == 'oglive' and len(self.commands) > 0:
|
||||||
else:
|
res['status'] = 'busy'
|
||||||
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
|
return res
|
||||||
|
|
||||||
@check_secret
|
@check_secret
|
||||||
|
@ -247,6 +371,7 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
# Rebooting thread
|
# Rebooting thread
|
||||||
def rebt():
|
def rebt():
|
||||||
operations.reboot()
|
operations.reboot()
|
||||||
|
|
||||||
threading.Thread(target=rebt).start()
|
threading.Thread(target=rebt).start()
|
||||||
return {'op': 'launched'}
|
return {'op': 'launched'}
|
||||||
|
|
||||||
|
@ -266,6 +391,7 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
def pwoff():
|
def pwoff():
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
operations.poweroff()
|
operations.poweroff()
|
||||||
|
|
||||||
threading.Thread(target=pwoff).start()
|
threading.Thread(target=pwoff).start()
|
||||||
return {'op': 'launched'}
|
return {'op': 'launched'}
|
||||||
|
|
||||||
|
@ -275,24 +401,42 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
Processes an script execution (script should be encoded in base64)
|
Processes an script execution (script should be encoded in base64)
|
||||||
:param path:
|
:param path:
|
||||||
:param get_params:
|
:param get_params:
|
||||||
:param post_params: JSON object {"script": "commands"}
|
:param post_params: object with format:
|
||||||
:param server: authorization header
|
id: operation id.
|
||||||
:return: JSON object {"op": "launched"}
|
script: command code
|
||||||
|
redirect_url: callback REST route
|
||||||
|
send_config: flag to send client's configuration after command execution (optional)
|
||||||
|
:param server: headers data
|
||||||
|
:rtype: JSON object with launching status
|
||||||
"""
|
"""
|
||||||
logger.debug('Processing script request')
|
logger.debug('Processing script operation with params: {}'.format(post_params))
|
||||||
# Decoding script (Windows scripts need a subprocess call per line)
|
# Processing data
|
||||||
script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8')
|
try:
|
||||||
if operations.os_type == 'Windows':
|
# Decoding script (Windows scripts need a subprocess call per line)
|
||||||
script = 'import subprocess; {0}'.format(
|
script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8')
|
||||||
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
|
if operations.os_type == 'Windows':
|
||||||
else:
|
script = 'import subprocess; {0}'.format(
|
||||||
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
|
||||||
# Executing script.
|
else:
|
||||||
if post_params.get('client', 'false') == 'false':
|
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
||||||
thr = ScriptExecutorThread(script)
|
op_id = post_params.get('id')
|
||||||
thr.start()
|
route = post_params.get('redirect_uri')
|
||||||
else:
|
send_config = (post_params.get('send_config', 'false') == 'true')
|
||||||
self.sendClientMessage('script', {'code': script})
|
# Checking if the thread id. exists
|
||||||
|
for c in self.commands:
|
||||||
|
if c.getName() == str(op_id):
|
||||||
|
raise Exception('Task id. already exists: {}'.format(op_id))
|
||||||
|
if post_params.get('client', 'false') == 'false':
|
||||||
|
# Launching a new thread
|
||||||
|
thr = threading.Thread(name=op_id, target=self._task_command, args=(route, script, op_id, send_config))
|
||||||
|
thr.start()
|
||||||
|
self.commands.append(thr)
|
||||||
|
else:
|
||||||
|
# Executing as normal user
|
||||||
|
self.sendClientMessage('script', {'code': script})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Got exception {}'.format(e))
|
||||||
|
return {'error': e}
|
||||||
return {'op': 'launched'}
|
return {'op': 'launched'}
|
||||||
|
|
||||||
@check_secret
|
@check_secret
|
||||||
|
@ -301,7 +445,7 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
Closes user session
|
Closes user session
|
||||||
"""
|
"""
|
||||||
logger.debug('Received logoff operation')
|
logger.debug('Received logoff operation')
|
||||||
# Sending log off message to OGAgent client
|
# Send log off message to OGAgent client
|
||||||
self.sendClientMessage('logoff', {})
|
self.sendClientMessage('logoff', {})
|
||||||
return {'op': 'sent to client'}
|
return {'op': 'sent to client'}
|
||||||
|
|
||||||
|
@ -311,9 +455,125 @@ class OpenGnSysWorker(ServerWorker):
|
||||||
Shows a message popup on the user's session
|
Shows a message popup on the user's session
|
||||||
"""
|
"""
|
||||||
logger.debug('Received message operation')
|
logger.debug('Received message operation')
|
||||||
# Sending popup message to OGAgent client
|
# Send popup message to OGAgent client
|
||||||
self.sendClientMessage('popup', post_params)
|
self.sendClientMessage('popup', post_params)
|
||||||
return {'op': 'launched'}
|
return {'op': 'launched'}
|
||||||
|
|
||||||
def process_client_popup(self, params):
|
def process_client_popup(self, params):
|
||||||
self.REST.sendMessage('popup_done', params)
|
self.REST.sendMessage('popup_done', params)
|
||||||
|
|
||||||
|
@check_secret
|
||||||
|
def process_config(self, path, get_params, post_params, server):
|
||||||
|
"""
|
||||||
|
Returns client configuration
|
||||||
|
:param path:
|
||||||
|
:param get_params:
|
||||||
|
:param post_params:
|
||||||
|
:param server:
|
||||||
|
:return: object
|
||||||
|
"""
|
||||||
|
serial_no = '' # Serial number
|
||||||
|
storage = [] # Storage configuration
|
||||||
|
warnings = 0 # Number of warnings
|
||||||
|
logger.debug('Received getconfig operation')
|
||||||
|
# Processing data
|
||||||
|
for row in operations.get_configuration().split(';'):
|
||||||
|
cols = row.split(':')
|
||||||
|
if len(cols) == 1:
|
||||||
|
if cols[0] != '':
|
||||||
|
# Serial number
|
||||||
|
serial_no = cols[0]
|
||||||
|
else:
|
||||||
|
# Skip blank rows
|
||||||
|
pass
|
||||||
|
elif len(cols) == 7:
|
||||||
|
disk, part_no, part_type, fs, op_sys, size, usage = cols
|
||||||
|
try:
|
||||||
|
if int(part_no) == 0:
|
||||||
|
# Disk information
|
||||||
|
storage.append({'disk': int(disk), 'parttable': int(part_type), 'size': int(size)})
|
||||||
|
else:
|
||||||
|
# Partition information
|
||||||
|
storage.append({'disk': int(disk), 'partition': int(part_no), 'parttype': part_type,
|
||||||
|
'filesystem': fs, 'operatingsystem': op_sys, '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 {'serial': serial_no, 'storage': storage, 'warnings': warnings}
|
||||||
|
|
||||||
|
@check_secret
|
||||||
|
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 = []
|
||||||
|
logger.debug('Received execinfo operation')
|
||||||
|
# Returning the arguments of all running threads
|
||||||
|
for c in self.commands:
|
||||||
|
if c.is_alive():
|
||||||
|
data.append(c.__dict__['_Thread__args'])
|
||||||
|
return data
|
||||||
|
|
||||||
|
@check_secret
|
||||||
|
def process_stopcmd(self, path, get_params, post_params, server):
|
||||||
|
"""
|
||||||
|
Stops a running process identified by its trace id.
|
||||||
|
:param path:
|
||||||
|
:param get_params:
|
||||||
|
:param post_params: JSON object {"trace": trace_id}
|
||||||
|
:param server: authorization header
|
||||||
|
:return: JSON object: {"stopped": trace_id}
|
||||||
|
"""
|
||||||
|
logger.debug('Received stopcmd operation with params {}:'.format(post_params))
|
||||||
|
# Find operation id. and stop the thread
|
||||||
|
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 {}
|
||||||
|
|
||||||
|
@check_secret
|
||||||
|
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:
|
||||||
|
:return: array of component data objects
|
||||||
|
"""
|
||||||
|
data = []
|
||||||
|
logger.debug('Received hardware operation')
|
||||||
|
# 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
|
||||||
|
|
||||||
|
@check_secret
|
||||||
|
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('Received software operation with params: {}'.format(post_params))
|
||||||
|
return operations.get_software(post_params.get('disk'), post_params.get('part'))
|
||||||
|
|
|
@ -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
|
|
@ -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().
|
||||||
|
"""
|
|
@ -0,0 +1,258 @@
|
||||||
|
# -*- 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 os
|
||||||
|
import fcntl
|
||||||
|
import subprocess
|
||||||
|
import struct
|
||||||
|
import array
|
||||||
|
import six
|
||||||
|
import chardet
|
||||||
|
from opengnsys import utils
|
||||||
|
from opengnsys.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
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(ogcmd):
|
||||||
|
"""
|
||||||
|
Loads OpenGnsys environment variables, executes the command and returns the result
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
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 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')
|
||||||
|
|
||||||
|
|
||||||
|
def poweroff():
|
||||||
|
"""
|
||||||
|
Simple power off 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')
|
||||||
|
|
||||||
|
|
||||||
|
def get_etc_path():
|
||||||
|
"""
|
||||||
|
Returns etc directory path.
|
||||||
|
"""
|
||||||
|
return os.sep + 'etc'
|
||||||
|
|
||||||
|
|
||||||
|
def get_configuration():
|
||||||
|
"""
|
||||||
|
Returns client's configuration
|
||||||
|
Warning: this operation may take some time
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
_exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration')
|
||||||
|
# Returns content of configuration file
|
||||||
|
cfgdata = open('/tmp/getconfig', 'r').read().strip()
|
||||||
|
except IOError:
|
||||||
|
cfgdata = ''
|
||||||
|
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()
|
||||||
|
try:
|
||||||
|
if out is not None:
|
||||||
|
encoding = chardet.detect(out)["encoding"]
|
||||||
|
if encoding is not None:
|
||||||
|
out = out.decode(encoding).encode("utf8")
|
||||||
|
if err is not None:
|
||||||
|
encoding = chardet.detect(err)["encoding"]
|
||||||
|
if encoding is not None:
|
||||||
|
err = err.decode(encoding).encode("utf8")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug("ERROR EXEC COMMAND: {}".format(str(e)))
|
||||||
|
|
||||||
|
stat = proc.returncode
|
||||||
|
return stat, out, err
|
||||||
|
|
||||||
|
|
||||||
|
def get_hardware():
|
||||||
|
"""
|
||||||
|
Returns client's hardware list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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
|
|
@ -27,12 +27,13 @@
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# 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
|
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
# Importing platform operations and getting operating system data.
|
# Importing platform operations and getting operating system data.
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
|
@ -45,6 +46,11 @@ else:
|
||||||
os_type = 'MacOS'
|
os_type = 'MacOS'
|
||||||
os_version = getMacosVersion().replace(',', '')
|
os_version = getMacosVersion().replace(',', '')
|
||||||
else:
|
else:
|
||||||
from .linux.operations import * # @UnusedWildImport
|
if os.path.exists('/scripts/oginit'):
|
||||||
os_type = 'Linux'
|
from .oglive.operations import * # @UnusedWildImport
|
||||||
os_version = getLinuxVersion()
|
os_type = 'ogLive'
|
||||||
|
os_version = get_oglive_version().replace(',', '')
|
||||||
|
else:
|
||||||
|
from .linux.operations import * # @UnusedWildImport
|
||||||
|
os_type = 'Linux'
|
||||||
|
os_version = getLinuxVersion()
|
||||||
|
|
|
@ -139,5 +139,5 @@ setup(
|
||||||
description='OpenGnsys Agent',
|
description='OpenGnsys Agent',
|
||||||
author='Adolfo Gomez',
|
author='Adolfo Gomez',
|
||||||
author_email='agomez@virtualcable.es',
|
author_email='agomez@virtualcable.es',
|
||||||
zipfile='OGAgent.zip',
|
zipfile='OGAgent.zip', requires=['six']
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue