#908: Fast-forward branch.
commit
eb2c5f8768
|
@ -68,6 +68,4 @@ ifeq ($(DISTRO),rh)
|
|||
endif
|
||||
|
||||
uninstall:
|
||||
rm -rf $(LIBDIR)
|
||||
# rm -f $(BINDIR)/ogagent
|
||||
rm -rf $(CFGDIR)
|
||||
rm -rf $(LIBDIR) $(CFGDIR) $(BINDIR)/ogagent $(INITDIR)/ogagent
|
||||
|
|
|
@ -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"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="UDSActorService"
|
||||
version="1.6.0.0"
|
||||
name="OGAgentService"
|
||||
version="1.1.2.0"
|
||||
processorArchitecture="x86"
|
||||
/>
|
||||
<description>Description</description>
|
||||
|
|
|
@ -155,11 +155,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):
|
||||
|
@ -171,11 +167,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():
|
||||
|
|
|
@ -30,20 +30,21 @@
|
|||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import threading
|
||||
import os
|
||||
import platform
|
||||
import time
|
||||
import random
|
||||
import shutil
|
||||
import signal
|
||||
import string
|
||||
import subprocess
|
||||
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
|
||||
from six.moves.urllib import parse
|
||||
|
||||
|
||||
# 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.
|
||||
"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
this, path, get_params, post_params, server = args # @UnusedVariable
|
||||
|
@ -73,18 +75,100 @@ def catch_background_error(fnc):
|
|||
fnc(*args, **kwargs)
|
||||
except Exception as e:
|
||||
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, 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):
|
||||
name = 'opengnsys'
|
||||
interface = None # Bound interface for OpenGnsys
|
||||
REST = None # REST object
|
||||
logged_in = False # User session flag
|
||||
locked = {}
|
||||
browser = {} # Browser info
|
||||
commands = [] # Running commands
|
||||
random = None # Random string for secure connections
|
||||
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):
|
||||
"""
|
||||
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))
|
||||
# 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:
|
||||
# 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)
|
||||
# Get network interfaces until they are active or timeout (5 minutes)
|
||||
for t in range(0, 300):
|
||||
|
@ -134,18 +224,61 @@ class OpenGnSysWorker(ServerWorker):
|
|||
logger.debug('Successful connection after {} tries'.format(t))
|
||||
elif t == 100:
|
||||
raise Exception('Initialization error: Cannot connect to remote server')
|
||||
# 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)
|
||||
# Completing OGAgent initialization process
|
||||
os_type = operations.os_type.lower()
|
||||
if os_type == 'oglive':
|
||||
# # Following code may be separated into a different function to launch the browser while getting the disk
|
||||
# # configuration
|
||||
message = """
|
||||
<html>
|
||||
<head></head>
|
||||
<style>
|
||||
#bar { width: 20px; height: 10px; position: relative; background: darkslategrey; }
|
||||
</style>
|
||||
<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):
|
||||
"""
|
||||
|
@ -213,23 +346,14 @@ class OpenGnSysWorker(ServerWorker):
|
|||
:param server:
|
||||
:return: JSON object {"status": "status_code", "loggedin": boolean}
|
||||
"""
|
||||
res = {'status': '', 'loggedin': self.logged_in}
|
||||
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'
|
||||
res = {'loggedin': self.logged_in}
|
||||
try:
|
||||
res['status'] = operations.os_type.lower()
|
||||
except KeyError:
|
||||
res['status'] = ''
|
||||
# Check if OpenGnsys Client is busy
|
||||
if res['status'] == 'oglive' and len(self.commands) > 0:
|
||||
res['status'] = 'busy'
|
||||
return res
|
||||
|
||||
@check_secret
|
||||
|
@ -247,6 +371,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
# Rebooting thread
|
||||
def rebt():
|
||||
operations.reboot()
|
||||
|
||||
threading.Thread(target=rebt).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
|
@ -266,6 +391,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
def pwoff():
|
||||
time.sleep(2)
|
||||
operations.poweroff()
|
||||
|
||||
threading.Thread(target=pwoff).start()
|
||||
return {'op': 'launched'}
|
||||
|
||||
|
@ -275,24 +401,42 @@ class OpenGnSysWorker(ServerWorker):
|
|||
Processes an script execution (script should be encoded in base64)
|
||||
:param path:
|
||||
:param get_params:
|
||||
:param post_params: JSON object {"script": "commands"}
|
||||
:param server: authorization header
|
||||
:return: JSON object {"op": "launched"}
|
||||
:param post_params: object with format:
|
||||
id: operation id.
|
||||
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')
|
||||
# Decoding script (Windows scripts need a subprocess call per line)
|
||||
script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8')
|
||||
if operations.os_type == 'Windows':
|
||||
script = 'import subprocess; {0}'.format(
|
||||
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
|
||||
else:
|
||||
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
||||
# Executing script.
|
||||
if post_params.get('client', 'false') == 'false':
|
||||
thr = ScriptExecutorThread(script)
|
||||
thr.start()
|
||||
else:
|
||||
self.sendClientMessage('script', {'code': script})
|
||||
logger.debug('Processing script operation with params: {}'.format(post_params))
|
||||
# Processing data
|
||||
try:
|
||||
# Decoding script (Windows scripts need a subprocess call per line)
|
||||
script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8')
|
||||
if operations.os_type == 'Windows':
|
||||
script = 'import subprocess; {0}'.format(
|
||||
';'.join(['subprocess.check_output({0},shell=True)'.format(repr(c)) for c in script.split('\n')]))
|
||||
else:
|
||||
script = 'import subprocess; subprocess.check_output("""{0}""",shell=True)'.format(script)
|
||||
op_id = post_params.get('id')
|
||||
route = post_params.get('redirect_uri')
|
||||
send_config = (post_params.get('send_config', 'false') == 'true')
|
||||
# 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'}
|
||||
|
||||
@check_secret
|
||||
|
@ -301,7 +445,7 @@ class OpenGnSysWorker(ServerWorker):
|
|||
Closes user session
|
||||
"""
|
||||
logger.debug('Received logoff operation')
|
||||
# Sending log off message to OGAgent client
|
||||
# Send log off message to OGAgent client
|
||||
self.sendClientMessage('logoff', {})
|
||||
return {'op': 'sent to client'}
|
||||
|
||||
|
@ -311,9 +455,125 @@ class OpenGnSysWorker(ServerWorker):
|
|||
Shows a message popup on the user's session
|
||||
"""
|
||||
logger.debug('Received message operation')
|
||||
# Sending popup message to OGAgent client
|
||||
# Send popup message to OGAgent client
|
||||
self.sendClientMessage('popup', post_params)
|
||||
return {'op': 'launched'}
|
||||
|
||||
def process_client_popup(self, 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.
|
||||
|
||||
"""
|
||||
@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
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Importing platform operations and getting operating system data.
|
||||
if sys.platform == 'win32':
|
||||
|
@ -45,6 +46,11 @@ else:
|
|||
os_type = 'MacOS'
|
||||
os_version = getMacosVersion().replace(',', '')
|
||||
else:
|
||||
from .linux.operations import * # @UnusedWildImport
|
||||
os_type = 'Linux'
|
||||
os_version = getLinuxVersion()
|
||||
if os.path.exists('/scripts/oginit'):
|
||||
from .oglive.operations import * # @UnusedWildImport
|
||||
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',
|
||||
author='Adolfo Gomez',
|
||||
author_email='agomez@virtualcable.es',
|
||||
zipfile='OGAgent.zip',
|
||||
zipfile='OGAgent.zip', requires=['six']
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue