Index: cloud_sync.py =================================================================== diff -u -r4ebf654ab1b0d436ce7d171bfe06c6bdc46aca47 -rb02f69db02eb8157d85f8d5c5b4e571784a6c212 --- cloud_sync.py (.../cloud_sync.py) (revision 4ebf654ab1b0d436ce7d171bfe06c6bdc46aca47) +++ cloud_sync.py (.../cloud_sync.py) (revision b02f69db02eb8157d85f8d5c5b4e571784a6c212) @@ -1,97 +1,143 @@ -"""Implementation of CloudSync controller""" - -import argparse -import logging -import socket -import sys - import werkzeug werkzeug.cached_property = werkzeug.utils.cached_property from flask import Flask, Response, request from flask_restplus import Api, Resource, fields, reqparse -from cloudsync.utils.helpers import * -from cloudsync.utils.globals import * -from cloudsync.utils.reachability import ReachabilityProvider -from cloudsync.busses.file_output_bus import FileOutputBus -from cloudsync.handlers.error_handler import ErrorHandler -from cloudsync.handlers.cs_mft_dcs_request_handler import NetworkRequestHandler -from cloudsync.handlers.ui_cs_request_handler import UICSMessageHandler -from cloudsync.busses.file_input_bus import FileInputBus +from cloudsync.handlers.error import Error from cloudsync.utils.heartbeat import HeartBeatProvider +from cloudsync.busses.file_input_bus import FileInputBus +from cloudsync.handlers.ui_cs_request_handler import UICSMessageHandler +from cloudsync.handlers.cs_mft_dcs_request_handler import NetworkRequestHandler +from cloudsync.handlers.error_handler import ErrorHandler +from cloudsync.busses.file_output_bus import FileOutputBus +from cloudsync.utils.reachability import ReachabilityProvider +from cloudsync.utils.globals import * +from cloudsync.utils.helpers import * +from cloudsync.utils.logging import LoggingConfig +import os +import sys -VERSION = "0.2.6" +VERSION = "0.4.15" + arguments = sys.argv -log_level = int(arguments[1]) -logging.basicConfig(filename='cloudsync.log', - level=log_level, - format='[%(asctime)s]: %(levelname)s - %(message)s | {%(pathname)s:%(lineno)d}', - ) - app = Flask(__name__) api = Api(app=app, version=VERSION, title="CloudSync Registration API", - description="Interface with DIA Manufacturing Tool") + description="Interface with DIA Manufacturing Tool * DCS") -for handler in app.logger.handlers: - app.logger.removeHandler(handler) -handler = logging.FileHandler('cloudsync.log') -handler.setLevel(log_level) -default_formatter = logging.Formatter('[%(asctime)s] %(levelname)s in %(module)s: %(message)s') -handler.setFormatter(default_formatter) +logconf = LoggingConfig() +logconf.initiate(app=app) -# app.logger.addHandler(handler) -app.logger.setLevel(log_level) +sleep(5) # wait for UI to prepare the configurations partition -g_utils.add_logger(app.logger) +try: + ok = False + print(SETUP_CONSOLE_LINE) -g_config = helpers_read_config(CONFIG_PATH) + if EXEC_MODE_UPGRADE_KEY in arguments: # ---------- upgrade + # Read from $HOME + if os.path.isfile(CONFIG_PATH) and os.access(CONFIG_PATH, os.R_OK | os.W_OK): #TODO test if this check needed? + EXEC_MODE = EXEC_MODE_UPGRADE + g_config = helpers_read_config(CONFIG_PATH) + ok = True + else: + if EXEC_MODE_UPDATE_KEY in arguments: # ---------- update + EXEC_MODE = EXEC_MODE_UPDATE + print(f"CloudSync starting in {EXEC_MODE} mode") + print("CloudSync update config started...") + # Update the $HOME from /var/configuraitons/ and update result in both for later update + oldConfig = helpers_read_config(OPERATION_CONFIG_FILE_PATH) + newConfig = helpers_read_config(CONFIG_PATH) + newConfig.update(oldConfig) + helpers_write_config(None , CONFIG_PATH , newConfig) + helpers_write_config(OPERATION_CONFIG_PATH, OPERATION_CONFIG_FILE_PATH, newConfig) + print("CloudSync update config done.") -reachability_provider = ReachabilityProvider(logger=app.logger) + # Read from /var/configuraitons/ # ---------- normal + if os.path.isfile(OPERATION_CONFIG_FILE_PATH) and os.access(OPERATION_CONFIG_FILE_PATH, os.R_OK): #TODO test if this check needed? + g_config = helpers_read_config(OPERATION_CONFIG_FILE_PATH) + CONFIG_PATH = OPERATION_CONFIG_FILE_PATH + ok = True -g_utils.add_reachability_provider(reachability_provider=reachability_provider) + if ok: + print(f"CloudSync started in {EXEC_MODE} mode") + print(f"Using config: {CONFIG_PATH}") + else: + g_utils.logger.error(f"Error reading config file in {EXEC_MODE}") -output_channel = FileOutputBus(logger=app.logger, max_size=100, file_channels_path=UI2CS_FILE_CHANNELS_PATH) + print(SETUP_CONSOLE_LINE) -error_handler = ErrorHandler(logger=app.logger, max_size=100, output_channel=output_channel) +except Exception as e: + g_utils.logger.error(f"Error reading config file - {e}") + print(SETUP_CONSOLE_LINE) + sys.exit(0) -network_request_handler = NetworkRequestHandler(logger=app.logger, max_size=1, output_channel=output_channel, - reachability_provider=reachability_provider, - error_handler=error_handler) -message_handler = UICSMessageHandler(logger=app.logger, max_size=20, network_request_handler=network_request_handler, - output_channel=output_channel, reachability_provider=reachability_provider, - error_handler=error_handler) -ui_cs_bus = FileInputBus(logger=app.logger, file_channels_path=UI2CS_FILE_CHANNELS_PATH, input_channel_name="inp.buf", - g_config=g_config, message_handler=message_handler) +try: + reachability_provider = ReachabilityProvider(logger=app.logger, url_reachability=g_config[CONFIG_KEBORMED][CONFIG_KEBORMED_REACHABILITY_URL]) +except Exception as e: + g_utils.logger.error( + "Reachability URL missing from config file. Using Default URL - {0}".format(DEFAULT_REACHABILITY_URL)) + reachability_provider = ReachabilityProvider( + logger=app.logger, url_reachability=DEFAULT_REACHABILITY_URL) -heartbeat_provider = HeartBeatProvider(logger=app.logger, network_request_handler=network_request_handler, - output_channel=output_channel) +try: + g_utils.add_reachability_provider(reachability_provider=reachability_provider) -if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'operation': - heartbeat_provider.send_heartbeat = True -else: - heartbeat_provider.send_heartbeat = False + output_channel = FileOutputBus(logger=app.logger, max_size=100, file_channels_path=UI2CS_FILE_CHANNELS_PATH) -parser = reqparse.RequestParser() + error_handler = ErrorHandler(logger=app.logger, max_size=100, output_channel=output_channel) -# START - REGISTRATION ENDPOINTS + network_request_handler = NetworkRequestHandler(logger=app.logger, max_size=1, output_channel=output_channel, + reachability_provider=reachability_provider, + error_handler=error_handler) + message_handler = UICSMessageHandler(logger=app.logger, max_size=20, + network_request_handler=network_request_handler, + output_channel=output_channel, reachability_provider=reachability_provider, + error_handler=error_handler) + ui_cs_bus = FileInputBus(logger=app.logger, file_channels_path=UI2CS_FILE_CHANNELS_PATH, + input_channel_name="inp.buf", + g_config=g_config, message_handler=message_handler) + heartbeat_provider = HeartBeatProvider(logger=app.logger, network_request_handler=network_request_handler, + output_channel=output_channel) + + logconf.set_network_provider(network_request_handler=network_request_handler) + logconf.set_error_provider(error_handler=error_handler) + logconf.set_configuration(g_config=g_config) + logconf.set_log_level(g_config[CONFIG_LOGS][CONFIG_LOGS_DEFAULT_LOG_LEVEL]) + + if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'operation': + heartbeat_provider.send_heartbeat = True + else: + heartbeat_provider.send_heartbeat = False + + g_utils.logger.debug("Current config path: {0}".format(CONFIG_PATH)) + + parser = reqparse.RequestParser() +except Exception as e: + g_utils.logger.error("Failed to start CS - {0}".format(e)) + sys.exit(0) + + @api.route("/version") class Version(Resource): @api.response(OK, "Success") def get(self): app.logger.info("Received /version HTTP request") - return { - "version": VERSION - }, OK + if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'registration': + return { + "version": VERSION + }, OK + +# START - REGISTRATION ENDPOINTS + @api.route("/credentials") class Credentials(Resource): - FIELD_CREDENTIALS = api.model("Credentials", { "certificate": fields.String, "public_key": fields.String, @@ -103,58 +149,71 @@ @api.response(BAD_REQUEST, "Validation Error") def put(self): """ - 4. Update the device's certificate and keys + Update the device's certificate and keys Manufacturing Tool -> Device """ app.logger.info("Received /credentials HTTP request") - payload = request.json - certificate = payload.get("certificate", None) - private_key = payload.get("private_key", None) - public_key = payload.get("public_key", None) - invalid_params = [] - if certificate is None: - invalid_params.append("certificate") - if private_key is None: - invalid_params.append("private_key") - if public_key is None: - invalid_params.append("public_key") - if len(invalid_params) > 0: - return {"invalidAttributes": invalid_params}, BAD_REQUEST - r = NetworkRequest(request_type=NetworkRequestType.MFT2CS_REQ_SET_CREDENTIALS, - url=request.url, - payload=payload, - method=request.method, - g_config=g_config, - ) - network_request_handler.enqueue_request(r) + if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'registration': + payload = request.json + certificate = payload.get("certificate", None) + private_key = payload.get("private_key", None) + public_key = payload.get("public_key", None) + invalid_params = [] + if certificate is None: + invalid_params.append("certificate") + if private_key is None: + invalid_params.append("private_key") + if public_key is None: + invalid_params.append("public_key") + if len(invalid_params) > 0: + error = Error("{0},2,{1}, invalid credentials: {2}".format(OutboundMessageIDs.CS2UI_ERROR.value, + ErrorIDs.CS_SAVE_CREDENTIALS_ERROR.value, + invalid_params)) + error_handler.enqueue_error(error=error) + return {"invalidAttributes": invalid_params}, BAD_REQUEST - return { - "invalidAttributes": [], - }, OK + try: + helpers_add_to_network_queue(network_request_handler=network_request_handler, + request_type=NetworkRequestType.MFT2CS_REQ_SET_CREDENTIALS, + url=request.url, + payload=payload, + method=request.method, + g_config=g_config, + success_message='CS2MFT_REQ_SET_CREDENTIALS request added to network ' + 'queue') + except Exception as e: + error = Error("{0},2,{1},{2}".format(OutboundMessageIDs.CS2UI_ERROR.value, + ErrorIDs.CS_SAVE_CREDENTIALS_ERROR.value, + e)) + error_handler.enqueue_error(error=error) + return { + "invalidAttributes": [], + }, OK + @api.route("/validate") class Validate(Resource): @api.response(OK, "Success") def head(self): """ - 5. Initiate device connectivity test + Initiate device connectivity test Manufacturing Tool -> Device """ app.logger.info("Received /validate HTTP request") - r = NetworkRequest(request_type=NetworkRequestType.MFT2CS_REQ_INIT_CONNECTIVITY_TEST, - url=request.url, - payload={}, - method=request.method, - g_config=g_config, - ) - network_request_handler.enqueue_request(r) + if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'registration': + r = NetworkRequest(request_type=NetworkRequestType.MFT2CS_REQ_INIT_CONNECTIVITY_TEST, + url=request.url, + payload={}, + method=request.method, + g_config=g_config, + ) + network_request_handler.enqueue_request(r) - # no body is returned - return {}, OK + return {}, OK @api.route("/reset") @@ -163,72 +222,36 @@ @api.response(OK, "Success") def head(self): """ - 13. Initiate a factory reset + Initiate a factory reset Assumes the connectivity test has already been completed Manufacturing Tool -> Device """ app.logger.info("Received /reset HTTP request") - r = NetworkRequest(request_type=NetworkRequestType.MFT2CS_REQ_FACTORY_RESET, - url=request.url, - payload={}, - method=request.method, - g_config=g_config, - ) - network_request_handler.enqueue_request(r) + if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'registration': + r = NetworkRequest(request_type=NetworkRequestType.MFT2CS_REQ_FACTORY_RESET, + url=request.url, + payload={}, + method=request.method, + g_config=g_config, + ) + network_request_handler.enqueue_request(r) - heartbeat_provider.send_heartbeat = True + heartbeat_provider.send_heartbeat = True - return {}, OK + return {}, OK + # END - REGISTRATION ENDPOINTS -@api.route("/config") -class Config(Resource): - @api.response(OK, "Success") - def get(self): - """ - Get the current json config - """ - app.logger.info("Received /config HTTP request") - return g_config, OK - - -@api.route("/token") -class Config(Resource): - - @api.response(OK, "Success") - def get(self): - """ - Get the currently stored token - """ - app.logger.info("Received /token HTTP request") - return helpers_read_access_token(DEVICE_KEBORMED_ACCESS_TOKEN_PATH), OK - - -@api.route("/logs") -class Logs(Resource): - - @api.hide - def get(self): - app.logger.info("Received /logs HTTP request") - content = "" - with open(CS_LOG_FILE, 'r') as f: - content = f.read() - return Response(content, mimetype="text/plain") - - def main(): - if DEBUG: - app.run(debug=False, use_reloader=False, host="0.0.0.0", port=g_config[CONFIG_DEVICE][CONFIG_DEVICE_PORT]) - elif VM: - app.logger.error("VM mode not implemented yet.") - elif SERVER: - app.logger.error("Server mode not implemented yet.") + if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'registration': + app.run(debug=False, use_reloader=False, host="0.0.0.0",port=g_config[CONFIG_DEVICE][CONFIG_DEVICE_PORT]) else: - app.logger.error("Production mode not implemented yet.") + while True: + sleep(1) if __name__ == '__main__':