diff --git a/iotronic_lightningrod/common/pam.py b/iotronic_lightningrod/common/pam.py index 3e4e8e0..2190782 100644 --- a/iotronic_lightningrod/common/pam.py +++ b/iotronic_lightningrod/common/pam.py @@ -77,6 +77,7 @@ class PamResp(Structure): def __repr__(self): return "" % (self.resp_retcode, self.resp) + conv_func = CFUNCTYPE( c_int, c_int, @@ -90,6 +91,7 @@ class PamWrapper(Structure): """pam_conv structure wrapper""" _fields_ = [("conv", conv_func), ("appdata_ptr", c_void_p)] + pamLib_start = libpam.pam_start pamLib_start.restype = c_int pamLib_start.argtypes = [ diff --git a/iotronic_lightningrod/devices/arancino.py b/iotronic_lightningrod/devices/arancino.py new file mode 100644 index 0000000..1a1038f --- /dev/null +++ b/iotronic_lightningrod/devices/arancino.py @@ -0,0 +1,56 @@ +# Copyright 2011 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +__author__ = "Nicola Peditto " + +import inspect + +from iotronic_lightningrod.devices import Device +from iotronic_lightningrod.devices.gpio import arancino + +from oslo_log import log as logging +LOG = logging.getLogger(__name__) + + +def whoami(): + return inspect.stack()[1][3] + + +def makeNothing(): + pass + + +class System(Device.Device): + + def __init__(self): + super(System, self).__init__("arancino") + + arancino.ArancinoGpio().EnableGPIO() + + def finalize(self): + """Function called at the end of module loading (after RPC registration). + + :return: + + """ + pass + + async def testRPC(self): + rpc_name = whoami() + LOG.info("RPC " + rpc_name + " CALLED...") + await makeNothing() + result = " - " + rpc_name + " result: testRPC is working!!!\n" + LOG.info(result) + return result diff --git a/iotronic_lightningrod/devices/gpio/arancino.py b/iotronic_lightningrod/devices/gpio/arancino.py new file mode 100644 index 0000000..4d55625 --- /dev/null +++ b/iotronic_lightningrod/devices/gpio/arancino.py @@ -0,0 +1,37 @@ +# Copyright 2011 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +__author__ = "Nicola Peditto " + +from oslo_log import log as logging + +from iotronic_lightningrod.devices.gpio import Gpio + +LOG = logging.getLogger(__name__) + + +class ArancinoGpio(Gpio.Gpio): + def __init__(self): + super(ArancinoGpio, self).__init__("arancino") + LOG.info("Arancino GPIO module importing...") + + # Enable GPIO + def EnableGPIO(self): + result = " - GPIO not available for 'arancino' device!" + LOG.info(result) + + def DisableGPIO(self): + result = " - GPIO not available for 'arancino' device!" + LOG.info(result) diff --git a/iotronic_lightningrod/devices/yun.py b/iotronic_lightningrod/devices/yun.py index 6225d47..72e6561 100644 --- a/iotronic_lightningrod/devices/yun.py +++ b/iotronic_lightningrod/devices/yun.py @@ -65,8 +65,9 @@ class System(Device.Device): """ LOG.info(" - readVoltage CALLED... reading pin " + Apin) - voltage = self.gpio._readVoltage(Apin) + voltage = await self.gpio._readVoltage(Apin) + + result = "read voltage for " + Apin + " pin: " + voltage - result = await "read voltage for " + Apin + " pin: " + voltage LOG.info(result) return result diff --git a/iotronic_lightningrod/lightningrod.py b/iotronic_lightningrod/lightningrod.py index 8c1053a..95ad8c4 100644 --- a/iotronic_lightningrod/lightningrod.py +++ b/iotronic_lightningrod/lightningrod.py @@ -26,7 +26,7 @@ from oslo_log import log as logging import asyncio import inspect import os -import pkg_resources +# import pkg_resources import signal import ssl import sys @@ -286,13 +286,22 @@ def iotronic_status(board_status): wamp_singleCheck(SESSION), loop ) - alive = alive.result() + try: + alive = alive.result(timeout=5) + except asyncio.TimeoutError: + LOG.warning('Check Iotronic request timeout') + alive.cancel() + alive = "alive_req_canceled" + + except asyncio.TimeoutError as e: + LOG.error(" - Iotronic check timeout: " + str(e)) + alive = "rpc_timeout" except Exception as e: LOG.error(" - Iotronic check: " + str(e)) - alive = e + alive = "not_connected" else: - alive = "Not connected!" + alive = "not_connected" return alive @@ -445,6 +454,9 @@ async def IotronicLogin(board, session, details): # reconnection = False else: + LOG.warning( + " - " + str(w_msg.result) + ": " + str(w_msg.message) + ) Bye() except exception.ApplicationError as e: @@ -819,8 +831,10 @@ def wampConnect(wamp_conf): async def onConnectFailure(session, fail_msg): LOG.warning("WAMP Connection Failure: " + str(fail_msg)) + """ LOG.warning(" - timeout set @ " + str(CONF.autobahn.connection_failure_timer)) + """ global connFailure if connFailure != None: @@ -930,9 +944,9 @@ def wampConnect(wamp_conf): LOG.error("Reconnection wrong status!") except IndexError as err: - LOG.error(" - Error parsing WAMP url: " + str(err)) - LOG.error(" --> port or address not specified") - board.status = "url_wamp_error" + LOG.error(" - Error parsing WAMP url: " + str(err)) + LOG.error(" --> port or address not specified") + board.status = "url_wamp_error" except Exception as err: LOG.error(" - WAMP connection error: " + str(err)) @@ -1026,68 +1040,73 @@ def modulesLoader(session): """ - LOG.info("Available modules: ") + try: - ep = [] + LOG.info("Available modules: ") - for ep in pkg_resources.iter_entry_points(group='s4t.modules'): - LOG.info(" - " + str(ep)) + ep = [] - if not ep: + for ep in pkg_resources.iter_entry_points(group='s4t.modules'): + LOG.info(" - " + str(ep)) - LOG.info("No modules available!") - sys.exit() + if not ep: - else: + LOG.info("No modules available!") + sys.exit() - modules = extension.ExtensionManager( - namespace='s4t.modules', - # invoke_on_load=True, - # invoke_args=(session,), - ) + else: - LOG.info('Modules to load:') + modules = extension.ExtensionManager( + namespace='s4t.modules', + # invoke_on_load=True, + # invoke_args=(session,), + ) - for ext in modules.extensions: + LOG.info('Modules to load:') - LOG.debug(ext.name) + for ext in modules.extensions: - if (ext.name == 'gpio') & (board.type == 'server'): - LOG.info("- GPIO module disabled for 'server' devices") + LOG.debug(ext.name) - else: + if (ext.name == 'gpio') & (board.type == 'server'): + LOG.info("- GPIO module disabled for 'server' devices") - if ext.name != "rest": + else: - mod = ext.plugin(board, session) + if ext.name != "rest": - global MODULES - MODULES[mod.name] = mod + mod = ext.plugin(board, session) - # Methods list for each module - meth_list = inspect.getmembers( - mod, predicate=inspect.ismethod - ) + global MODULES + MODULES[mod.name] = mod - global RPC - RPC[mod.name] = meth_list + # Methods list for each module + meth_list = inspect.getmembers( + mod, predicate=inspect.ismethod + ) - if len(meth_list) == 3: - # there are at least two methods for each module: - # "__init__" and "finalize" + global RPC + RPC[mod.name] = meth_list - LOG.info(" - No RPC to register for " - + str(ext.name) + " module!") + if len(meth_list) == 3: + # there are at least two methods for each module: + # "__init__" and "finalize" - else: - LOG.info(" - RPC list of " + str(mod.name) + ":") - moduleWampRegister(SESSION, meth_list) + LOG.info(" - No RPC to register for " + + str(ext.name) + " module!") - # Call the finalize procedure for each module - mod.finalize() + else: + LOG.info(" - RPC list of " + str(mod.name) + ":") + moduleWampRegister(SESSION, meth_list) - LOG.info("Lightning-rod modules loaded.") - LOG.info("\n\nListening...") + # Call the finalize procedure for each module + mod.finalize() + + except Exception as err: + LOG.warning("Board modules loading error: " + str(err)) + + LOG.info("Lightning-rod modules loaded.") + LOG.info("\n\nListening...") def moduleReloadInfo(session): diff --git a/iotronic_lightningrod/modules/device_manager.py b/iotronic_lightningrod/modules/device_manager.py index f1816d9..f2536ef 100644 --- a/iotronic_lightningrod/modules/device_manager.py +++ b/iotronic_lightningrod/modules/device_manager.py @@ -21,11 +21,14 @@ import os import subprocess import threading import time +import json +import requests from autobahn.wamp import exception from datetime import datetime from iotronic_lightningrod.common import utils +# from iotronic_lightningrod.common.exception import timeout from iotronic_lightningrod.config import package_path from iotronic_lightningrod.lightningrod import RPC_devices from iotronic_lightningrod.lightningrod import wampNotify @@ -495,7 +498,8 @@ class DeviceManager(Module.Module): + " " + str(pkg) else: - command = command + " " + str(cmd) + " " + str(pkg) + command = command + " " + str(cmd) \ + + " " + str(pkg) if 'version' in parameters: @@ -682,6 +686,41 @@ class DeviceManager(Module.Module): return w_msg.serialize() + # SC + async def DeviceFactoryReset(self, req, parameters=None): + req_id = req['uuid'] + rpc_name = utils.getFuncName() + LOG.info("RPC " + rpc_name + " CALLED [req_id: " + str(req_id) + "]:") + if parameters is not None: + LOG.info(" - " + rpc_name + " parameters: " + str(parameters)) + + def FactoryReset(): + message = factory_reset() + w_msg = WM.WampSuccess(msg=message, req_id=req_id) + + if (req['main_request_uuid'] != None): + wampNotify(self.device_session, + self.board, w_msg.serialize(), rpc_name) + else: + return w_msg + + if (req['main_request_uuid'] != None): + + LOG.info(" - main request: " + str(req['main_request_uuid'])) + try: + threading.Thread(target=FactoryReset).start() + w_msg = WM.WampRunning(msg=rpc_name, req_id=req_id) + + except Exception as err: + message = "Error in thr_" + rpc_name + ": " + str(err) + LOG.error(message) + w_msg = WM.WampError(msg=message, req_id=req_id) + + else: + w_msg = FactoryReset() + + return w_msg.serialize() + # SC async def DeviceNetConfig(self, req, parameters=None): req_id = req['uuid'] @@ -717,6 +756,136 @@ class DeviceManager(Module.Module): return w_msg.serialize() + # SC + async def DeviceRestSubmit(self, req, parameters=None): + req_id = req['uuid'] + rpc_name = utils.getFuncName() + LOG.info("RPC " + rpc_name + " CALLED [req_id: " + str(req_id) + "]:") + if parameters is not None: + LOG.info(" - " + rpc_name + " parameters: " + str(parameters)) + + def RestSubmit(): + + try: + + if 'url' in parameters: + url = str(parameters['url']) + else: + message = "Error RestSubmit: no url specified." + LOG.error(message) + w_msg = WM.WampError(msg=message, req_id=req_id) + return w_msg + + if 'method' in parameters: + method = str(parameters['method']) + else: + message = "Error RestSubmit: no REST method specified." + LOG.error(message) + w_msg = WM.WampError(msg=message, req_id=req_id) + return w_msg + + response = requests.request( + method, + url, + params=json.dumps(parameters['params']) if '\ + params' in parameters else None, + data=json.dumps(parameters['data']) if '\ + data' in parameters else None, + json=parameters['json'] if '\ + json' in parameters else None, + headers=parameters['headers'] if '\ + headers' in parameters else None, + cookies=parameters['cookies'] if '\ + cookies' in parameters else None, + files=parameters['files'] if '\ + files' in parameters else None, + auth=parameters['auth'] if '\ + auth' in parameters else None, + timeout=float(parameters['timeout']) if '\ + timeout' in parameters else None, + allow_redirects=parameters['allow_redirects'] if '\ + allow_redirects' in parameters else True, + proxies=parameters['proxies'] if '\ + proxies' in parameters else None, + verify=parameters['verify'] if '\ + verify' in parameters else True, + stream=parameters['stream'] if '\ + stream' in parameters else False, + cert=parameters['cert'] if '\ + cert' in parameters else None, + + ) + + res = json.loads(response.text) + + w_msg = WM.WampSuccess(msg=res, req_id=req_id) + + except Exception as err: + return WM.WampError(msg=str(err), req_id=req_id) + + if (req['main_request_uuid'] != None): + wampNotify(self.device_session, + self.board, w_msg.serialize(), rpc_name) + else: + return w_msg + + if (req['main_request_uuid'] != None): + + LOG.info(" - main request: " + str(req['main_request_uuid'])) + try: + + threading.Thread(target=RestSubmit).start() + w_msg = WM.WampRunning(msg=rpc_name, req_id=req_id) + + except Exception as err: + message = "Error in thr_" + rpc_name + ": " + str(err) + LOG.error(message) + w_msg = WM.WampError(msg=message, req_id=req_id) + + else: + w_msg = RestSubmit() + + return w_msg.serialize() + + +def lr_install(): + bashCommand = "lr_install" + process = subprocess.Popen(bashCommand.split(), + stdout=subprocess.PIPE) + output, error = process.communicate() + + return + + +def factory_reset(): + + LOG.info("Lightning-rod factory reset: ") + + # delete nginx conf.d files + os.system("rm /etc/nginx/conf.d/lr_*") + LOG.info("--> NGINX settings deleted.") + + # delete letsencrypt + os.system("rm -r /etc/letsencrypt/*") + LOG.info("--> LetsEncrypt settings deleted.") + + # delete var-iotronic + os.system("rm -r /var/lib/iotronic/*") + LOG.info("--> Iotronic data deleted.") + + # delete etc-iotronic + os.system("rm -r /etc/iotronic/*") + LOG.info("--> Iotronic settings deleted.") + + # exec lr_install + lr_install() + + # restart LR + LOG.info("--> LR restarting in 5 seconds...") + lr_utils.LR_restart_delayed(5) + + return "Device reset completed" + def getIfconfig(): diff --git a/iotronic_lightningrod/modules/plugin_manager.py b/iotronic_lightningrod/modules/plugin_manager.py index 0db9bbd..bb2c4e2 100644 --- a/iotronic_lightningrod/modules/plugin_manager.py +++ b/iotronic_lightningrod/modules/plugin_manager.py @@ -25,6 +25,7 @@ import queue import shutil import threading import time +import platform from iotronic_lightningrod.common import utils @@ -149,9 +150,17 @@ class PluginManager(Module.Module): try: - if (plugin_uuid in PLUGINS_THRS) and ( - PLUGINS_THRS[plugin_uuid].isAlive() - ): + worker_alive = False + if (plugin_uuid in PLUGINS_THRS): + worker = PLUGINS_THRS[plugin_uuid] + + pyvers = platform.python_version_tuple() + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + + if (plugin_uuid in PLUGINS_THRS) and worker_alive: LOG.warning(" - Plugin " + plugin_uuid + " already started!") @@ -380,10 +389,19 @@ class PluginManager(Module.Module): plugin_name = plugins_conf['plugins'][plugin_uuid]['name'] + worker_alive = False + if (plugin_uuid in PLUGINS_THRS): + worker = PLUGINS_THRS[plugin_uuid] + + pyvers = platform.python_version_tuple() + + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + # Check if the plugin is already running - if (plugin_uuid in PLUGINS_THRS) and ( - PLUGINS_THRS[plugin_uuid].isAlive() - ): + if (plugin_uuid in PLUGINS_THRS) and worker_alive: message = "ALREADY STARTED!" LOG.warning(" - Plugin " @@ -495,10 +513,19 @@ class PluginManager(Module.Module): if plugin_uuid in PLUGINS_THRS: - worker = PLUGINS_THRS[plugin_uuid] - LOG.debug(" - Stopping plugin " + str(worker)) + worker_alive = False + if (plugin_uuid in PLUGINS_THRS): + worker = PLUGINS_THRS[plugin_uuid] + LOG.debug(" - Stopping plugin " + str(worker)) - if worker.isAlive(): + pyvers = platform.python_version_tuple() + + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + + if worker_alive: if 'delay' in parameters: time.sleep(delay) @@ -556,9 +583,17 @@ class PluginManager(Module.Module): try: - if (plugin_uuid in PLUGINS_THRS) and ( - PLUGINS_THRS[plugin_uuid].isAlive() - ): + worker_alive = False + if (plugin_uuid in PLUGINS_THRS): + worker = PLUGINS_THRS[plugin_uuid] + + pyvers = platform.python_version_tuple() + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + + if (plugin_uuid in PLUGINS_THRS) and worker_alive: message = "Plugin " + plugin_uuid + " already started!" LOG.warning(" - " + message) @@ -727,7 +762,14 @@ class PluginManager(Module.Module): if plugin_uuid in PLUGINS_THRS: worker = PLUGINS_THRS[plugin_uuid] - if worker.isAlive(): + + pyvers = platform.python_version_tuple() + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + + if worker_alive: LOG.info(" - Plugin '" + plugin_name + "' is running...") worker.stop() @@ -798,17 +840,28 @@ class PluginManager(Module.Module): worker = PLUGINS_THRS[plugin_uuid] + pyvers = platform.python_version_tuple() + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + # STOP PLUGIN---------------------------------------------- - if worker.isAlive(): + if worker_alive: LOG.info(" - Thread " + plugin_uuid + " is running, stopping...") LOG.debug(" - Stopping plugin " + str(worker)) worker.stop() - while worker.isAlive(): - pass + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + while worker.is_alive(): + pass + else: + while worker.isAlive(): + pass # Remove from plugin thread list del PLUGINS_THRS[plugin_uuid] @@ -893,7 +946,13 @@ class PluginManager(Module.Module): worker = PLUGINS_THRS[plugin_uuid] - if worker.isAlive(): + pyvers = platform.python_version_tuple() + if int(pyvers[0]) == 3 and int(pyvers[1]) >= 9: + worker_alive = worker.is_alive() + else: + worker_alive = worker.isAlive() + + if worker_alive: result = "ALIVE" else: result = "DEAD" diff --git a/iotronic_lightningrod/modules/proxies/nginx.py b/iotronic_lightningrod/modules/proxies/nginx.py index 1a08a1b..68eb6cc 100644 --- a/iotronic_lightningrod/modules/proxies/nginx.py +++ b/iotronic_lightningrod/modules/proxies/nginx.py @@ -24,7 +24,6 @@ LOG = logging.getLogger(__name__) import json import os -import shutil import subprocess import time @@ -212,6 +211,16 @@ class ProxyManager(Proxy.Proxy): nginx_board_conf = '''server {{ listen 50000; server_name {0}; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + location / {{ proxy_pass http://127.0.0.1:1474; }} @@ -294,6 +303,39 @@ class ProxyManager(Proxy.Proxy): return json.dumps(nginxMsg) + def _proxyRenewWebservice(self): + + nginxMsg = {} + + try: + + command = "/usr/bin/certbot " \ + "renew " \ + "--force-renewal " \ + "-w /var/www/html" + + LOG.info("Certbot is renewing certificate:") + LOG.info(command) + + certbot_result = call(command, shell=True) + LOG.info("CERTBOT RESULT: " + str(certbot_result)) + + if (certbot_result == 0): + nginxMsg['result'] = "SUCCESS" + nginxMsg['message'] = "Webservice certificate renewed." + else: + nginxMsg['result'] = "ERROR" + nginxMsg['message'] = "Renewing certificate failure." + + LOG.info("--> " + nginxMsg['message']) + + except Exception as err: + nginxMsg['log'] = "Renewing certificate error: " + str(err) + nginxMsg['code'] = "" + LOG.warning("--> " + nginxMsg['log']) + + return json.dumps(nginxMsg) + def _exposeWebservice(self, board_dns, service_dns, local_port, dns_list): nginxMsg = {} @@ -316,7 +358,7 @@ class ProxyManager(Proxy.Proxy): proxy_set_header Connection "upgrade"; location / {{ - proxy_pass http://localhost:{1}; + proxy_pass http://127.0.0.1:{1}; }} location ~ /.well-known {{ root /var/www/html; diff --git a/iotronic_lightningrod/modules/rest_manager.py b/iotronic_lightningrod/modules/rest_manager.py index 83ae3aa..c067c8e 100644 --- a/iotronic_lightningrod/modules/rest_manager.py +++ b/iotronic_lightningrod/modules/rest_manager.py @@ -34,8 +34,9 @@ from flask import request from flask import send_file from flask import session as f_session from flask import url_for +from flask import abort +# from flask import Response -import getpass import os import subprocess import threading @@ -144,6 +145,18 @@ class RestManager(Module.Module): lr_cty = sock_bundle[2] + " - " + sock_bundle[0] \ + " - " + sock_bundle[1] + webservice_list = [] + nginx_path = "/etc/nginx/conf.d/" + + if os.path.exists(nginx_path): + active_webservice_list = [f for f in os.listdir(nginx_path) + if os.path.isfile(os.path.join(nginx_path, f))] + + if len(active_webservice_list) != 0: + for ws in active_webservice_list: + ws = ws.replace('.conf', '') + webservice_list.append(ws) + info = { 'board_id': board.uuid, 'board_name': board.name, @@ -155,6 +168,7 @@ class RestManager(Module.Module): 'board_reg_status': str(board.status), 'iotronic_status': str(iotronic_status(board.status)), 'service_list': service_list, + 'webservice_list': webservice_list, 'serial_dev': device_manager.getSerialDevice(), 'nic': lr_cty, 'lr_version': str( @@ -162,56 +176,85 @@ class RestManager(Module.Module): ) } - return info + return info, 200 @app.route('/status') def status(): - if ('username' in f_session): + try: - f_session['status'] = str(board.status) + if ('username' in f_session): + + f_session['status'] = str(board.status) + + wstun_status = service_manager.wstun_status() + if wstun_status == 0: + wstun_status = "Online" + else: + wstun_status = "Offline" + + service_list = service_manager.services_list("html") + if service_list == "": + service_list = "no services exposed!" + + webservice_list = "" + nginx_path = "/etc/nginx/conf.d/" + + if os.path.exists(nginx_path): + active_webservice_list = [ + f for f in os.listdir(nginx_path) + if os.path.isfile(os.path.join(nginx_path, f)) + ] + + for ws in active_webservice_list: + ws = ws.replace('.conf', '')[3:] + webservice_list = webservice_list + "\ +
  • " + ws + "
  • " + else: + webservice_list = "no webservices exposed!" + + if webservice_list == "": + webservice_list = "no webservices exposed!" + + lr_cty = "N/A" + from iotronic_lightningrod.lightningrod import wport + sock_bundle = lr_utils.get_socket_info(wport) + + if sock_bundle != "N/A": + lr_cty = sock_bundle[2] + " - " + sock_bundle[0] \ + + " - " + sock_bundle[1] + + info = { + 'board_id': board.uuid, + 'board_name': board.name, + 'wagent': board.agent, + 'session_id': board.session_id, + 'timestamp': str( + datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')), + 'wstun_status': wstun_status, + 'board_reg_status': str(board.status), + 'iotronic_status': str(iotronic_status(board.status)), + 'service_list': str(service_list), + 'webservice_list': str(webservice_list), + 'serial_dev': device_manager.getSerialDevice(), + 'nic': lr_cty, + 'lr_version': str( + utils.get_version("iotronic-lightningrod") + ) + } + + return render_template('status.html', **info) - wstun_status = service_manager.wstun_status() - if wstun_status == 0: - wstun_status = "Online" else: - wstun_status = "Offline" - - service_list = service_manager.services_list("html") - if service_list == "": - service_list = "no services exposed!" - - lr_cty = "N/A" - from iotronic_lightningrod.lightningrod import wport - sock_bundle = lr_utils.get_socket_info(wport) - - if sock_bundle != "N/A": - lr_cty = sock_bundle[2] + " - " + sock_bundle[0] \ - + " - " + sock_bundle[1] + return redirect(url_for('login', next=request.endpoint)) + except Exception as err: + LOG.error(err) info = { - 'board_id': board.uuid, - 'board_name': board.name, - 'wagent': board.agent, - 'session_id': board.session_id, - 'timestamp': str( - datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')), - 'wstun_status': wstun_status, - 'board_reg_status': str(board.status), - 'iotronic_status': str(iotronic_status(board.status)), - 'service_list': str(service_list), - 'serial_dev': device_manager.getSerialDevice(), - 'nic': lr_cty, - 'lr_version': str( - utils.get_version("iotronic-lightningrod") - ) - } - + 'messages': [str(err)] + } return render_template('status.html', **info) - else: - return redirect(url_for('login', next=request.endpoint)) - @app.route('/system') def system(): if 'username' in f_session: @@ -360,7 +403,7 @@ class RestManager(Module.Module): **info, error=error ) - return redirect("/config", code=302) + # return redirect("/config", code=302) else: return redirect("/", code=302) @@ -370,6 +413,9 @@ class RestManager(Module.Module): @app.route('/backup', methods=['GET']) def backup_download(): + # LOG.info(request.query_string) + # LOG.info(request.__dict__) + if 'username' in f_session: print("Identity file downloading: ") @@ -433,7 +479,33 @@ class RestManager(Module.Module): if request.method == 'POST': - if request.form.get('reg_btn') == 'CONFIGURE': + req_body = request.get_json() + + LOG.debug(req_body) + + if req_body != None: + + if 'action' in req_body: + + if req_body['action'] == "configure": + LOG.info("API LR configuration") + + ragent = req_body['urlwagent'] + code = req_body['code'] + + lr_config(ragent, code) + + if 'hostname' in req_body: + if req_body['hostname'] != "": + change_hostname(req_body['hostname']) + + return {"result": "LR configured, \ + authenticating..."}, 200 + + else: + abort(400) + + elif request.form.get('reg_btn') == 'CONFIGURE': ragent = request.form['urlwagent'] code = request.form['code'] lr_config(ragent, code) @@ -633,6 +705,12 @@ class RestManager(Module.Module): return render_template('config.html', **info) else: + if request.method == 'POST': + req_body = request.get_json() + + if req_body != None and str(board.status) != "first_boot": + return {"result": "LR already configured!"}, 403 + return redirect(url_for('login', next=request.endpoint)) app.run(host='0.0.0.0', port=1474, debug=False, use_reloader=False) diff --git a/iotronic_lightningrod/modules/service_manager.py b/iotronic_lightningrod/modules/service_manager.py index 9cbc25a..3c03488 100644 --- a/iotronic_lightningrod/modules/service_manager.py +++ b/iotronic_lightningrod/modules/service_manager.py @@ -1,5 +1,4 @@ -# Copyright 2017 MDSLAB - University of Messina -# All Rights Reserved. +# Copyright 2017 MDSLAB - University of Messina. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -24,9 +23,6 @@ import signal import socket import subprocess import time - -import threading - import copy from datetime import datetime from random import randint @@ -36,9 +32,7 @@ from urllib.parse import urlparse from iotronic_lightningrod.common import utils from iotronic_lightningrod.config import package_path from iotronic_lightningrod.modules import Module - from iotronic_lightningrod import lightningrod - import iotronic_lightningrod.wampmessage as WM @@ -101,6 +95,8 @@ class ServiceManager(Module.Module): if wurl_list[0] == "wss": is_wss = True + self.board_id = board.uuid + if is_wss: self.wstun_url = "wss://" + self.wstun_ip + ":" + self.wstun_port else: @@ -600,7 +596,7 @@ class ServiceManager(Module.Module): except Exception as err: LOG.error(" --> Parsing error in " + s_conf_FILE + ": " + str(err)) - if os.path.isfile(s_conf_FILE): + if os.path.isfile(s_conf_FILE + '.bkp '): LOG.info(" --> restoring services.json file...") @@ -621,6 +617,28 @@ class ServiceManager(Module.Module): LOG.error(" --> services.json backup file does not exist!") s_conf = None + if s_conf == None: + try: + LOG.info(" --> loading services.json template file") + template_conf = '''{ + "services": { + + } + } + ''' + with open(s_conf_FILE, "w") as text_file: + text_file.write("%s" % template_conf) + + with open(s_conf_FILE) as settings: + s_conf = json.load(settings) + + except Exception as err: + LOG.error( + " --> Loading template error: \ + manual check on device required!" + ) + s_conf = None + return s_conf def _wstunMon(self, wstun, local_port): @@ -762,16 +780,24 @@ class ServiceManager(Module.Module): try: + # subp_cmd = [CONF.services.wstun_bin, opt_reverse, + # self.wstun_url, '-u "' + str(self.board_id) + '"'] + + subp_cmd = [CONF.services.wstun_bin, opt_reverse, + self.wstun_url, '-u', '' + str(self.board_id) + ''] + wstun = subprocess.Popen( - [CONF.services.wstun_bin, opt_reverse, self.wstun_url], + subp_cmd, stdout=subprocess.PIPE ) if (event != "boot"): print("WSTUN start event:") - cmd_print = 'WSTUN exec: ' + str(CONF.services.wstun_bin) \ - + " " + opt_reverse + ' ' + self.wstun_url + # cmd_print = 'WSTUN exec: ' + str(CONF.services.wstun_bin) \ + # + " " + opt_reverse + ' ' + self.wstun_url + cmd_print = 'WSTUN exec: ' + str(subp_cmd) + print(" - " + str(cmd_print)) LOG.debug(cmd_print) @@ -845,16 +871,24 @@ class ServiceManager(Module.Module): ) try: + + # subp_cmd = [CONF.services.wstun_bin, opt_reverse, self.wstun_url, + # '-u "' + str(self.board_id) + '"'] + + subp_cmd = [CONF.services.wstun_bin, opt_reverse, self.wstun_url, + '-u', '' + str(self.board_id) + ''] + wstun = subprocess.Popen( - [CONF.services.wstun_bin, opt_reverse, self.wstun_url], + subp_cmd, stdout=subprocess.PIPE ) if (event != "boot"): print("WSTUN start event:") - cmd_print = 'WSTUN exec: ' + str(CONF.services.wstun_bin) + " " \ - + opt_reverse + ' ' + self.wstun_url + # cmd_print = 'WSTUN exec: ' + str(CONF.services.wstun_bin) + " " \ + # + opt_reverse + ' ' + self.wstun_url + cmd_print = 'WSTUN exec: ' + str(subp_cmd) print(" - " + str(cmd_print)) LOG.debug(cmd_print) @@ -926,9 +960,32 @@ class ServiceManager(Module.Module): if parameters is not None: LOG.info(" - " + rpc_name + " parameters: " + str(parameters)) - thr_list = str(threading.enumerate()) + tuns = { + "sockets": [], + "procs": [] + } - w_msg = WM.WampSuccess(msg=thr_list, req_id=req_id) + """ LSOF """ + res_lsof = subprocess.Popen( + "lsof -i -n -P | grep '8080'| grep -v grep", + shell=True, + stdout=subprocess.PIPE + ) + sockets = res_lsof.communicate()[0].decode("utf-8").split("\n") + + tuns['sockets'] = sockets[:-1] + + """ PS """ + res_ps = subprocess.Popen( + "ps aux | grep 'wstun' | grep -v grep", + shell=True, + stdout=subprocess.PIPE + ) + ps_s4t = res_ps.communicate()[0].decode("utf-8").split("\n") + + tuns['procs'] = ps_s4t[:-1] + + w_msg = WM.WampSuccess(msg=tuns, req_id=req_id) return w_msg.serialize() diff --git a/iotronic_lightningrod/modules/utils.py b/iotronic_lightningrod/modules/utils.py index e6a4e35..a6005ad 100644 --- a/iotronic_lightningrod/modules/utils.py +++ b/iotronic_lightningrod/modules/utils.py @@ -27,6 +27,8 @@ import subprocess import sys import threading import time +import signal + from iotronic_lightningrod.common import utils from iotronic_lightningrod.config import entry_points_name @@ -40,6 +42,9 @@ LOG = logging.getLogger(__name__) global connFailureRecovery connFailureRecovery = None +global gdbPid +gdbPid = None + class Utility(Module.Module): @@ -200,18 +205,35 @@ def destroyWampSocket(): LOG.warning("WAMP Connection Recovery timer: EXPIRED") lr_utils.LR_restart() + def timeoutGDB(): + LOG.warning("WAMP Connection Recovery GDB timer: EXPIRED") + + global gdbPid + os.kill(gdbPid, signal.SIGKILL) + LOG.warning("WAMP Connection Recovery GDB process: KILLED") + + LOG.warning("WAMP Connection Recovery GDB process: LR restarting...") + lr_utils.LR_restart() + connFailureRecovery = Timer(30, timeout) connFailureRecovery.start() LOG.warning("WAMP Connection Recovery timer: STARTED") try: + gdbTimeoutCheck = Timer(30, timeoutGDB) + gdbTimeoutCheck.start() + LOG.debug("WAMP Connection Recovery GDB timer: STARTED") + process = subprocess.Popen( ["gdb", "-p", str(LR_PID)], stdin=subprocess.PIPE, stdout=subprocess.PIPE ) + global gdbPid + gdbPid = process.pid + proc = psutil.Process() conn_list = proc.connections() @@ -250,6 +272,9 @@ def destroyWampSocket(): ) connFailureRecovery.cancel() + gdbTimeoutCheck.cancel() + LOG.debug("WAMP Connection Recovery GDB timer: CLEANED") + if wamp_conn_set == False: LOG.warning("WAMP CONNECTION NOT FOUND: LR restarting...") # In conn_list there is not the WAMP connection! diff --git a/iotronic_lightningrod/modules/web/static/images/stack4thingsblack.png b/iotronic_lightningrod/modules/web/static/images/stack4thingsblack.png new file mode 100644 index 0000000..92f519e Binary files /dev/null and b/iotronic_lightningrod/modules/web/static/images/stack4thingsblack.png differ diff --git a/iotronic_lightningrod/modules/web/static/images/stack4thingslogo.png b/iotronic_lightningrod/modules/web/static/images/stack4thingslogo.png new file mode 100644 index 0000000..c95dbab Binary files /dev/null and b/iotronic_lightningrod/modules/web/static/images/stack4thingslogo.png differ diff --git a/iotronic_lightningrod/modules/web/templates/home.html b/iotronic_lightningrod/modules/web/templates/home.html index 39d79b2..b4c9ef3 100644 --- a/iotronic_lightningrod/modules/web/templates/home.html +++ b/iotronic_lightningrod/modules/web/templates/home.html @@ -33,7 +33,13 @@ {% else %} -
    +
    + +
    + stack4thingslogo + +
    + {% endif %} diff --git a/iotronic_lightningrod/modules/web/templates/login.html b/iotronic_lightningrod/modules/web/templates/login.html index 290741d..7ea59d8 100644 --- a/iotronic_lightningrod/modules/web/templates/login.html +++ b/iotronic_lightningrod/modules/web/templates/login.html @@ -28,7 +28,10 @@