979 lines
34 KiB
Python
979 lines
34 KiB
Python
# 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.
|
|
|
|
from __future__ import absolute_import
|
|
|
|
__author__ = "Nicola Peditto <n.peditto@gmail.com>"
|
|
|
|
from datetime import datetime
|
|
import importlib as imp
|
|
import json
|
|
import os
|
|
import queue
|
|
import shutil
|
|
import threading
|
|
import time
|
|
import platform
|
|
|
|
|
|
from iotronic_lightningrod.common import utils
|
|
from iotronic_lightningrod.lightningrod import wampNotify
|
|
from iotronic_lightningrod.modules import Module
|
|
from iotronic_lightningrod.modules.plugins import PluginSerializer
|
|
import iotronic_lightningrod.wampmessage as WM
|
|
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
CONF = cfg.CONF
|
|
PLUGINS_THRS = {}
|
|
PLUGINS_CONF_FILE = CONF.lightningrod_home + "/plugins.json"
|
|
|
|
|
|
class PluginManager(Module.Module):
|
|
|
|
"""Plugin module to manage board plugins.
|
|
|
|
"""
|
|
|
|
def __init__(self, board, session):
|
|
"""Init function for PluginManager module.
|
|
|
|
:param board:
|
|
:param session:
|
|
|
|
"""
|
|
|
|
# Module declaration
|
|
super(PluginManager, self).__init__("PluginManager", board)
|
|
|
|
# Creation of plugins.json configuration file
|
|
self._createPluginsConf()
|
|
|
|
def finalize(self):
|
|
"""Function called at the end of module loading.
|
|
|
|
This function in this module reloads
|
|
the enabled (asynchronous) plugins at boot.
|
|
|
|
"""
|
|
|
|
# Reboot boot enabled plugins
|
|
self._rebootOnBootPlugins()
|
|
|
|
def restore(self):
|
|
pass
|
|
|
|
def _loadPluginsConf(self):
|
|
"""Load plugins.json JSON configuration.
|
|
|
|
:return: JSON Plugins configuration
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
with open(PLUGINS_CONF_FILE) as settings:
|
|
plugins_conf = json.load(settings)
|
|
|
|
except Exception as err:
|
|
LOG.error(
|
|
"Parsing error in " + PLUGINS_CONF_FILE + ": " + str(err))
|
|
plugins_conf = None
|
|
|
|
return plugins_conf
|
|
|
|
def _getEnabledPlugins(self):
|
|
"""This function gets the list of all asynchronous plugins.
|
|
|
|
We considered only those plugins with 'callable' flag set to False
|
|
and 'onboot' flag set to True.
|
|
|
|
:return: enabledPlugins List
|
|
|
|
"""
|
|
enabledPlugins = []
|
|
plugins_conf = self._loadPluginsConf()
|
|
|
|
for plugin in plugins_conf['plugins']:
|
|
|
|
if plugins_conf['plugins'][plugin]['callable'] is False:
|
|
|
|
if plugins_conf['plugins'][plugin]['onboot'] is True:
|
|
|
|
if plugins_conf['plugins'][plugin]['status'] == \
|
|
"operative":
|
|
enabledPlugins.append(plugin)
|
|
|
|
if len(enabledPlugins) != 0:
|
|
LOG.info(" - Enabled plugins list: " + str(enabledPlugins))
|
|
|
|
return enabledPlugins
|
|
|
|
def _rebootOnBootPlugins(self):
|
|
"""Reboot at boot each enabled asynchronous plugin
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
f_name = utils.getFuncName()
|
|
LOG.info("Rebooting enabled plugins:")
|
|
|
|
enabledPlugins = self._getEnabledPlugins()
|
|
|
|
if enabledPlugins.__len__() == 0:
|
|
|
|
message = "No plugin to reboot!"
|
|
LOG.info(" - " + message)
|
|
|
|
else:
|
|
|
|
for plugin_uuid in enabledPlugins:
|
|
|
|
plugins_conf = self._loadPluginsConf()
|
|
plugin_name = plugins_conf['plugins'][plugin_uuid]['name']
|
|
|
|
try:
|
|
|
|
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!")
|
|
|
|
else:
|
|
|
|
LOG.info(" - Rebooting plugin " + plugin_uuid)
|
|
|
|
plugin_home = \
|
|
CONF.lightningrod_home + "/plugins/" + plugin_uuid
|
|
plugin_filename = \
|
|
plugin_home + "/" + plugin_uuid + ".py"
|
|
plugin_params_file = \
|
|
plugin_home + "/" + plugin_uuid + ".json"
|
|
|
|
if os.path.exists(plugin_filename):
|
|
|
|
task = imp.machinery.SourceFileLoader(
|
|
"plugin",
|
|
plugin_filename
|
|
).load_module()
|
|
|
|
if os.path.exists(plugin_params_file):
|
|
|
|
with open(plugin_params_file) as conf:
|
|
plugin_params = json.load(conf)
|
|
|
|
worker = task.Worker(
|
|
plugin_uuid,
|
|
plugin_name,
|
|
q_result=None,
|
|
params=plugin_params
|
|
)
|
|
|
|
PLUGINS_THRS[plugin_uuid] = worker
|
|
LOG.info(" - Starting plugin " + str(worker))
|
|
|
|
worker.start()
|
|
|
|
else:
|
|
message = "ERROR " + plugin_params_file \
|
|
+ " does not exist!"
|
|
|
|
LOG.error(" - "
|
|
+ worker.complete(f_name, message))
|
|
|
|
else:
|
|
message = "ERROR " \
|
|
+ plugin_filename + " does not exist!"
|
|
|
|
LOG.error(
|
|
" - " + worker.complete(f_name, message))
|
|
|
|
message = "rebooted!"
|
|
|
|
LOG.info(" - " + worker.complete(f_name, message))
|
|
|
|
except Exception as err:
|
|
message = "Error rebooting plugin " \
|
|
+ plugin_uuid + ": " + str(err)
|
|
LOG.error(" - " + message)
|
|
|
|
def _createPluginsConf(self):
|
|
"""Create plugins.json file if it does not exist.
|
|
|
|
"""
|
|
if not os.path.exists(PLUGINS_CONF_FILE):
|
|
LOG.debug("plugins.json does not exist: creating...")
|
|
plugins_conf = {'plugins': {}}
|
|
with open(PLUGINS_CONF_FILE, 'w') as f:
|
|
json.dump(plugins_conf, f, indent=4)
|
|
|
|
# SC
|
|
async def PluginInject(self, req, plugin, onboot, parameters=None):
|
|
"""Plugin injection procedure into the board:
|
|
|
|
1. get Plugin files
|
|
2. deserialize files
|
|
3. store files
|
|
|
|
:param plugin:
|
|
:param onboot:
|
|
:return:
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
|
|
plugin_uuid = plugin['uuid']
|
|
plugin_name = plugin['name']
|
|
code = plugin['code']
|
|
callable = plugin['callable']
|
|
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: "
|
|
+ str(req_id) + "] for plugin '"
|
|
+ plugin_name + "' (" + plugin_uuid + "):")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
|
|
def Inject():
|
|
|
|
# Deserialize the plugin code received
|
|
ser = PluginSerializer.ObjectSerializer()
|
|
loaded = ser.deserialize_entity(code)
|
|
# LOG.debug("- plugin loaded code:\n" + loaded)
|
|
|
|
plugin_path = CONF.lightningrod_home \
|
|
+ "/plugins/" + plugin_uuid + "/"
|
|
plugin_filename = plugin_path + plugin_uuid + ".py"
|
|
|
|
# Plugin folder creation if does not exist
|
|
if not os.path.exists(plugin_path):
|
|
os.makedirs(plugin_path)
|
|
|
|
# Plugin code file creation
|
|
with open(plugin_filename, "w") as pluginfile:
|
|
pluginfile.write(loaded)
|
|
|
|
# Load plugins.json configuration file
|
|
plugins_conf = self._loadPluginsConf()
|
|
|
|
# LOG.debug("Plugin setup:\n"
|
|
# + json.dumps(plugin, indent=4, sort_keys=True))
|
|
|
|
# Save plugin settings in plugins.json
|
|
if plugin_uuid not in plugins_conf['plugins']:
|
|
|
|
# It is a new plugin
|
|
plugins_conf['plugins'][plugin_uuid] = {}
|
|
plugins_conf['plugins'][plugin_uuid]['name'] = plugin_name
|
|
plugins_conf['plugins'][plugin_uuid]['onboot'] = onboot
|
|
plugins_conf['plugins'][plugin_uuid]['callable'] = callable
|
|
plugins_conf['plugins'][plugin_uuid]['injected_at'] = \
|
|
datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
|
plugins_conf['plugins'][plugin_uuid]['updated_at'] = ""
|
|
plugins_conf['plugins'][plugin_uuid]['status'] = "injected"
|
|
|
|
LOG.info(" - Plugin '" + plugin_name + "' created!")
|
|
message = rpc_name + " result: INJECTED"
|
|
|
|
else:
|
|
# The plugin was already injected and we are updating it
|
|
plugins_conf['plugins'][plugin_uuid]['name'] = plugin_name
|
|
plugins_conf['plugins'][plugin_uuid]['onboot'] = onboot
|
|
plugins_conf['plugins'][plugin_uuid]['callable'] = callable
|
|
plugins_conf['plugins'][plugin_uuid]['updated_at'] = \
|
|
datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
|
plugins_conf['plugins'][plugin_uuid]['status'] = "updated"
|
|
|
|
LOG.info("Plugin '" + plugin_name
|
|
+ "' (" + str(plugin_uuid) + ") updated!")
|
|
message = rpc_name + " result: UPDATED"
|
|
|
|
LOG.info(" - Plugin setup:\n" + json.dumps(
|
|
plugins_conf['plugins'][plugin_uuid],
|
|
indent=4,
|
|
sort_keys=True
|
|
))
|
|
|
|
# Apply the changes to plugins.json
|
|
with open(PLUGINS_CONF_FILE, 'w') as f:
|
|
json.dump(plugins_conf, f, indent=4)
|
|
|
|
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=Inject).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 = Inject()
|
|
|
|
except Exception as err:
|
|
message = "Plugin injection error: " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
LOG.warning(message)
|
|
|
|
return w_msg.serialize()
|
|
|
|
return w_msg.serialize()
|
|
|
|
# SC
|
|
async def PluginStart(self, req, plugin_uuid, parameters=None):
|
|
"""To start an asynchronous plugin;
|
|
|
|
the plugin will run until the PluginStop is called.
|
|
|
|
:param plugin_uuid:
|
|
:param parameters:
|
|
:return: return a response to RPC request
|
|
|
|
"""
|
|
|
|
try:
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: " + str(req_id)
|
|
+ "] for '" + plugin_uuid + "' plugin:")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
|
|
plugins_conf = self._loadPluginsConf()
|
|
|
|
if plugin_uuid in plugins_conf['plugins']:
|
|
|
|
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 worker_alive:
|
|
|
|
message = "ALREADY STARTED!"
|
|
LOG.warning(" - Plugin "
|
|
+ plugin_uuid + " already started!")
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
else:
|
|
|
|
plugin_home = \
|
|
CONF.lightningrod_home + "/plugins/" + plugin_uuid
|
|
plugin_filename = \
|
|
plugin_home + "/" + plugin_uuid + ".py"
|
|
plugin_params_file = \
|
|
plugin_home + "/" + plugin_uuid + ".json"
|
|
|
|
# Import plugin (as python module)
|
|
if os.path.exists(plugin_filename):
|
|
|
|
task = imp.machinery.SourceFileLoader(
|
|
"plugin",
|
|
plugin_filename
|
|
).load_module()
|
|
|
|
LOG.info(" - Plugin '" + plugin_uuid + "' imported!")
|
|
|
|
# Store input parameters of the plugin
|
|
if parameters is not None:
|
|
|
|
with open(plugin_params_file, 'w') as f:
|
|
json.dump(parameters, f, indent=4)
|
|
|
|
with open(plugin_params_file) as conf:
|
|
plugin_params = json.load(conf)
|
|
|
|
LOG.info(" - plugin with parameters:")
|
|
LOG.info(" " + str(plugin_params))
|
|
|
|
else:
|
|
plugin_params = None
|
|
|
|
worker = task.Worker(
|
|
plugin_uuid,
|
|
plugin_name,
|
|
params=plugin_params
|
|
)
|
|
|
|
PLUGINS_THRS[plugin_uuid] = worker
|
|
LOG.debug(" - Starting plugin " + str(worker))
|
|
|
|
worker.start()
|
|
|
|
# Apply the changes to plugins.json
|
|
with open(PLUGINS_CONF_FILE, 'w') as f:
|
|
plugins_conf['plugins'][plugin_uuid]['status'] = \
|
|
'operative'
|
|
json.dump(plugins_conf, f, indent=4)
|
|
|
|
response = "STARTED"
|
|
LOG.info(" - " + worker.complete(rpc_name, response))
|
|
w_msg = WM.WampSuccess(msg=response, req_id=req_id)
|
|
|
|
else:
|
|
message = \
|
|
rpc_name + " - ERROR " \
|
|
+ plugin_filename + " does not exist!"
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
else:
|
|
message = "Plugin " + plugin_uuid \
|
|
+ " does not exist in this board!"
|
|
LOG.warning(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
except Exception as err:
|
|
message = \
|
|
rpc_name + " - ERROR - plugin (" + plugin_uuid + ") - " \
|
|
+ str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=str(err), req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
return w_msg.serialize()
|
|
|
|
# SC
|
|
async def PluginStop(self, req, plugin_uuid, parameters=None):
|
|
"""To stop an asynchronous plugin
|
|
|
|
:param plugin_uuid: ID of plufin to stop
|
|
:param parameters: JSON OPTIONAL stop parameters; 'delay' in seconds
|
|
:return: return a response to RPC request
|
|
|
|
"""
|
|
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: "
|
|
+ str(req_id) + "] for '" + plugin_uuid + "' plugin:")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
if 'delay' in parameters:
|
|
delay = parameters['delay']
|
|
LOG.info(" --> stop delay: " + str(delay))
|
|
|
|
try:
|
|
|
|
if plugin_uuid in PLUGINS_THRS:
|
|
|
|
worker_alive = False
|
|
if (plugin_uuid in PLUGINS_THRS):
|
|
worker = PLUGINS_THRS[plugin_uuid]
|
|
LOG.debug(" - Stopping plugin " + str(worker))
|
|
|
|
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)
|
|
|
|
worker.stop()
|
|
|
|
del PLUGINS_THRS[plugin_uuid]
|
|
|
|
message = "STOPPED"
|
|
LOG.info(" - " + worker.complete(rpc_name, message))
|
|
w_msg = WM.WampSuccess(msg=message, req_id=req_id)
|
|
|
|
else:
|
|
message = \
|
|
rpc_name \
|
|
+ " - ERROR - plugin (" + plugin_uuid \
|
|
+ ") is instantiated but is not running anymore!"
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
else:
|
|
message = \
|
|
rpc_name + " - WARNING " \
|
|
+ plugin_uuid + " is not running!"
|
|
LOG.warning(" - " + message)
|
|
w_msg = WM.WampWarning(msg=message, req_id=req_id)
|
|
|
|
except Exception as err:
|
|
message = \
|
|
rpc_name \
|
|
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=str(err), req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
return w_msg.serialize()
|
|
|
|
# SC
|
|
async def PluginCall(self, req, plugin_uuid, parameters=None):
|
|
"""To execute a synchronous plugin into the board
|
|
|
|
:param plugin_uuid:
|
|
:param parameters:
|
|
:return: return a response to RPC request
|
|
|
|
"""
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: "
|
|
+ str(req_id) + "] for '" + plugin_uuid + "' plugin:")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
|
|
try:
|
|
|
|
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)
|
|
w_msg = WM.WampWarning(msg=message, req_id=req_id)
|
|
|
|
else:
|
|
|
|
plugin_home = CONF.lightningrod_home \
|
|
+ "/plugins/" + plugin_uuid
|
|
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
|
|
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
|
|
|
|
plugins_conf = self._loadPluginsConf()
|
|
plugin_name = plugins_conf['plugins'][plugin_uuid]['name']
|
|
|
|
# Import plugin (as python module)
|
|
if os.path.exists(plugin_filename):
|
|
|
|
try:
|
|
|
|
# task = imp.load_source("plugin", plugin_filename)
|
|
task = imp.machinery.SourceFileLoader(
|
|
"plugin",
|
|
plugin_filename
|
|
).load_module()
|
|
|
|
LOG.info(" - Plugin " + plugin_uuid + " imported!")
|
|
|
|
q_result = queue.Queue()
|
|
|
|
except Exception as err:
|
|
message = "Error importing plugin " \
|
|
+ plugin_filename + ": " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=str(err), req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
try:
|
|
|
|
# Store input parameters of the plugin
|
|
if parameters is not None:
|
|
with open(plugin_params_file, 'w') as f:
|
|
json.dump(parameters, f, indent=4)
|
|
|
|
with open(plugin_params_file) as conf:
|
|
plugin_params = json.load(conf)
|
|
|
|
LOG.info(" - Plugin configuration:\n"
|
|
+ str(plugin_params))
|
|
|
|
else:
|
|
plugin_params = None
|
|
|
|
worker = task.Worker(
|
|
plugin_uuid,
|
|
plugin_name,
|
|
q_result=q_result,
|
|
params=plugin_params
|
|
)
|
|
|
|
PLUGINS_THRS[plugin_uuid] = worker
|
|
LOG.debug(" - Executing plugin " + str(worker))
|
|
|
|
worker.start()
|
|
|
|
while q_result.empty():
|
|
pass
|
|
|
|
response = q_result.get()
|
|
|
|
LOG.info(" - " + worker.complete(rpc_name, response))
|
|
w_msg = WM.WampSuccess(msg=response, req_id=req_id)
|
|
|
|
except Exception as err:
|
|
message = "Error spawning plugin " \
|
|
+ plugin_filename + ": " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=str(err), req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
else:
|
|
message = \
|
|
rpc_name \
|
|
+ " - ERROR " + plugin_filename + " does not exist!"
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
except Exception as err:
|
|
message = \
|
|
rpc_name \
|
|
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
return w_msg.serialize()
|
|
|
|
# SC
|
|
async def PluginRemove(self, req, plugin_uuid, parameters=None):
|
|
"""To remove a plugin from the board
|
|
|
|
:param plugin_uuid:
|
|
:return: return a response to RPC request
|
|
|
|
"""
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: "
|
|
+ str(req_id) + "] for '"
|
|
+ plugin_uuid + "' plugin:")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
|
|
plugin_path = CONF.lightningrod_home + "/plugins/" + plugin_uuid + "/"
|
|
|
|
if os.path.exists(plugin_path) is False \
|
|
or os.path.exists(PLUGINS_CONF_FILE) is False:
|
|
|
|
message = "Plugin paths or files do not exist!"
|
|
LOG.error(message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
else:
|
|
|
|
LOG.info(" - Removing plugin...")
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
shutil.rmtree(
|
|
plugin_path,
|
|
ignore_errors=False,
|
|
onerror=None
|
|
)
|
|
|
|
except Exception as err:
|
|
message = "Removing plugin's files error in " \
|
|
+ plugin_path + ": " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
# Remove from plugins.json file its configuration
|
|
try:
|
|
|
|
plugins_conf = self._loadPluginsConf()
|
|
|
|
if plugin_uuid in plugins_conf['plugins']:
|
|
|
|
plugin_name = \
|
|
plugins_conf['plugins'][plugin_uuid]['name']
|
|
|
|
del plugins_conf['plugins'][plugin_uuid]
|
|
|
|
with open(PLUGINS_CONF_FILE, 'w') as f:
|
|
json.dump(plugins_conf, f, indent=4)
|
|
|
|
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 worker_alive:
|
|
LOG.info(" - Plugin '"
|
|
+ plugin_name + "' is running...")
|
|
worker.stop()
|
|
LOG.info(" ...stopped!")
|
|
|
|
del PLUGINS_THRS[plugin_uuid]
|
|
|
|
message = "PluginRemove result: " \
|
|
+ plugin_uuid + " removed!"
|
|
LOG.info(" - " + message)
|
|
|
|
else:
|
|
message = "PluginRemove result: " \
|
|
+ plugin_uuid + " already removed!"
|
|
LOG.warning(" - " + message)
|
|
|
|
w_msg = WM.WampSuccess(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
except Exception as err:
|
|
message = "Updating plugins.json error: " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
except Exception as err:
|
|
message = "Plugin removing error: {0}".format(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
# SC
|
|
async def PluginReboot(self, req, plugin_uuid, parameters=None):
|
|
"""To reboot an asynchronous plugin (callable = false) into the board.
|
|
|
|
:return: return a response to RPC request
|
|
|
|
"""
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: "
|
|
+ str(req_id) + "] for '"
|
|
+ plugin_uuid + "' plugin:")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
|
|
LOG.info(" - plugin restarting with parameters:")
|
|
LOG.info(" " + str(parameters))
|
|
|
|
try:
|
|
|
|
plugin_home = CONF.lightningrod_home + "/plugins/" + plugin_uuid
|
|
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
|
|
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
|
|
|
|
plugins_conf = self._loadPluginsConf()
|
|
plugin_name = plugins_conf['plugins'][plugin_uuid]['name']
|
|
callable = plugins_conf['plugins'][plugin_uuid]['callable']
|
|
|
|
if callable is 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()
|
|
|
|
# STOP PLUGIN----------------------------------------------
|
|
|
|
if worker_alive:
|
|
|
|
LOG.info(" - Thread "
|
|
+ plugin_uuid + " is running, stopping...")
|
|
LOG.debug(" - Stopping plugin " + str(worker))
|
|
worker.stop()
|
|
|
|
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]
|
|
LOG.debug(" - plugin data structure cleaned!")
|
|
|
|
# START PLUGIN-------------------------------------------------
|
|
if os.path.exists(plugin_filename):
|
|
|
|
# Import plugin python module
|
|
# task = imp.load_source("plugin", plugin_filename)
|
|
task = imp.machinery.SourceFileLoader(
|
|
"plugin",
|
|
plugin_filename
|
|
).load_module()
|
|
|
|
if parameters is None:
|
|
|
|
if os.path.exists(plugin_params_file):
|
|
|
|
with open(plugin_params_file) as conf:
|
|
plugin_params = json.load(conf)
|
|
|
|
else:
|
|
plugin_params = None
|
|
|
|
else:
|
|
plugin_params = parameters
|
|
LOG.info(" - plugin restarting with parameters:")
|
|
LOG.info(" " + str(plugin_params))
|
|
|
|
worker = task.Worker(
|
|
plugin_uuid,
|
|
plugin_name,
|
|
params=plugin_params
|
|
)
|
|
|
|
PLUGINS_THRS[plugin_uuid] = worker
|
|
LOG.info(" - Starting plugin " + str(worker))
|
|
|
|
worker.start()
|
|
|
|
message = "REBOOTED"
|
|
LOG.info(" - " + worker.complete(rpc_name, message))
|
|
w_msg = WM.WampSuccess(msg=message, req_id=req_id)
|
|
|
|
else:
|
|
message = "ERROR '" + plugin_filename + "' does not exist!"
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
except Exception as err:
|
|
message = "Error rebooting plugin '" \
|
|
+ plugin_uuid + "': " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
return w_msg.serialize()
|
|
|
|
# SC
|
|
async def PluginStatus(self, req, plugin_uuid, parameters=None):
|
|
"""Check status thread plugin
|
|
|
|
:param plugin_uuid:
|
|
:return:
|
|
|
|
"""
|
|
req_id = req['uuid']
|
|
rpc_name = utils.getFuncName()
|
|
|
|
LOG.info("RPC " + rpc_name + " CALLED [req_id: "
|
|
+ str(req_id) + "] for '"
|
|
+ plugin_uuid + "' plugin:")
|
|
|
|
if parameters is not None:
|
|
LOG.info(" - " + rpc_name + " parameters: " + str(parameters))
|
|
|
|
try:
|
|
|
|
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 worker_alive:
|
|
result = "ALIVE"
|
|
else:
|
|
result = "DEAD"
|
|
|
|
LOG.info(" - " + worker.complete(rpc_name, result))
|
|
w_msg = WM.WampSuccess(msg=result, req_id=req_id)
|
|
|
|
else:
|
|
result = "DEAD"
|
|
LOG.info(" - " + rpc_name + " result for "
|
|
+ plugin_uuid + ": " + result)
|
|
w_msg = WM.WampSuccess(msg=result, req_id=req_id)
|
|
|
|
except Exception as err:
|
|
message = \
|
|
rpc_name \
|
|
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
|
LOG.error(" - " + message)
|
|
w_msg = WM.WampError(msg=message, req_id=req_id)
|
|
|
|
return w_msg.serialize()
|
|
|
|
return w_msg.serialize()
|