#750: OGAgent activation and deactivation now compatible with new web console.

oglive^2^2^2
Ramón M. Gómez 2019-04-09 10:52:05 +02:00
commit 1ff2475ee3
4 changed files with 67 additions and 34 deletions

View File

@ -8,6 +8,8 @@ path=test_modules/server
# Remote OpenGnsys Service # Remote OpenGnsys Service
remote=https://192.168.2.10/opengnsys/rest remote=https://192.168.2.10/opengnsys/rest
# Alternate OpenGnsys Service (comment out to enable this option)
#altremote=https://10.0.2.2/opengnsys/rest
# Log Level, if ommited, will be set to INFO # Log Level, if ommited, will be set to INFO
log=DEBUG log=DEBUG

View File

@ -26,9 +26,9 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # 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. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
''' """
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' """
# pylint: disable-msg=E1101,W0703 # pylint: disable-msg=E1101,W0703
@ -43,7 +43,8 @@ from .log import logger
from .utils import exceptionToMessage from .utils import exceptionToMessage
VERIFY_CERT = False VERIFY_CERT = False # Do not check server certificate
TIMEOUT = 5 # Connection timout, in seconds
class RESTError(Exception): class RESTError(Exception):
@ -66,30 +67,34 @@ try:
except Exception: except Exception:
pass # In fact, isn't too important, but wil log warns to logging file pass # In fact, isn't too important, but wil log warns to logging file
class REST(object): class REST(object):
''' """
Simple interface to remote REST apis. Simple interface to remote REST apis.
The constructor expects the "base url" as parameter, that is, the url that will be common on all REST requests The constructor expects the "base url" as parameter, that is, the url that will be common on all REST requests
Remember that this is a helper for "easy of use". You can provide your owns using requests lib for example. Remember that this is a helper for "easy of use". You can provide your owns using requests lib for example.
Examples: Examples:
v = REST('https://example.com/rest/v1/') (Can omit trailing / if desired) v = REST('https://example.com/rest/v1/') (Can omit trailing / if desired)
v.sendMessage('hello?param1=1&param2=2') v.sendMessage('hello?param1=1&param2=2')
This will generate a GET message to https://example.com/rest/v1/hello?param1=1&param2=2, and return the deserialized JSON result or an exception This will generate a GET message to https://example.com/rest/v1/hello?param1=1&param2=2, and return the
deserialized JSON result or an exception
v.sendMessage('hello?param1=1&param2=2', {'name': 'mario' }) v.sendMessage('hello?param1=1&param2=2', {'name': 'mario' })
This will generate a POST message to https://example.com/rest/v1/hello?param1=1&param2=2, with json encoded body {'name': 'mario' }, and also returns This will generate a POST message to https://example.com/rest/v1/hello?param1=1&param2=2, with json encoded
body {'name': 'mario' }, and also returns
the deserialized JSON result or raises an exception in case of error the deserialized JSON result or raises an exception in case of error
''' """
def __init__(self, url): def __init__(self, url):
''' """
Initializes the REST helper Initializes the REST helper
url is the full url of the REST API Base, as for example "https://example.com/rest/v1". url is the full url of the REST API Base, as for example "https://example.com/rest/v1".
@param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired. @param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired.
''' """
self.endpoint = url self.endpoint = url
if self.endpoint[-1] != '/': if self.endpoint[-1] != '/':
self.endpoint += '/' self.endpoint += '/'
# Some OSs ships very old python requests lib implementations, workaround them... # Some OSs ships very old python requests lib implementations, workaround them...
try: try:
self.newerRequestLib = requests.__version__.split('.')[0] >= '1' self.newerRequestLib = requests.__version__.split('.')[0] >= '1'
@ -105,37 +110,39 @@ class REST(object):
pass pass
def _getUrl(self, method): def _getUrl(self, method):
''' """
Internal method Internal method
Composes the URL based on "method" Composes the URL based on "method"
@param method: Method to append to base url for composition @param method: Method to append to base url for composition
''' """
url = self.endpoint + method url = self.endpoint + method
return url return url
def _request(self, url, data=None): def _request(self, url, data=None):
''' """
Launches the request Launches the request
@param url: The url to obtain @param url: The url to obtain
@param data: if None, the request will be sent as a GET request. If != None, the request will be sent as a POST, with data serialized as JSON in the body. @param data: if None, the request will be sent as a GET request. If != None, the request will be sent as a POST,
''' with data serialized as JSON in the body.
"""
try: try:
if data is None: if data is None:
logger.debug('Requesting using GET (no data provided) {}'.format(url)) logger.debug('Requesting using GET (no data provided) {}'.format(url))
# Old requests version does not support verify, but they do not checks ssl certificate by default # Old requests version does not support verify, but it do not checks ssl certificate by default
if self.newerRequestLib: if self.newerRequestLib:
r = requests.get(url, verify=VERIFY_CERT) r = requests.get(url, verify=VERIFY_CERT, timeout=TIMEOUT)
else: else:
r = requests.get(url) r = requests.get(url)
else: # POST else: # POST
logger.debug('Requesting using POST {}, data: {}'.format(url, data)) logger.debug('Requesting using POST {}, data: {}'.format(url, data))
if self.newerRequestLib: if self.newerRequestLib:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=VERIFY_CERT) r = requests.post(url, data=data, headers={'content-type': 'application/json'},
verify=VERIFY_CERT, timeout=TIMEOUT)
else: else:
r = requests.post(url, data=data, headers={'content-type': 'application/json'}) r = requests.post(url, data=data, headers={'content-type': 'application/json'})
r = json.loads(r.content) # Using instead of r.json() to make compatible with oooold rquests lib versions r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
raise ConnectionError(e) raise ConnectionError(e)
except Exception as e: except Exception as e:
@ -144,17 +151,17 @@ class REST(object):
return r return r
def sendMessage(self, msg, data=None, processData=True): def sendMessage(self, msg, data=None, processData=True):
''' """
Sends a message to remote REST server Sends a message to remote REST server
@param data: if None or omitted, message will be a GET, else it will send a POST @param data: if None or omitted, message will be a GET, else it will send a POST
@param processData: if True, data will be serialized to json before sending, else, data will be sent as "raw" @param processData: if True, data will be serialized to json before sending, else, data will be sent as "raw"
''' """
logger.debug('Invoking post message {} with data {}'.format(msg, data)) logger.debug('Invoking post message {} with data {}'.format(msg, data))
if processData and data is not None: if processData and data is not None:
data = json.dumps(data) data = json.dumps(data)
url = self._getUrl(msg) url = self._getUrl(msg)
logger.debug('Requesting {}'.format(url)) logger.debug('Requesting {}'.format(url))
return self._request(url, data) return self._request(url, data)

View File

@ -33,7 +33,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
from .log import logger
config = None config = None

View File

@ -46,6 +46,7 @@ from opengnsys.workers import ServerWorker
from six.moves.urllib import parse from six.moves.urllib import parse
# Error handler decorator. # Error handler decorator.
def catch_background_error(fnc): def catch_background_error(fnc):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
@ -85,7 +86,7 @@ def check_locked_partition(sync=False):
class OpenGnSysWorker(ServerWorker): class OpenGnSysWorker(ServerWorker):
name = 'opengnsys' name = 'opengnsys'
interface = None # Binded interface for OpenGnsys interface = None # Bound interface for OpenGnsys
REST = None # REST object REST = None # REST object
loggedin = False # User session flag loggedin = False # User session flag
locked = {} # Locked partitions locked = {} # Locked partitions
@ -138,7 +139,7 @@ class OpenGnSysWorker(ServerWorker):
except OSError: except OSError:
pass pass
# Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists # Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists
# (used in "exam mode" of the University of Seville) # (used in "exam mode" from the University of Seville)
hostsFile = os.path.join(operations.get_etc_path(), 'hosts') hostsFile = os.path.join(operations.get_etc_path(), 'hosts')
newHostsFile = hostsFile + '.' + self.interface.ip.split('.')[0] newHostsFile = hostsFile + '.' + self.interface.ip.split('.')[0]
if os.path.isfile(newHostsFile): if os.path.isfile(newHostsFile):
@ -146,17 +147,41 @@ class OpenGnSysWorker(ServerWorker):
# Generate random secret to send on activation # Generate random secret to send on activation
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))
# Send initialization message # Send initialization message
self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip, try:
'secret': self.random, 'ostype': operations.os_type, try:
'osversion': operations.os_version}) self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip,
'secret': self.random, 'ostype': operations.os_type,
'osversion': operations.os_version})
# New web compatibility.
#self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip,
# 'secret': self.random, 'ostype': operations.os_type,
# 'osversion': operations.os_version,
# 'status': operations.os_type.lower()})
except:
# Trying to initialize on alternative server, if defined
# (used in "exam mode" from the University of Seville)
self.REST = REST(self.service.config.get('opengnsys', 'altremote'))
self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip,
'secret': self.random, 'ostype': operations.osType,
'osversion': operations.osVersion, 'alt_url': True})
# New web compatibility.
#self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip,
# 'secret': self.random, 'ostype': operations.os_type,
# 'osversion': operations.os_version,
# 'status': operations.os_type.lower()})
except:
logger.error('Initialization error')
def onDeactivation(self): def onDeactivation(self):
""" """
Sends OGAgent stopping notification to OpenGnsys server Sends OGAgent stopping notification to OpenGnsys server
""" """
logger.debug('onDeactivation') logger.debug('onDeactivation')
self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip, #self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip,
'ostype': operations.os_type, 'osversion': operations.os_version}) # 'ostype': operations.os_type, 'osversion': operations.os_version})
self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip,
'ostype': operations.os_type, 'osversion': operations.os_version
'status': 'off'})
def processClientMessage(self, message, data): def processClientMessage(self, message, data):
logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data)) logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data))