def test_Configuration_read_test(self): f = open(self.confPath, 'w', encoding="utf-8") f.write(self.testConf) f.close() conf = Configuration(self.confPath) self.assertEqual(conf.get_conf("Client", "database"), "../db") self.assertEqual(conf.get_conf("Client", "frontend-port"), 5000) os.remove(self.confPath)
def __init__(self, config: Configuration, path, mission_name): self._logger = logging.getLogger(__name__) """ Get Satellite catalog number from tle to be used as name for log file. """ try: tle_req = requests.get(config.get_conf("Client", "tle-url"), timeout=5) if tle_req.status_code == 200: mission_name = tle_req.text.split("\n")[1][2:7] except: self.log.warning( "Connection to tle endpoint failed. Using default packet log file name ({})" .format(mission_name)) if not os.path.isdir(path): os.mkdir(path) logfile = "{}/{}_packets.log".format(path, mission_name) self.file_logger = logging.FileHandler(logfile)
def main(argv): """ Main loop function. """ """ Parse command line options """ opts, args = getopt(argv, "vc:") conf_path = None for opt, arg in opts: if opt == "-c": conf_path = arg if conf_path is None: """ Default conf path """ conf_path = "../configuration.ini" """ Create the configuration object """ conf = Configuration(conf_path) """ Set up logging """ if not conf.get_conf("Client", "debug-log"): logging.basicConfig(level=logging.INFO) else: logging.basicConfig(level=logging.DEBUG) _logger = logging.getLogger(__name__) if not os.path.isdir("../logs"): os.mkdir("../logs") formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') handler = logging.FileHandler("../logs/system_logs.log") handler.setFormatter(formatter) logging.getLogger('').addHandler(handler) _logger.info("Using configuration from: %s", conf_path) """ Create the database object """ db_loc = os.path.join(util.get_root(), conf.get_conf("Client", "database")) database = TelemetryDB(db_loc) database.init_db() """ Update grafana and kaitai configurations """ if conf.get_conf("Client", "automatic-updating"): Updater(conf).checkForUpdates() """ Build the other components. """ ax_listener = AXListener(conf) sids_relay = SIDSRelay(conf, database) telemetry_listener = TelemetryListener(database) file_logger = FileLogger(conf, conf.get_conf("Client", "logs"), "log") """ Create the flask app and start it in a forked process. """ port = conf.get_conf("Client", "frontend-port") """ Set the handler for SIGTERM, so we can exit a bit more gracefully. """ signal.signal(signal.SIGTERM, terminate_handler) """ Hook the callbacks to the ax_listener. """ ax_listener.add_callback(database.insert_ax_frame) ax_listener.add_callback(sids_relay.relay) ax_listener.add_callback(file_logger.log_ax_frame) ax_listener.add_callback(telemetry_listener.receive) tnc_pool = TNCPool(conf, ax_listener) tnc_pool.connect_main_tnc() api_app = api.create_app(conf, tnc_pool, sids_relay) """ We set the daemon option to True, so that the client will quit once the other threads have finished because we don't have a good way of stopping the Flask app properly. """ api_thread = Thread(target=api_app.run, kwargs={"port": port}, daemon=True) api_thread.start() _logger.info("For the GUI open localhost:{}".format(port)) try: """ On windows, the KeyboardInterrupt doesn't break the join. """ if platform.system() == "Windows": while api_thread.isAlive: api_thread.join(2) else: api_thread.join() except (KeyboardInterrupt, SystemExit): pass finally: tnc_pool.cleanup()
def create_app(config: Configuration, tnc_pool: TNCPool, sids_relay: SIDSRelay) -> Flask: """ Creates a flask app for the api. """ log = logging.getLogger(__name__) static_folder = os.path.join(util.get_root(), config.get_conf("Client", "static-files-path")) app = Flask(__name__, static_url_path="", static_folder=static_folder) CORS(app) if not config.get_conf("Client", "debug-log"): server_log = logging.getLogger("werkzeug") server_log.setLevel(logging.WARN) # swagger specific swagger_url = "/api/docs" api_url = "/api/static/swagger.yaml" swaggerui_blueprint = get_swaggerui_blueprint( swagger_url, api_url, config={ "app_name": "Estcube 2 Telemetry API" } ) app.register_blueprint(swaggerui_blueprint, url_prefix=swagger_url) # end swagger specific @app.route("/api/sids/status", methods=["GET"]) def get_sids_status(): return jsonify(sids_relay.get_status()), 200 @app.route("/api/sids/toggle", methods=["POST"]) def toggle_relay(): response_json = request.get_json() current_relay_status = response_json["Mission Control"]["relay-enabled"] config.set_conf(section="Mission Control", element="relay-enabled", value=current_relay_status) if current_relay_status: threading.Thread(target=sids_relay.relay_unrelayed_packets, daemon=True).start() return response_json, 200 @app.route("/api/tnc/<name>/status", methods=["GET"]) def get_tnc_connection_check(name: str): if tnc_pool is None: return jsonify({"error": "TNC Pool is not defined."}), 500 res = tnc_pool.check_tnc(name) return jsonify({"name": name, "status": res.name}), 200 @app.route("/api/tnc/Main/start", methods=["POST"]) def post_tnc_main_start(): if tnc_pool is None: return jsonify({"error": "TNC Pool is not defined."}), 500 tnc_pool.connect_main_tnc() return "", 204 @app.route("/api/tnc/<name>/stop", methods=["POST"]) def post_tnc_connection_stop(name: str): if tnc_pool is None: return jsonify({"error": "TNC Pool is not defined."}), 500 tnc_pool.stop_tnc(name) return "", 204 @app.route("/api/conf", methods=["GET"]) def getconf(): """ Returns the whole current configuration object. """ res = config.get_all_conf() return jsonify(res) @app.route("/api/conf/constraints", methods=["GET"]) def get_constraints(): """ Returns all of the constraints for the configuration. """ constrs = config.get_constraints() return jsonify(constrs) @app.route("/api/conf/full", methods=["GET"]) def get_full_conf(): res = config.get_conf_with_constraints() return res @app.route("/", methods=["GET"]) def get_index(): return send_file(os.path.join(static_folder, "index.html")) @app.errorhandler(404) def not_found(e): return send_file(os.path.join(static_folder, "index.html")) @app.route("/api/static/<path:path>") def send_static(path): return send_from_directory("static", path) @app.route("/api/conf", methods=["POST"]) def post_set_conf(): some_json = request.get_json() try: for i in some_json: for j in some_json[i]: config.set_conf(section=i, element=j, value=some_json[i][j]) except: _, error, _ = sys.exc_info() return jsonify({"Error": "{err}".format(err=error)}), 500 return jsonify(some_json), 200 return app
@app.errorhandler(404) def not_found(e): return send_file(os.path.join(static_folder, "index.html")) @app.route("/api/static/<path:path>") def send_static(path): return send_from_directory("static", path) @app.route("/api/conf", methods=["POST"]) def post_set_conf(): some_json = request.get_json() try: for i in some_json: for j in some_json[i]: config.set_conf(section=i, element=j, value=some_json[i][j]) except: _, error, _ = sys.exc_info() return jsonify({"Error": "{err}".format(err=error)}), 500 return jsonify(some_json), 200 return app if __name__ == "__main__": CONF_PATH = os.path.join(util.get_root(), "configuration.ini") conf = Configuration(CONF_PATH) STATIC_PATH = os.path.join(util.get_root(), conf.get_conf("Client", "static-files-path")) APP = create_app(conf, STATIC_PATH, None) APP.run(debug=True)