import os import sys import logging from logging.handlers import TimedRotatingFileHandler 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.utils.heartbeat import HeartBeatProvider from cloudsync.handlers.error import Error VERSION = "0.4.8" arguments = sys.argv log_level = int(arguments[1]) if not os.path.exists(CS_LOG_PATH): os.makedirs(CS_LOG_PATH) app = Flask(__name__) api = Api(app=app, version=VERSION, title="CloudSync Registration API", description="Interface with DIA Manufacturing Tool * DCS") # Remove existing handlers for handler in app.logger.handlers: app.logger.removeHandler(handler) # Create a TimedRotatingFileHandler that writes logs to a new file every midnight, in UTC time handler = TimedRotatingFileHandler(CS_LOG_FILE, when="midnight", interval=1, utc=True) handler.suffix = "%m-%d-%Y" # Set the log level handler.setLevel(log_level) # Add a formatter default_formatter = logging.Formatter('[%(asctime)s] %(levelname)s in %(module)s: %(message)s | {%(pathname)s:%(lineno)d}') handler.setFormatter(default_formatter) # Add the handler to the logger app.logger.addHandler(handler) app.logger.setLevel(log_level) # Get the root logger root_logger = logging.getLogger() # Add the handlers to the root logger root_logger.addHandler(handler) root_logger.setLevel(log_level) g_utils.add_logger(app.logger) sleep(5) # wait for UI to prepare the configurations partition try: g_config = helpers_read_config(CONFIG_PATH) if g_config[CONFIG_DEVICE][CONFIG_DEVICE_MODE] == 'operation': g_config = helpers_read_config(OPERATION_CONFIG_FILE_PATH) CONFIG_PATH = OPERATION_CONFIG_FILE_PATH reachability_provider = ReachabilityProvider(logger=app.logger, url_reachability=g_config[CONFIG_KEBORMED][CONFIG_KEBORMED_REACHABILITY_URL]) g_utils.add_reachability_provider(reachability_provider=reachability_provider) output_channel = FileOutputBus(logger=app.logger, max_size=100, file_channels_path=UI2CS_FILE_CHANNELS_PATH) error_handler = ErrorHandler(logger=app.logger, max_size=100, output_channel=output_channel) 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) 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") 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, "private_key": fields.String, }) @api.doc(body=FIELD_CREDENTIALS, validate=True) @api.response(OK, "Success") @api.response(BAD_REQUEST, "Validation Error") def put(self): """ Update the device's certificate and keys Manufacturing Tool -> Device """ app.logger.info("Received /credentials HTTP request") 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 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): """ Initiate device connectivity test Manufacturing Tool -> Device """ app.logger.info("Received /validate HTTP request") 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) return {}, OK @api.route("/reset") class Reset(Resource): @api.response(OK, "Success") def head(self): """ Initiate a factory reset Assumes the connectivity test has already been completed Manufacturing Tool -> Device """ app.logger.info("Received /reset HTTP request") 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 return {}, OK # END - REGISTRATION ENDPOINTS def main(): 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: while True: sleep(1) if __name__ == '__main__': main()