Release Lightning-rod v0.4.5:
- Added REST server for local LR management - Modules loader updated: - Single module loader added - Service Manager improved: - Add checks status for WSTUN server - Cleaning procedures added for WSTUN monitor threads - services.json backup management fixed - Webservice Manager updated: - NGINX listening ports changed - Docker procedures updated - Installation procedures updated - Requirements fixed: Flask Change-Id: I73fdb55a2ea58d0f7a76eab78734d067133438c5
This commit is contained in:
parent
db3994293f
commit
c2d6ecf0f5
|
@ -8,6 +8,9 @@ include scripts/lr_install
|
||||||
include scripts/lr_configure
|
include scripts/lr_configure
|
||||||
include scripts/device_bkp_rest
|
include scripts/device_bkp_rest
|
||||||
include iotronic_lightningrod/proxies/configs/*
|
include iotronic_lightningrod/proxies/configs/*
|
||||||
|
include iotronic_lightningrod/modules/web/static/css/*
|
||||||
|
include iotronic_lightningrod/modules/web/static/js/*
|
||||||
|
include iotronic_lightningrod/modules/web/templates/*
|
||||||
|
|
||||||
include AUTHORS
|
include AUTHORS
|
||||||
include ChangeLog
|
include ChangeLog
|
||||||
|
|
|
@ -3,34 +3,10 @@
|
||||||
GitHub repo:
|
GitHub repo:
|
||||||
- https://github.com/openstack/iotronic-lightning-rod
|
- https://github.com/openstack/iotronic-lightning-rod
|
||||||
|
|
||||||
# Configure Lightning-rod environment
|
|
||||||
|
|
||||||
* Create the folder in your system to store Lightning-rod settings <LR_CONF_PATH> (e.g. "/etc/iotronic/"):
|
|
||||||
```
|
|
||||||
sudo mkdir <LR_CONF_PATH>
|
|
||||||
```
|
|
||||||
|
|
||||||
* Get Lightning-rod configuration template files:
|
|
||||||
```
|
|
||||||
cd <LR_CONF_PATH>
|
|
||||||
sudo wget https://raw.githubusercontent.com/openstack/iotronic-lightning-rod/master/templates/settings.example.json -O settings.json
|
|
||||||
sudo wget https://raw.githubusercontent.com/openstack/iotronic-lightning-rod/master/etc/iotronic/iotronic.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
* Configure Lightning-rod identity:
|
|
||||||
```
|
|
||||||
cd <LR_CONF_PATH>
|
|
||||||
wget https://raw.githubusercontent.com/openstack/iotronic-lightning-rod/master/scripts/lr_configure
|
|
||||||
chmod +x lr_configure
|
|
||||||
./lr_configure -c <REGISTRATION-TOKEN> <WAMP-REG-AGENT-URL> <LR_CONF_PATH>
|
|
||||||
```
|
|
||||||
|
|
||||||
# Create container:
|
# Create container:
|
||||||
```
|
```
|
||||||
docker run -d --privileged \
|
docker run -d --privileged \
|
||||||
-v lr_var:/var/lib/iotronic -v lr_le:/etc/letsencrypt/ \
|
-v lr_var:/var/lib/iotronic -v lr_le:/etc/letsencrypt/ \
|
||||||
-v <LR_CONF_PATH>/settings.json:/etc/iotronic/settings.json \
|
|
||||||
-v <LR_CONF_PATH>/iotronic.conf:/etc/iotronic/iotronic.conf \
|
|
||||||
--net=host --restart unless-stopped \
|
--net=host --restart unless-stopped \
|
||||||
--name=lightning-rod mdslab/rpi-openstack-iotronic-lightning-rod
|
--name=lightning-rod mdslab/rpi-openstack-iotronic-lightning-rod
|
||||||
```
|
```
|
||||||
|
|
|
@ -3,34 +3,10 @@
|
||||||
GitHub repo:
|
GitHub repo:
|
||||||
- https://github.com/openstack/iotronic-lightning-rod
|
- https://github.com/openstack/iotronic-lightning-rod
|
||||||
|
|
||||||
# Configure Lightning-rod environment
|
|
||||||
|
|
||||||
* Create the folder in your system to store Lightning-rod settings <LR_CONF_PATH> (e.g. "/etc/iotronic/"):
|
|
||||||
```
|
|
||||||
sudo mkdir <LR_CONF_PATH>
|
|
||||||
```
|
|
||||||
|
|
||||||
* Get Lightning-rod configuration template files:
|
|
||||||
```
|
|
||||||
cd <LR_CONF_PATH>
|
|
||||||
sudo wget https://raw.githubusercontent.com/openstack/iotronic-lightning-rod/master/templates/settings.example.json -O settings.json
|
|
||||||
sudo wget https://raw.githubusercontent.com/openstack/iotronic-lightning-rod/master/etc/iotronic/iotronic.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
* Configure Lightning-rod identity:
|
|
||||||
```
|
|
||||||
cd <LR_CONF_PATH>
|
|
||||||
wget https://raw.githubusercontent.com/openstack/iotronic-lightning-rod/master/scripts/lr_configure
|
|
||||||
chmod +x lr_configure
|
|
||||||
./lr_configure -c <REGISTRATION-TOKEN> <WAMP-REG-AGENT-URL> <LR_CONF_PATH>
|
|
||||||
```
|
|
||||||
|
|
||||||
# Create container:
|
# Create container:
|
||||||
```
|
```
|
||||||
docker run -d --privileged \
|
docker run -d --privileged \
|
||||||
-v lr_var:/var/lib/iotronic -v lr_le:/etc/letsencrypt/ \
|
-v lr_var:/var/lib/iotronic -v lr_le:/etc/letsencrypt/ \
|
||||||
-v <LR_CONF_PATH>/settings.json:/etc/iotronic/settings.json \
|
|
||||||
-v <LR_CONF_PATH>/iotronic.conf:/etc/iotronic/iotronic.conf \
|
|
||||||
--net=host --restart unless-stopped \
|
--net=host --restart unless-stopped \
|
||||||
--name=lightning-rod mdslab/openstack-iotronic-lightning-rod
|
--name=lightning-rod mdslab/openstack-iotronic-lightning-rod
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,8 +1,47 @@
|
||||||
IoTronic Lightning-rod installation guide for Raspberry Pi 2/3
|
IoTronic Lightning-rod installation guide for Raspberry Pi 2/3
|
||||||
============================================================
|
==============================================================
|
||||||
|
|
||||||
We tested this procedure on a Raspberry Pi 2/3 board (Raspbian).
|
We tested this procedure on a Raspberry Pi 2/3 board (Raspbian).
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* OS requirement
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt install python3 python3-setuptools python3-pip gdb lsof
|
||||||
|
|
||||||
|
* NodeJS
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||||
|
apt-get install -y nodejs
|
||||||
|
npm install -g npm
|
||||||
|
echo "NODE_PATH=/usr/lib/node_modules" | tee -a /etc/environment
|
||||||
|
source /etc/environment > /dev/null
|
||||||
|
|
||||||
|
|
||||||
|
* WSTUN:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
npm install -g --unsafe @mdslab/wstun
|
||||||
|
|
||||||
|
* NGINX:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt install -y nginx
|
||||||
|
sed -i 's/# server_names_hash_bucket_size 64;/server_names_hash_bucket_size 64;/g' /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
* Certbot
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt-get install python-certbot-nginx
|
||||||
|
|
||||||
|
|
||||||
Install Lightning-rod
|
Install Lightning-rod
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -26,6 +65,7 @@ Iotronic setup
|
||||||
Arguments required:
|
Arguments required:
|
||||||
* <REGISTRATION-TOKEN> , token released by IoTronic registration procedure
|
* <REGISTRATION-TOKEN> , token released by IoTronic registration procedure
|
||||||
* <WAMP-REG-AGENT-URL> , IoTronic Crossbar server WAMP URL:
|
* <WAMP-REG-AGENT-URL> , IoTronic Crossbar server WAMP URL:
|
||||||
|
|
||||||
ws(s)://<IOTRONIC-CROSSBAR-IP>:<IOTRONIC-CROSSBAR-PORT>/
|
ws(s)://<IOTRONIC-CROSSBAR-IP>:<IOTRONIC-CROSSBAR-PORT>/
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
|
|
|
@ -4,6 +4,46 @@ IoTronic Lightning-rod installation guide for Ubuntu 16.04
|
||||||
We tested this procedure on a Ubuntu 16.04 (also within a LXD
|
We tested this procedure on a Ubuntu 16.04 (also within a LXD
|
||||||
container). Everything needs to be run as root.
|
container). Everything needs to be run as root.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* OS requirement
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt install python3 python3-setuptools python3-pip gdb lsof
|
||||||
|
|
||||||
|
* NodeJS
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||||
|
apt-get install -y nodejs
|
||||||
|
npm install -g npm
|
||||||
|
echo "NODE_PATH=/usr/lib/node_modules" | tee -a /etc/environment
|
||||||
|
source /etc/environment > /dev/null
|
||||||
|
|
||||||
|
|
||||||
|
* WSTUN:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
npm install -g --unsafe @mdslab/wstun
|
||||||
|
|
||||||
|
* NGINX:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt install -y nginx
|
||||||
|
sed -i 's/# server_names_hash_bucket_size 64;/server_names_hash_bucket_size 64;/g' /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
* Certbot
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt-get install python-certbot-nginx
|
||||||
|
|
||||||
|
|
||||||
Install Lightning-rod
|
Install Lightning-rod
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
::
|
::
|
||||||
|
@ -26,6 +66,7 @@ Iotronic setup
|
||||||
Arguments required:
|
Arguments required:
|
||||||
* <REGISTRATION-TOKEN> , token released by IoTronic registration procedure
|
* <REGISTRATION-TOKEN> , token released by IoTronic registration procedure
|
||||||
* <WAMP-REG-AGENT-URL> , IoTronic Crossbar server WAMP URL:
|
* <WAMP-REG-AGENT-URL> , IoTronic Crossbar server WAMP URL:
|
||||||
|
|
||||||
ws(s)://<IOTRONIC-CROSSBAR-IP>:<IOTRONIC-CROSSBAR-PORT>/
|
ws(s)://<IOTRONIC-CROSSBAR-IP>:<IOTRONIC-CROSSBAR-PORT>/
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
|
|
|
@ -27,6 +27,9 @@ CONF = cfg.CONF
|
||||||
|
|
||||||
SETTINGS = '/etc/iotronic/settings.json'
|
SETTINGS = '/etc/iotronic/settings.json'
|
||||||
|
|
||||||
|
# global FIRST_BOOT
|
||||||
|
FIRST_BOOT = False
|
||||||
|
|
||||||
|
|
||||||
class Board(object):
|
class Board(object):
|
||||||
|
|
||||||
|
@ -107,14 +110,26 @@ class Board(object):
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if str(err) != 'uuid':
|
if str(err) != 'uuid':
|
||||||
LOG.warning("settings.json file exception: " + str(err))
|
if self.status == None:
|
||||||
|
LOG.warning("settings.json file exception: " + str(err))
|
||||||
|
|
||||||
# STATUS REGISTERED
|
# STATUS REGISTERED
|
||||||
try:
|
try:
|
||||||
self.code = board_config['code']
|
self.code = board_config['code']
|
||||||
LOG.info('First registration board settings: ')
|
|
||||||
LOG.info(' - code: ' + str(self.code))
|
if self.code == "<REGISTRATION-TOKEN>":
|
||||||
self.getWampAgent(self.iotronic_config)
|
# LR start to waiting for first configuration
|
||||||
|
global FIRST_BOOT
|
||||||
|
if FIRST_BOOT == False:
|
||||||
|
FIRST_BOOT = True
|
||||||
|
LOG.info("FIRST BOOT procedure started")
|
||||||
|
self.status = "first_boot"
|
||||||
|
|
||||||
|
else:
|
||||||
|
LOG.info('First registration board settings: ')
|
||||||
|
LOG.info(' - code: ' + str(self.code))
|
||||||
|
self.getWampAgent(self.iotronic_config)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
LOG.error("Wrong code: " + str(err))
|
LOG.error("Wrong code: " + str(err))
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
|
|
|
@ -32,12 +32,15 @@ import signal
|
||||||
import ssl
|
import ssl
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import txaio
|
import txaio
|
||||||
|
|
||||||
from pip._vendor import pkg_resources
|
from pip._vendor import pkg_resources
|
||||||
|
|
||||||
# IoTronic imports
|
# IoTronic imports
|
||||||
from iotronic_lightningrod.Board import Board
|
from iotronic_lightningrod.Board import Board
|
||||||
|
from iotronic_lightningrod.Board import FIRST_BOOT
|
||||||
|
|
||||||
from iotronic_lightningrod.common.exception import timeoutALIVE
|
from iotronic_lightningrod.common.exception import timeoutALIVE
|
||||||
from iotronic_lightningrod.common.exception import timeoutRPC
|
from iotronic_lightningrod.common.exception import timeoutRPC
|
||||||
from iotronic_lightningrod.common import utils
|
from iotronic_lightningrod.common import utils
|
||||||
|
@ -71,6 +74,7 @@ lr_opts = [
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.register_opts(lr_opts)
|
CONF.register_opts(lr_opts)
|
||||||
|
|
||||||
|
global SESSION
|
||||||
SESSION = None
|
SESSION = None
|
||||||
global board
|
global board
|
||||||
board = None
|
board = None
|
||||||
|
@ -81,6 +85,7 @@ RPC_proxies = {}
|
||||||
zombie_alert = True
|
zombie_alert = True
|
||||||
|
|
||||||
# ASYNCIO
|
# ASYNCIO
|
||||||
|
global loop
|
||||||
loop = None
|
loop = None
|
||||||
component = None
|
component = None
|
||||||
txaio.start_logging(level="info")
|
txaio.start_logging(level="info")
|
||||||
|
@ -110,6 +115,8 @@ class LightningRod(object):
|
||||||
CONF(project='iotronic')
|
CONF(project='iotronic')
|
||||||
logging.setup(CONF, DOMAIN)
|
logging.setup(CONF, DOMAIN)
|
||||||
|
|
||||||
|
self.w = None
|
||||||
|
|
||||||
if (utils.checkIotronicConf(CONF)):
|
if (utils.checkIotronicConf(CONF)):
|
||||||
|
|
||||||
if CONF.debug:
|
if CONF.debug:
|
||||||
|
@ -133,8 +140,20 @@ class LightningRod(object):
|
||||||
global board
|
global board
|
||||||
board = Board()
|
board = Board()
|
||||||
|
|
||||||
self.w = WampManager(board.wamp_config)
|
# Start REST server
|
||||||
|
singleModuleLoader("rest", session=None)
|
||||||
|
|
||||||
|
if(board.status == "first_boot"):
|
||||||
|
LOG.info("LR FIRST BOOT: waiting for first configuration...")
|
||||||
|
|
||||||
|
while (board.status == "first_boot"):
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# LR was configured and we have to load its new configuration
|
||||||
|
board.loadSettings()
|
||||||
|
|
||||||
|
# Start Wamp Manager
|
||||||
|
self.w = WampManager(board.wamp_config)
|
||||||
self.w.start()
|
self.w.start()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -146,7 +165,8 @@ class LightningRod(object):
|
||||||
# No zombie alert activation
|
# No zombie alert activation
|
||||||
zombie_alert = False
|
zombie_alert = False
|
||||||
LOG.info("LR is shutting down...")
|
LOG.info("LR is shutting down...")
|
||||||
self.w.stop()
|
if self.w != None:
|
||||||
|
self.w.stop()
|
||||||
Bye()
|
Bye()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error("Error closing LR")
|
LOG.error("Error closing LR")
|
||||||
|
@ -177,6 +197,46 @@ class WampManager(object):
|
||||||
LOG.info("WAMP server stopped!")
|
LOG.info("WAMP server stopped!")
|
||||||
|
|
||||||
|
|
||||||
|
def iotronic_status(board_status):
|
||||||
|
|
||||||
|
if board_status != "first_boot":
|
||||||
|
# WS ALIVE
|
||||||
|
try:
|
||||||
|
alive = asyncio.run_coroutine_threadsafe(
|
||||||
|
wamp_singleCheck(SESSION),
|
||||||
|
loop
|
||||||
|
)
|
||||||
|
alive = alive.result()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(" - Iotronic check: " + str(e))
|
||||||
|
alive = e
|
||||||
|
else:
|
||||||
|
alive = "Not connected!"
|
||||||
|
|
||||||
|
return alive
|
||||||
|
|
||||||
|
|
||||||
|
async def wamp_singleCheck(session):
|
||||||
|
try:
|
||||||
|
|
||||||
|
# LOG.debug("ALIVE sending...")
|
||||||
|
|
||||||
|
with timeoutALIVE(seconds=CONF.rpc_alive_timer, action="ws_alive"):
|
||||||
|
res = await session.call(
|
||||||
|
str(board.agent) + u'.stack4things.wamp_alive',
|
||||||
|
board_uuid=board.uuid,
|
||||||
|
board_name=board.name
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.debug("WampCheck attempt " + str(res))
|
||||||
|
|
||||||
|
except exception.ApplicationError as e:
|
||||||
|
LOG.error(" - Iotronic Connection RPC error: " + str(e))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
async def wamp_checks(session):
|
async def wamp_checks(session):
|
||||||
|
|
||||||
while (True):
|
while (True):
|
||||||
|
@ -594,6 +654,8 @@ def wampConnect(wamp_conf):
|
||||||
"\n- connected = " + str(connected)
|
"\n- connected = " + str(connected)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
board.session_id = "N/A"
|
||||||
|
|
||||||
if board.status == "operative" and reconnection is False:
|
if board.status == "operative" and reconnection is False:
|
||||||
|
|
||||||
#################
|
#################
|
||||||
|
@ -696,6 +758,58 @@ def moduleWampRegister(session, meth_list):
|
||||||
LOG.info(" --> " + str(meth[0]))
|
LOG.info(" --> " + str(meth[0]))
|
||||||
|
|
||||||
|
|
||||||
|
def singleModuleLoader(module_name, session=None):
|
||||||
|
ep = []
|
||||||
|
|
||||||
|
for ep in pkg_resources.iter_entry_points(group='s4t.modules'):
|
||||||
|
# LOG.info(" - " + str(ep))
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not ep:
|
||||||
|
|
||||||
|
LOG.info("No modules available!")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
else:
|
||||||
|
modules = extension.ExtensionManager(
|
||||||
|
namespace='s4t.modules',
|
||||||
|
# invoke_on_load=True,
|
||||||
|
# invoke_args=(session,),
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.info('Module "' + module_name + '" loading:')
|
||||||
|
|
||||||
|
for ext in modules.extensions:
|
||||||
|
|
||||||
|
if (ext.name == 'rest'):
|
||||||
|
|
||||||
|
mod = ext.plugin(board, session)
|
||||||
|
|
||||||
|
global MODULES
|
||||||
|
MODULES[mod.name] = mod
|
||||||
|
|
||||||
|
# Methods list for each module
|
||||||
|
meth_list = inspect.getmembers(mod, predicate=inspect.ismethod)
|
||||||
|
|
||||||
|
global RPC
|
||||||
|
RPC[mod.name] = meth_list
|
||||||
|
|
||||||
|
if len(meth_list) == 3:
|
||||||
|
# there are at least two methods for each module:
|
||||||
|
# "__init__" and "finalize"
|
||||||
|
|
||||||
|
LOG.info(" - No RPC to register for "
|
||||||
|
+ str(ext.name) + " module!")
|
||||||
|
|
||||||
|
else:
|
||||||
|
if(session != None):
|
||||||
|
LOG.info(" - RPC list of " + str(mod.name) + ":")
|
||||||
|
moduleWampRegister(SESSION, meth_list)
|
||||||
|
|
||||||
|
# Call the finalize procedure for each module
|
||||||
|
mod.finalize()
|
||||||
|
|
||||||
|
|
||||||
def modulesLoader(session):
|
def modulesLoader(session):
|
||||||
"""Modules loader method thorugh stevedore libraries.
|
"""Modules loader method thorugh stevedore libraries.
|
||||||
|
|
||||||
|
@ -727,36 +841,41 @@ def modulesLoader(session):
|
||||||
|
|
||||||
for ext in modules.extensions:
|
for ext in modules.extensions:
|
||||||
|
|
||||||
# LOG.debug(ext.name)
|
LOG.debug(ext.name)
|
||||||
|
|
||||||
if (ext.name == 'gpio') & (board.type == 'server'):
|
if (ext.name == 'gpio') & (board.type == 'server'):
|
||||||
LOG.info("- GPIO module disabled for 'server' devices")
|
LOG.info("- GPIO module disabled for 'server' devices")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
mod = ext.plugin(board, session)
|
|
||||||
|
|
||||||
global MODULES
|
if ext.name != "rest":
|
||||||
MODULES[mod.name] = mod
|
|
||||||
|
|
||||||
# Methods list for each module
|
mod = ext.plugin(board, session)
|
||||||
meth_list = inspect.getmembers(mod, predicate=inspect.ismethod)
|
|
||||||
|
|
||||||
global RPC
|
global MODULES
|
||||||
RPC[mod.name] = meth_list
|
MODULES[mod.name] = mod
|
||||||
|
|
||||||
if len(meth_list) == 3:
|
# Methods list for each module
|
||||||
# there are at least two methods for each module:
|
meth_list = inspect.getmembers(
|
||||||
# "__init__" and "finalize"
|
mod, predicate=inspect.ismethod
|
||||||
|
)
|
||||||
|
|
||||||
LOG.info(" - No RPC to register for "
|
global RPC
|
||||||
+ str(ext.name) + " module!")
|
RPC[mod.name] = meth_list
|
||||||
|
|
||||||
else:
|
if len(meth_list) == 3:
|
||||||
LOG.info(" - RPC list of " + str(mod.name) + ":")
|
# there are at least two methods for each module:
|
||||||
moduleWampRegister(SESSION, meth_list)
|
# "__init__" and "finalize"
|
||||||
|
|
||||||
# Call the finalize procedure for each module
|
LOG.info(" - No RPC to register for "
|
||||||
mod.finalize()
|
+ str(ext.name) + " module!")
|
||||||
|
|
||||||
|
else:
|
||||||
|
LOG.info(" - RPC list of " + str(mod.name) + ":")
|
||||||
|
moduleWampRegister(SESSION, meth_list)
|
||||||
|
|
||||||
|
# Call the finalize procedure for each module
|
||||||
|
mod.finalize()
|
||||||
|
|
||||||
LOG.info("Lightning-rod modules loaded.")
|
LOG.info("Lightning-rod modules loaded.")
|
||||||
LOG.info("\n\nListening...")
|
LOG.info("\n\nListening...")
|
||||||
|
|
|
@ -27,7 +27,6 @@ from datetime import datetime
|
||||||
|
|
||||||
from iotronic_lightningrod.config import package_path
|
from iotronic_lightningrod.config import package_path
|
||||||
from iotronic_lightningrod.lightningrod import RPC_devices
|
from iotronic_lightningrod.lightningrod import RPC_devices
|
||||||
from iotronic_lightningrod.lightningrod import SESSION
|
|
||||||
from iotronic_lightningrod.modules import Module
|
from iotronic_lightningrod.modules import Module
|
||||||
from iotronic_lightningrod.modules import utils
|
from iotronic_lightningrod.modules import utils
|
||||||
import iotronic_lightningrod.wampmessage as WM
|
import iotronic_lightningrod.wampmessage as WM
|
||||||
|
@ -88,13 +87,13 @@ class DeviceManager(Module.Module):
|
||||||
for meth in dev_meth_list:
|
for meth in dev_meth_list:
|
||||||
|
|
||||||
if (meth[0] != "__init__") & (meth[0] != "finalize"):
|
if (meth[0] != "__init__") & (meth[0] != "finalize"):
|
||||||
# LOG.info(" - " + str(meth[0]))
|
LOG.info(" - " + str(meth[0]))
|
||||||
# rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
|
# rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
|
||||||
rpc_addr = u'iotronic.' + str(board.session_id) + '.' + \
|
rpc_addr = u'iotronic.' + str(board.session_id) + '.' + \
|
||||||
board.uuid + '.' + meth[0]
|
board.uuid + '.' + meth[0]
|
||||||
|
|
||||||
# LOG.debug(" --> " + str(rpc_addr))
|
# LOG.debug(" --> " + str(rpc_addr))
|
||||||
SESSION.register(meth[1], rpc_addr)
|
self.device_session.register(meth[1], rpc_addr)
|
||||||
|
|
||||||
LOG.info(" --> " + str(meth[0]) + " registered!")
|
LOG.info(" --> " + str(meth[0]) + " registered!")
|
||||||
|
|
||||||
|
@ -164,17 +163,22 @@ class DeviceManager(Module.Module):
|
||||||
rpc_name = utils.getFuncName()
|
rpc_name = utils.getFuncName()
|
||||||
LOG.info("RPC " + rpc_name + " CALLED")
|
LOG.info("RPC " + rpc_name + " CALLED")
|
||||||
|
|
||||||
command = "ifconfig"
|
message = getIfconfig()
|
||||||
|
|
||||||
out = subprocess.Popen(
|
|
||||||
command,
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE
|
|
||||||
)
|
|
||||||
|
|
||||||
output = out.communicate()[0].decode('utf-8').strip()
|
|
||||||
|
|
||||||
message = str(output)
|
|
||||||
w_msg = WM.WampSuccess(message)
|
w_msg = WM.WampSuccess(message)
|
||||||
|
|
||||||
return w_msg.serialize()
|
return w_msg.serialize()
|
||||||
|
|
||||||
|
|
||||||
|
def getIfconfig():
|
||||||
|
|
||||||
|
command = "ifconfig"
|
||||||
|
|
||||||
|
out = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
stdout=subprocess.PIPE
|
||||||
|
)
|
||||||
|
|
||||||
|
output = str(out.communicate()[0].decode('utf-8').strip())
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
# 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
|
||||||
|
# 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 <n.peditto@gmail.com>"
|
||||||
|
|
||||||
|
from iotronic_lightningrod.lightningrod import board
|
||||||
|
from iotronic_lightningrod.lightningrod import iotronic_status
|
||||||
|
from iotronic_lightningrod.modules import device_manager
|
||||||
|
from iotronic_lightningrod.modules import Module
|
||||||
|
from iotronic_lightningrod.modules import service_manager
|
||||||
|
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from flask import Flask
|
||||||
|
from flask import redirect
|
||||||
|
from flask import render_template
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class RestManager(Module.Module):
|
||||||
|
|
||||||
|
def __init__(self, board, session=None):
|
||||||
|
super(RestManager, self).__init__("RestManager", board)
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
threading.Thread(target=self._runRestServer, args=()).start()
|
||||||
|
|
||||||
|
def restore(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _runRestServer(self):
|
||||||
|
|
||||||
|
APP_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
TEMPLATE_PATH = os.path.join(APP_PATH, 'modules/web/templates/')
|
||||||
|
STATIC_PATH = os.path.join(APP_PATH, 'modules/web/static/')
|
||||||
|
|
||||||
|
app = Flask(
|
||||||
|
__name__,
|
||||||
|
template_folder=TEMPLATE_PATH,
|
||||||
|
static_folder=STATIC_PATH,
|
||||||
|
static_url_path="/static"
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
return render_template('home.html')
|
||||||
|
|
||||||
|
@app.route('/status')
|
||||||
|
def status():
|
||||||
|
|
||||||
|
wstun_status = service_manager.wstun_status()
|
||||||
|
if wstun_status == 0:
|
||||||
|
wstun_status = "Online"
|
||||||
|
else:
|
||||||
|
wstun_status = "Offline"
|
||||||
|
|
||||||
|
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_manager.services_list())
|
||||||
|
}
|
||||||
|
|
||||||
|
return render_template('status.html', **info)
|
||||||
|
|
||||||
|
@app.route('/network')
|
||||||
|
def network():
|
||||||
|
info = {
|
||||||
|
'ifconfig': device_manager.getIfconfig().replace('\n', '<br>')
|
||||||
|
}
|
||||||
|
return render_template('network.html', **info)
|
||||||
|
|
||||||
|
def lr_config(ragent, code):
|
||||||
|
bashCommand = "lr_configure %s %s " % (code, ragent)
|
||||||
|
process = subprocess.Popen(bashCommand.split(),
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
output, error = process.communicate()
|
||||||
|
# print(output)
|
||||||
|
return
|
||||||
|
|
||||||
|
@app.route('/config', methods=['GET', 'POST'])
|
||||||
|
def config():
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
|
||||||
|
ragent = request.form['urlwagent']
|
||||||
|
code = request.form['code']
|
||||||
|
lr_config(ragent, code)
|
||||||
|
return redirect("/status", code=302)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if board.status == "first_boot":
|
||||||
|
urlwagent = request.args.get('urlwagent') or ""
|
||||||
|
code = request.args.get('code') or ""
|
||||||
|
info = {
|
||||||
|
'urlwagent': urlwagent,
|
||||||
|
'code': code
|
||||||
|
}
|
||||||
|
return render_template('config.html', **info)
|
||||||
|
else:
|
||||||
|
return redirect("/status", code=302)
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=1474, debug=False, use_reloader=False)
|
|
@ -20,10 +20,14 @@ import json
|
||||||
import os
|
import os
|
||||||
import psutil
|
import psutil
|
||||||
import pyinotify
|
import pyinotify
|
||||||
|
import queue
|
||||||
import signal
|
import signal
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
@ -35,7 +39,7 @@ from iotronic_lightningrod.modules import utils
|
||||||
import iotronic_lightningrod.wampmessage as WM
|
import iotronic_lightningrod.wampmessage as WM
|
||||||
|
|
||||||
from iotronic_lightningrod import lightningrod
|
from iotronic_lightningrod import lightningrod
|
||||||
|
from random import randint
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
@ -62,6 +66,17 @@ CONF.register_opts(wstun_opts, group=service_group)
|
||||||
s_conf_FILE = CONF.lightningrod_home + "/services.json"
|
s_conf_FILE = CONF.lightningrod_home + "/services.json"
|
||||||
|
|
||||||
|
|
||||||
|
ws_server_alive = 0
|
||||||
|
|
||||||
|
|
||||||
|
WS_MON_LIST = {}
|
||||||
|
|
||||||
|
global wstun_ip
|
||||||
|
wstun_ip = None
|
||||||
|
global wstun_port
|
||||||
|
wstun_port = None
|
||||||
|
|
||||||
|
|
||||||
class ServiceManager(Module.Module):
|
class ServiceManager(Module.Module):
|
||||||
|
|
||||||
def __init__(self, board, session):
|
def __init__(self, board, session):
|
||||||
|
@ -72,6 +87,11 @@ class ServiceManager(Module.Module):
|
||||||
self.wstun_ip = urlparse(board.wamp_config["url"])[1].split(':')[0]
|
self.wstun_ip = urlparse(board.wamp_config["url"])[1].split(':')[0]
|
||||||
self.wstun_port = "8080"
|
self.wstun_port = "8080"
|
||||||
|
|
||||||
|
global wstun_port
|
||||||
|
wstun_port = self.wstun_port
|
||||||
|
global wstun_ip
|
||||||
|
wstun_ip = self.wstun_ip
|
||||||
|
|
||||||
is_wss = False
|
is_wss = False
|
||||||
wurl_list = board.wamp_config["url"].split(':')
|
wurl_list = board.wamp_config["url"].split(':')
|
||||||
if wurl_list[0] == "wss":
|
if wurl_list[0] == "wss":
|
||||||
|
@ -112,101 +132,128 @@ class ServiceManager(Module.Module):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
if len(s_conf['services']) != 0:
|
print("WSTUN server checks:")
|
||||||
|
|
||||||
wstun_process_list = []
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(4)
|
||||||
|
global ws_server_alive
|
||||||
|
ws_server_alive = sock.connect_ex(
|
||||||
|
(self.wstun_ip, int(self.wstun_port)))
|
||||||
|
|
||||||
for p in psutil.process_iter():
|
if ws_server_alive == 0:
|
||||||
if len(p.cmdline()) != 0:
|
|
||||||
if (p.name() == "node" and "wstun" in p.cmdline()[1]):
|
|
||||||
wstun_process_list.append(p)
|
|
||||||
|
|
||||||
if len(s_conf) != 0:
|
print(" - WSTUN server is online!")
|
||||||
print("\nWSTUN processes:")
|
|
||||||
|
|
||||||
for s_uuid in s_conf['services']:
|
sock.close() # close check socket
|
||||||
|
|
||||||
service_name = \
|
if len(s_conf['services']) != 0:
|
||||||
s_conf['services'][s_uuid]['name']
|
|
||||||
service_pid = \
|
|
||||||
s_conf['services'][s_uuid]['pid']
|
|
||||||
LOG.info(" - " + service_name)
|
|
||||||
|
|
||||||
if len(wstun_process_list) != 0:
|
wstun_process_list = []
|
||||||
|
|
||||||
for wp in wstun_process_list:
|
try:
|
||||||
|
for p in psutil.process_iter():
|
||||||
|
if len(p.cmdline()) != 0:
|
||||||
|
if ((p.name() == "node") and (
|
||||||
|
"wstun" in p.cmdline()[1]
|
||||||
|
)):
|
||||||
|
wstun_process_list.append(p)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(
|
||||||
|
" --> PSUTIL [finalize]: " +
|
||||||
|
"error getting wstun processes info: " + str(e)
|
||||||
|
)
|
||||||
|
|
||||||
if service_pid == wp.pid:
|
if len(s_conf) != 0:
|
||||||
LOG.info(
|
print("\nWSTUN processes:")
|
||||||
" --> the tunnel for '" + service_name
|
|
||||||
+ "' already exists; killing..."
|
|
||||||
)
|
|
||||||
|
|
||||||
# 1. Kill wstun process (if exists)
|
for s_uuid in s_conf['services']:
|
||||||
|
|
||||||
# No zombie alert activation
|
service_name = \
|
||||||
lightningrod.zombie_alert = False
|
s_conf['services'][s_uuid]['name']
|
||||||
LOG.debug(
|
service_pid = \
|
||||||
"[WSTUN-RESTORE] - "
|
s_conf['services'][s_uuid]['pid']
|
||||||
"on-finalize zombie_alert: " +
|
LOG.info(" - " + service_name)
|
||||||
str(lightningrod.zombie_alert)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
if len(wstun_process_list) != 0:
|
||||||
os.kill(service_pid, signal.SIGINT)
|
|
||||||
print("OLD WSTUN KILLED: " + str(wp))
|
|
||||||
LOG.info(" --> service '" + service_name
|
|
||||||
+ "' with PID " + str(service_pid)
|
|
||||||
+ " was killed; "
|
|
||||||
+ "creating new one...")
|
|
||||||
|
|
||||||
except OSError:
|
for wp in wstun_process_list:
|
||||||
LOG.warning(
|
|
||||||
" - WSTUN process already killed, "
|
|
||||||
"creating new one...")
|
|
||||||
|
|
||||||
break
|
if service_pid == wp.pid:
|
||||||
|
LOG.info(
|
||||||
|
" --> the tunnel for '" + service_name
|
||||||
|
+ "' already exists; killing..."
|
||||||
|
)
|
||||||
|
|
||||||
# 2. Create the reverse tunnel
|
# 1. Kill wstun process (if exists)
|
||||||
public_port = \
|
|
||||||
s_conf['services'][s_uuid]['public_port']
|
|
||||||
local_port = \
|
|
||||||
s_conf['services'][s_uuid]['local_port']
|
|
||||||
|
|
||||||
wstun = self._startWstun(
|
# No zombie alert activation
|
||||||
public_port, local_port, event="boot"
|
lightningrod.zombie_alert = False
|
||||||
)
|
LOG.debug(
|
||||||
|
"[WSTUN-RESTORE] - "
|
||||||
|
"on-finalize zombie_alert: " +
|
||||||
|
str(lightningrod.zombie_alert)
|
||||||
|
)
|
||||||
|
|
||||||
if wstun != None:
|
try:
|
||||||
|
os.kill(service_pid, signal.SIGINT)
|
||||||
|
print("OLD WSTUN KILLED: " + str(wp))
|
||||||
|
LOG.info(
|
||||||
|
" --> service '" + service_name
|
||||||
|
+ "' with PID " + str(service_pid)
|
||||||
|
+ " was killed; "
|
||||||
|
+ "creating new one...")
|
||||||
|
|
||||||
service_pid = wstun.pid
|
except OSError:
|
||||||
|
LOG.warning(
|
||||||
|
" - WSTUN process already killed, "
|
||||||
|
"creating new one...")
|
||||||
|
|
||||||
# 3. Update services.json file
|
break
|
||||||
s_conf['services'][s_uuid]['pid'] = \
|
|
||||||
service_pid
|
|
||||||
s_conf['services'][s_uuid]['updated_at'] = \
|
|
||||||
datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
|
||||||
|
|
||||||
self._updateServiceConf(s_conf, s_uuid,
|
# 2. Create the reverse tunnel
|
||||||
output=True)
|
public_port = \
|
||||||
|
s_conf['services'][s_uuid]['public_port']
|
||||||
|
local_port = \
|
||||||
|
s_conf['services'][s_uuid]['local_port']
|
||||||
|
|
||||||
LOG.info(" --> Cloud service '" + service_name
|
wstun = self._startWstunOnBoot(
|
||||||
+ "' tunnel established.")
|
public_port, local_port, event="boot")
|
||||||
else:
|
|
||||||
message = "Error spawning " + str(service_name) \
|
if wstun != None:
|
||||||
+ " service tunnel!"
|
|
||||||
LOG.error(" - " + message)
|
service_pid = wstun.pid
|
||||||
|
|
||||||
|
# 3. Update services.json file
|
||||||
|
s_conf['services'][s_uuid]['pid'] = \
|
||||||
|
service_pid
|
||||||
|
s_conf['services'][s_uuid]['updated_at'] = \
|
||||||
|
datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||||
|
|
||||||
|
self._updateServiceConf(s_conf, s_uuid,
|
||||||
|
output=True)
|
||||||
|
|
||||||
|
LOG.info(" --> Cloud service '" + service_name
|
||||||
|
+ "' tunnel established.")
|
||||||
|
else:
|
||||||
|
message = "Error spawning " + str(service_name) \
|
||||||
|
+ " service tunnel!"
|
||||||
|
LOG.error(" - " + message)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGCHLD, self._zombie_hunter)
|
||||||
|
|
||||||
|
# Reactivate zombies monitoring
|
||||||
|
if not lightningrod.zombie_alert:
|
||||||
|
lightningrod.zombie_alert = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
LOG.info(" --> No service tunnels to establish.")
|
||||||
|
|
||||||
signal.signal(signal.SIGCHLD, self._zombie_hunter)
|
signal.signal(signal.SIGCHLD, self._zombie_hunter)
|
||||||
|
|
||||||
# Reactivate zombies monitoring
|
|
||||||
if not lightningrod.zombie_alert:
|
|
||||||
lightningrod.zombie_alert = True
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
LOG.info(" --> No service tunnels to establish.")
|
sock.close() # close check socket
|
||||||
|
print(" - WSTUN server is offline!")
|
||||||
signal.signal(signal.SIGCHLD, self._zombie_hunter)
|
LOG.error("WSTUN server is offline!")
|
||||||
|
|
||||||
def restore(self):
|
def restore(self):
|
||||||
LOG.info("Cloud service tunnels to restore:")
|
LOG.info("Cloud service tunnels to restore:")
|
||||||
|
@ -227,21 +274,30 @@ class ServiceManager(Module.Module):
|
||||||
|
|
||||||
# No zombie alert activation
|
# No zombie alert activation
|
||||||
lightningrod.zombie_alert = False
|
lightningrod.zombie_alert = False
|
||||||
LOG.debug("[WSTUN-RESTORE] - Restore zombie_alert: " + str(
|
LOG.debug(
|
||||||
lightningrod.zombie_alert))
|
"[WSTUN-RESTORE] - Restore zombie_alert: "
|
||||||
|
+ str(lightningrod.zombie_alert)
|
||||||
|
)
|
||||||
|
|
||||||
# Collect all alive WSTUN proccesses
|
# Collect all alive WSTUN proccesses
|
||||||
for p in psutil.process_iter():
|
try:
|
||||||
if (p.name() == "node"):
|
for p in psutil.process_iter():
|
||||||
if (p.status() == psutil.STATUS_ZOMBIE):
|
if (p.name() == "node"):
|
||||||
LOG.warning("WSTUN ZOMBIE: " + str(p))
|
if (p.status() == psutil.STATUS_ZOMBIE):
|
||||||
wstun_process_list.append(p)
|
LOG.warning("WSTUN ZOMBIE: " + str(p))
|
||||||
elif ("wstun" in p.cmdline()[1]):
|
wstun_process_list.append(p)
|
||||||
LOG.warning("WSTUN ALIVE: " + str(p))
|
elif ("wstun" in p.cmdline()[1]):
|
||||||
wstun_process_list.append(p)
|
LOG.warning("WSTUN ALIVE: " + str(p))
|
||||||
|
wstun_process_list.append(p)
|
||||||
|
|
||||||
psutil.Process(p.pid).kill()
|
psutil.Process(p.pid).kill()
|
||||||
LOG.warning(" --> PID " + str(p.pid) + " killed!")
|
LOG.warning(" --> PID " + str(p.pid)
|
||||||
|
+ " killed!")
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(
|
||||||
|
" --> PSUTIL [restore]: " +
|
||||||
|
"error getting wstun processes info: " + str(e)
|
||||||
|
)
|
||||||
|
|
||||||
LOG.debug("[WSTUN-RESTORE] - WSTUN processes to restore:\n"
|
LOG.debug("[WSTUN-RESTORE] - WSTUN processes to restore:\n"
|
||||||
+ str(wstun_process_list))
|
+ str(wstun_process_list))
|
||||||
|
@ -270,12 +326,21 @@ class ServiceManager(Module.Module):
|
||||||
|
|
||||||
zombie_list = []
|
zombie_list = []
|
||||||
|
|
||||||
for p in psutil.process_iter():
|
try:
|
||||||
if len(p.cmdline()) == 0:
|
|
||||||
if ((p.name() == "node") and
|
for p in psutil.process_iter():
|
||||||
(p.status() == psutil.STATUS_ZOMBIE)):
|
if len(p.cmdline()) == 0:
|
||||||
print(" - process: " + str(p))
|
if ((p.name() == "node") and
|
||||||
zombie_list.append(p.pid)
|
(p.status() == psutil.STATUS_ZOMBIE)):
|
||||||
|
print(" - process: " + str(p))
|
||||||
|
zombie_list.append(p.pid)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(
|
||||||
|
" --> PSUTIL [_zombie_hunter]: " +
|
||||||
|
"error getting wstun processes info. " +
|
||||||
|
"Please restore manually your services: " + str(e)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if len(zombie_list) == 0:
|
if len(zombie_list) == 0:
|
||||||
# print(" - no action required.")
|
# print(" - no action required.")
|
||||||
|
@ -404,15 +469,13 @@ class ServiceManager(Module.Module):
|
||||||
:return: JSON Services configuration
|
:return: JSON Services configuration
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
with open(s_conf_FILE) as settings:
|
with open(s_conf_FILE) as settings:
|
||||||
s_conf = json.load(settings)
|
s_conf = json.load(settings)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
LOG.error(" --> Parsing error in "
|
LOG.error(" --> Parsing error in " + s_conf_FILE + ": " + str(err))
|
||||||
+ s_conf_FILE + ": " + str(err))
|
|
||||||
|
|
||||||
if os.path.isfile(s_conf_FILE):
|
if os.path.isfile(s_conf_FILE):
|
||||||
|
|
||||||
|
@ -437,7 +500,7 @@ class ServiceManager(Module.Module):
|
||||||
|
|
||||||
return s_conf
|
return s_conf
|
||||||
|
|
||||||
def _wstunMon(self, wstun):
|
def _wstunMon(self, wstun, local_port):
|
||||||
|
|
||||||
wfd_check = True
|
wfd_check = True
|
||||||
|
|
||||||
|
@ -503,6 +566,8 @@ class ServiceManager(Module.Module):
|
||||||
event_notifier = pyinotify.ThreadedNotifier(
|
event_notifier = pyinotify.ThreadedNotifier(
|
||||||
watch_manager, EventProcessor()
|
watch_manager, EventProcessor()
|
||||||
)
|
)
|
||||||
|
event_notifier.setName("TN-" + str(local_port))
|
||||||
|
WS_MON_LIST[str(local_port)] = event_notifier
|
||||||
|
|
||||||
watch_this = os.path.abspath(
|
watch_this = os.path.abspath(
|
||||||
"/proc/" + str(wstun.pid) + "/fd/" + str(wstun_fd)
|
"/proc/" + str(wstun.pid) + "/fd/" + str(wstun_fd)
|
||||||
|
@ -512,13 +577,27 @@ class ServiceManager(Module.Module):
|
||||||
|
|
||||||
def _startWstun(self, public_port, local_port, event="no-set"):
|
def _startWstun(self, public_port, local_port, event="no-set"):
|
||||||
|
|
||||||
import socket
|
count_ws = 0
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(4)
|
sock.settimeout(4)
|
||||||
result = sock.connect_ex((self.wstun_ip, int(self.wstun_port)))
|
|
||||||
|
|
||||||
if result == 0:
|
global ws_server_alive
|
||||||
|
ws_server_alive = sock.connect_ex(
|
||||||
|
(self.wstun_ip, int(self.wstun_port))
|
||||||
|
)
|
||||||
|
|
||||||
|
while(ws_server_alive != 0 and count_ws < 5):
|
||||||
|
count_ws = count_ws + 1
|
||||||
|
LOG.warning(
|
||||||
|
"WSTUN server is offline! Retry " + str(count_ws) + "/5..."
|
||||||
|
)
|
||||||
|
time.sleep(randint(3, 6))
|
||||||
|
global ws_server_alive
|
||||||
|
ws_server_alive = sock.connect_ex(
|
||||||
|
(self.wstun_ip, int(self.wstun_port))
|
||||||
|
)
|
||||||
|
|
||||||
|
if ws_server_alive == 0:
|
||||||
|
|
||||||
sock.close() # close check socket
|
sock.close() # close check socket
|
||||||
|
|
||||||
|
@ -542,12 +621,22 @@ class ServiceManager(Module.Module):
|
||||||
# WSTUN MON
|
# WSTUN MON
|
||||||
# #############################################################
|
# #############################################################
|
||||||
|
|
||||||
Thread(
|
try:
|
||||||
target=self._wstunMon,
|
if event != "enable":
|
||||||
args=(wstun,)
|
WS_MON_LIST[str(local_port)].stop()
|
||||||
).start()
|
except Exception as err:
|
||||||
|
LOG.error("Error stopping WSTUN monitor: " + str(err))
|
||||||
|
|
||||||
# self._wstunMon(wstun)
|
wsmon = Thread(
|
||||||
|
target=self._wstunMon,
|
||||||
|
name="THR-" + str(local_port),
|
||||||
|
args=(wstun, local_port, )
|
||||||
|
)
|
||||||
|
|
||||||
|
wsmon.start()
|
||||||
|
|
||||||
|
# print(threading.enumerate())
|
||||||
|
print(WS_MON_LIST)
|
||||||
|
|
||||||
# #############################################################
|
# #############################################################
|
||||||
|
|
||||||
|
@ -561,25 +650,89 @@ class ServiceManager(Module.Module):
|
||||||
|
|
||||||
return wstun
|
return wstun
|
||||||
|
|
||||||
|
def _startWstunOnBoot(self, public_port, local_port, event="no-set"):
|
||||||
|
|
||||||
|
opt_reverse = "-r" + str(public_port) + ":127.0.0.1:" + str(
|
||||||
|
local_port)
|
||||||
|
|
||||||
|
try:
|
||||||
|
wstun = subprocess.Popen(
|
||||||
|
[CONF.services.wstun_bin, opt_reverse, self.wstun_url],
|
||||||
|
stdout=subprocess.PIPE
|
||||||
|
)
|
||||||
|
|
||||||
|
if (event != "boot"):
|
||||||
|
print("WSTUN start event:")
|
||||||
|
|
||||||
|
cmd_print = 'WSTUN exec: ' + str(CONF.services.wstun_bin) \
|
||||||
|
+ opt_reverse + ' ' + self.wstun_url
|
||||||
|
print(" - " + str(cmd_print))
|
||||||
|
LOG.debug(cmd_print)
|
||||||
|
|
||||||
|
# WSTUN MON
|
||||||
|
# #############################################################
|
||||||
|
|
||||||
|
wsmon = Thread(
|
||||||
|
target=self._wstunMon,
|
||||||
|
name="THR-" + str(local_port),
|
||||||
|
args=(wstun, local_port,)
|
||||||
|
)
|
||||||
|
|
||||||
|
wsmon.start()
|
||||||
|
|
||||||
|
# #############################################################
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
LOG.error("Error spawning WSTUN process: " + str(err))
|
||||||
|
wstun = None
|
||||||
|
|
||||||
|
return wstun
|
||||||
|
|
||||||
|
async def ServicesStatus(self):
|
||||||
|
rpc_name = utils.getFuncName()
|
||||||
|
LOG.info("RPC " + rpc_name + " CALLED")
|
||||||
|
|
||||||
|
thr_list = str(threading.enumerate())
|
||||||
|
# print(WS_MON_LIST)
|
||||||
|
print(thr_list + "\n" + str(WS_MON_LIST))
|
||||||
|
|
||||||
|
w_msg = WM.WampSuccess(thr_list)
|
||||||
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
def _updateServiceConf(self, s_conf, s_uuid, output=True):
|
def _updateServiceConf(self, s_conf, s_uuid, output=True):
|
||||||
|
|
||||||
# Apply the changes to services.json
|
if s_conf == "":
|
||||||
with open(s_conf_FILE, 'w') as f:
|
|
||||||
json.dump(s_conf, f, indent=4)
|
|
||||||
|
|
||||||
if output:
|
LOG.error(" - ERROR new services.json content is empty: " +
|
||||||
LOG.info(" - service updated:\n" + json.dumps(
|
"Restoring backup.")
|
||||||
s_conf['services'][s_uuid],
|
|
||||||
indent=4,
|
|
||||||
sort_keys=True
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
LOG.info(" - services.json file updated!")
|
|
||||||
|
|
||||||
# Backup json file before update
|
# Restore backup json file on error
|
||||||
os.system(
|
os.system(
|
||||||
'cp ' + s_conf_FILE + ' ' + s_conf_FILE + '.bkp'
|
'cp ' + s_conf_FILE + '.bkp ' + s_conf_FILE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# Apply the changes to services.json
|
||||||
|
with open(s_conf_FILE, 'w') as f:
|
||||||
|
json.dump(s_conf, f, indent=4)
|
||||||
|
|
||||||
|
print(s_conf)
|
||||||
|
|
||||||
|
if output:
|
||||||
|
LOG.info(" - service updated:\n" + json.dumps(
|
||||||
|
s_conf['services'][s_uuid],
|
||||||
|
indent=4,
|
||||||
|
sort_keys=True
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
LOG.info(" - services.json file updated!")
|
||||||
|
|
||||||
|
# Backup json file before update
|
||||||
|
os.system(
|
||||||
|
'cp ' + s_conf_FILE + ' ' + s_conf_FILE + '.bkp'
|
||||||
|
)
|
||||||
|
|
||||||
async def ServiceEnable(self, service, public_port):
|
async def ServiceEnable(self, service, public_port):
|
||||||
|
|
||||||
|
@ -647,8 +800,8 @@ class ServiceManager(Module.Module):
|
||||||
w_msg = WM.WampSuccess(message)
|
w_msg = WM.WampSuccess(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = "Error spawning " + str(service_name) \
|
message = "Error spawning '" + str(service_name) \
|
||||||
+ " service tunnel!"
|
+ "' service tunnel!"
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = WM.WampError(message)
|
w_msg = WM.WampError(message)
|
||||||
|
|
||||||
|
@ -822,8 +975,7 @@ class ServiceManager(Module.Module):
|
||||||
s_conf['services'][s_uuid]['updated_at'] = \
|
s_conf['services'][s_uuid]['updated_at'] = \
|
||||||
datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||||
|
|
||||||
self._updateServiceConf(s_conf, s_uuid,
|
self._updateServiceConf(s_conf, s_uuid, output=True)
|
||||||
output=True)
|
|
||||||
|
|
||||||
message = "service " + str(service_name) \
|
message = "service " + str(service_name) \
|
||||||
+ " restored on port " \
|
+ " restored on port " \
|
||||||
|
@ -898,3 +1050,41 @@ class ServiceManager(Module.Module):
|
||||||
w_msg = WM.WampError(message)
|
w_msg = WM.WampError(message)
|
||||||
|
|
||||||
return w_msg.serialize()
|
return w_msg.serialize()
|
||||||
|
|
||||||
|
|
||||||
|
def services_list():
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
s_list = ""
|
||||||
|
|
||||||
|
with open(s_conf_FILE) as settings:
|
||||||
|
s_conf = json.load(settings)
|
||||||
|
|
||||||
|
for s_uuid in s_conf['services']:
|
||||||
|
s_service = str(s_conf['services'][s_uuid]['name']) \
|
||||||
|
+ " - " + str(s_conf['services'][s_uuid]['public_port']) \
|
||||||
|
+ " - " + str(s_conf['services'][s_uuid]['local_port'])
|
||||||
|
s_list = s_list + "<li>" + s_service + "</li>"
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
LOG.error("Error getting services list: " + str(err))
|
||||||
|
s_list = str(err)
|
||||||
|
|
||||||
|
return s_list
|
||||||
|
|
||||||
|
|
||||||
|
def wstun_status():
|
||||||
|
|
||||||
|
if(wstun_ip != None) and (wstun_port != None):
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(4)
|
||||||
|
global ws_server_alive
|
||||||
|
ws_server_alive = sock.connect_ex((wstun_ip, int(wstun_port)))
|
||||||
|
sock.close() # close check socket
|
||||||
|
|
||||||
|
else:
|
||||||
|
ws_server_alive = "N/A"
|
||||||
|
|
||||||
|
return ws_server_alive
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,330 @@
|
||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v4.1.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2018 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2018 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||||
|
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||||
|
*/
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: 1.15;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
-ms-overflow-style: scrollbar;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-ms-viewport {
|
||||||
|
width: device-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #212529;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
[tabindex="-1"]:focus {
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
height: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title],
|
||||||
|
abbr[data-original-title] {
|
||||||
|
text-decoration: underline;
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: transparent;
|
||||||
|
-webkit-text-decoration-skip: objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([tabindex]) {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([tabindex]):focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
-ms-overflow-style: scrollbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: left;
|
||||||
|
caption-side: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
outline: 1px dotted;
|
||||||
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
html [type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"],
|
||||||
|
input[type="checkbox"] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="date"],
|
||||||
|
input[type="time"],
|
||||||
|
input[type="datetime-local"],
|
||||||
|
input[type="month"] {
|
||||||
|
-webkit-appearance: listbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: inherit;
|
||||||
|
color: inherit;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,8 @@
|
||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v4.1.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2018 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2018 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||||
|
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||||
|
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,24 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>{% block title %}{% endblock %} - LR</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<nav class="navbar navbar-expand-sm bg-dark navbar-dark justify-content-center">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="navbar-brand" href="/">Home</a>
|
||||||
|
<a class="navbar-brand" href="/status">Status</a>
|
||||||
|
<a class="navbar-brand" href="/network">Network</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<header class="text-center" style="margin-top: 20px;">
|
||||||
|
{% block header %}{% endblock %}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h3>{% block title %}First configuration{% endblock %}</h3>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="form-group">
|
||||||
|
<form method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="urlwagent">Registration Agent: </label>
|
||||||
|
<input class="form-control" name="urlwagent" id="urlwagent" value="{{ urlwagent }}" required></div>
|
||||||
|
<div class="form-group"></div>
|
||||||
|
<label for="code">Registration Code:</label>
|
||||||
|
<input class="form-control" name="code" id="code" value="{{ code }}" required></div>
|
||||||
|
<input class="btn btn-success" type="submit" value="CONFIGURE">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h1>{% block title %}Welcome in Lightning-rod{% endblock %}</h1>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h1>{% block title %}Networking{% endblock %}</h1>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="jumbotron">
|
||||||
|
<h4> Ifconfig </h4>
|
||||||
|
<pre>
|
||||||
|
{% autoescape false %}
|
||||||
|
{{ ifconfig }}
|
||||||
|
{% endautoescape %}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h1>{% block title %}Status{% endblock %}</h1>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="jumbotron">
|
||||||
|
<h2>Board info @ {{ timestamp }}</h2>
|
||||||
|
<br>
|
||||||
|
<h4> Iotronic </h4>
|
||||||
|
<li> Iotronic connection status: {{iotronic_status}}</li>
|
||||||
|
<li>Name: {{ board_name }} </li>
|
||||||
|
<li>UUID: {{ board_id }} </li>
|
||||||
|
<li>Registartion status: {{ board_reg_status }}</li>
|
||||||
|
<li>WAMP Agent: {{ wagent }} </li>
|
||||||
|
<li>Session ID: {{ session_id }} </li>
|
||||||
|
<br>
|
||||||
|
<h4> WSTUN </h4>
|
||||||
|
<li>Status: {{ wstun_status }} </li>
|
||||||
|
<li>Services: <br>
|
||||||
|
<ul>
|
||||||
|
{% autoescape false %}
|
||||||
|
{{service_list}}
|
||||||
|
{% endautoescape %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -17,7 +17,6 @@ __author__ = "Nicola Peditto <n.peditto@gmail.com>"
|
||||||
|
|
||||||
from iotronic_lightningrod.config import package_path
|
from iotronic_lightningrod.config import package_path
|
||||||
from iotronic_lightningrod.lightningrod import RPC_proxies
|
from iotronic_lightningrod.lightningrod import RPC_proxies
|
||||||
from iotronic_lightningrod.lightningrod import SESSION
|
|
||||||
from iotronic_lightningrod.modules import Module
|
from iotronic_lightningrod.modules import Module
|
||||||
from iotronic_lightningrod.modules import utils
|
from iotronic_lightningrod.modules import utils
|
||||||
import iotronic_lightningrod.wampmessage as WM
|
import iotronic_lightningrod.wampmessage as WM
|
||||||
|
@ -58,6 +57,8 @@ class WebServiceManager(Module.Module):
|
||||||
|
|
||||||
LOG.info(" - Proxy used: " + CONF.webservices.proxy.upper())
|
LOG.info(" - Proxy used: " + CONF.webservices.proxy.upper())
|
||||||
|
|
||||||
|
self.session = session
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proxy_type = CONF.webservices.proxy
|
proxy_type = CONF.webservices.proxy
|
||||||
path = package_path + "/modules/proxies/" + proxy_type + ".py"
|
path = package_path + "/modules/proxies/" + proxy_type + ".py"
|
||||||
|
@ -168,11 +169,11 @@ class WebServiceManager(Module.Module):
|
||||||
|
|
||||||
# LOG.debug(" --> " + str(rpc_addr))
|
# LOG.debug(" --> " + str(rpc_addr))
|
||||||
if not meth[0].startswith('_'):
|
if not meth[0].startswith('_'):
|
||||||
SESSION.register(meth[1], rpc_addr)
|
self.session.register(meth[1], rpc_addr)
|
||||||
LOG.info(" --> " + str(meth[0]))
|
LOG.info(" --> " + str(meth[0]))
|
||||||
|
|
||||||
async def ExposeWebservice(self, board_dns, service_dns,
|
async def ExposeWebservice(self,
|
||||||
local_port, dns_list):
|
board_dns, service_dns, local_port, dns_list):
|
||||||
|
|
||||||
rpc_name = utils.getFuncName()
|
rpc_name = utils.getFuncName()
|
||||||
LOG.info("RPC " + rpc_name + " CALLED")
|
LOG.info("RPC " + rpc_name + " CALLED")
|
||||||
|
|
|
@ -11,3 +11,4 @@ oslo.config>=5.1.0 # Apache-2.0
|
||||||
oslo.log>=3.36.0 # Apache-2.0
|
oslo.log>=3.36.0 # Apache-2.0
|
||||||
pyinotify>=0.9.6;sys_platform!='win32' and sys_platform!='darwin' and sys_platform!='sunos5' # MIT
|
pyinotify>=0.9.6;sys_platform!='win32' and sys_platform!='darwin' and sys_platform!='sunos5' # MIT
|
||||||
pyOpenSSL>=16.2.0 # Apache-2.0
|
pyOpenSSL>=16.2.0 # Apache-2.0
|
||||||
|
Flask!=0.11,>=1.0.2 # BSD
|
||||||
|
|
|
@ -67,6 +67,7 @@ s4t.modules =
|
||||||
service = iotronic_lightningrod.modules.service_manager:ServiceManager
|
service = iotronic_lightningrod.modules.service_manager:ServiceManager
|
||||||
network = iotronic_lightningrod.modules.network_manager:NetworkManager
|
network = iotronic_lightningrod.modules.network_manager:NetworkManager
|
||||||
webservice = iotronic_lightningrod.modules.webservice_manager:WebServiceManager
|
webservice = iotronic_lightningrod.modules.webservice_manager:WebServiceManager
|
||||||
|
rest = iotronic_lightningrod.modules.rest_manager:RestManager
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
build_scripts =
|
build_scripts =
|
||||||
|
|
Loading…
Reference in New Issue