Example #1
0
    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)