From 05521ba91f273f56de95032b8bea901374404adb Mon Sep 17 00:00:00 2001 From: Muhammad Umer Date: Sat, 7 Dec 2024 00:05:49 +0500 Subject: [PATCH 1/4] Update index.html --- templates/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/index.html b/templates/index.html index d833c96..23c7963 100644 --- a/templates/index.html +++ b/templates/index.html @@ -44,6 +44,7 @@
HOME CONFIGURE + LOGGING
From a7a597c1962635fd2a2aaa6890aee1417b1ebc27 Mon Sep 17 00:00:00 2001 From: Muhammad Umer Date: Sat, 7 Dec 2024 00:06:48 +0500 Subject: [PATCH 2/4] Create logs.html --- templates/logs.html | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 templates/logs.html diff --git a/templates/logs.html b/templates/logs.html new file mode 100644 index 0000000..36f1092 --- /dev/null +++ b/templates/logs.html @@ -0,0 +1,75 @@ + + + + + + Application Logs + + + + + + + +
+ +
+ +
+

Application Logs

+ +
+
+ {% if logs %} + {% for log in logs %} +
+ {{ log }} +
+ {% endfor %} + {% else %} +

No logs available.

+ {% endif %} +
+
+
+ + + + From 17c6984023ce89fdc84a229722097d4a38350df5 Mon Sep 17 00:00:00 2001 From: Muhammad Umer Date: Sat, 7 Dec 2024 00:07:34 +0500 Subject: [PATCH 3/4] Update app.py --- app.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index 2e90e2e..27e2c29 100644 --- a/app.py +++ b/app.py @@ -2,6 +2,9 @@ from flask import Flask, render_template, request, jsonify, session # external from werkzeug.security import generate_password_hash # built-in from flask_session import Session # external +import logging +from logging.handlers import RotatingFileHandler + import os, shutil, signal, atexit, json, yaml, logging, tempfile # built-in import docker, ansible_runner # external @@ -126,7 +129,7 @@ def cleanup_files(file_paths): os.unlink(file_path) # removes the file -def run_ansible(hosts, command): # [NOTE] shell command execution will not work with other shells like zsh, fish, etc. +def run_ansible(hosts, command): """ Runs an Ansible playbook on specified hosts with a given command. """ @@ -146,8 +149,8 @@ def run_ansible(hosts, command): # [NOTE] shell command execution will not work - name: Run custom command on all hosts hosts: all tasks: - - name: Execute custom command (in interactive bash shell) - shell: bash -i -c "{command}" + - name: Execute custom command + shell: {command} register: command_output - debug: var: command_output.stdout @@ -206,6 +209,30 @@ def cleanup(action='stop'): app.logger.info("Cleanup complete.") +import logging +from logging.handlers import RotatingFileHandler + +# Set up logging +log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + +# Create a file handler for logging to a file (overwrite the file each time) +log_file = "app.log" # The log file name +file_handler = logging.FileHandler(log_file, mode='w') # 'w' mode will overwrite the file each time +file_handler.setFormatter(log_formatter) +file_handler.setLevel(logging.DEBUG) # Adjust the level as needed (DEBUG, INFO, WARNING, ERROR, CRITICAL) + +# Create a console handler for logging to the terminal +console_handler = logging.StreamHandler() +console_handler.setFormatter(log_formatter) +console_handler.setLevel(logging.DEBUG) # Adjust the level as needed + +# Get the app logger and set the log level +app.logger.setLevel(logging.DEBUG) +app.logger.addHandler(file_handler) +app.logger.addHandler(console_handler) + +# Log a message to verify setup +app.logger.info("Logging setup complete. Logs will be saved to 'app.log' and displayed on the console.") ## ROUTES (FOR RENDERING HTML PAGES) ## @app.route('/') @@ -253,12 +280,22 @@ def SPAWN_MACHINES_ROUTE(): app.logger.error(f"Error in spawn_machines: {str(e)}") return jsonify({"error": str(e)}), 500 +@app.route('/logs', methods=['GET']) +def display_logs_from_file(): + """Render the logs HTML page with logs read directly from the log file.""" + log_file_path = 'app.log' # Path to your log file + try: + with open(log_file_path, 'r') as f: + logs = f.readlines() # Read all lines from the log file + except FileNotFoundError: + logs = ["Log file not found. Please ensure the application is running and generating logs."] + return render_template('logs.html', logs=logs) @app.route('/run_command', methods=['POST']) def RUN_COMMAND_ROUTE(): try: command = request.form['command'] - if not command: + if not command: # [IMPROVEMENT] ensure that command text-box can accept multi-line commands & also accepts: ". / | & {} () etc" return jsonify({"error": "Invalid command."}), 400 machine_info = session.get('machine_info', []) @@ -372,12 +409,7 @@ def FETCH_STOPPED_CONTAINERS_ROUTE(): @app.route('/save_config', methods=['POST']) -def SAVE_CONFIG_ROUTE(): # [NOTE] needs testing, [IMPROVEMENT] add error handling & handle msg in div instead of alert - # Get the list of spawned containers - machine_info = session.get('machine_info', []) - if not machine_info: - return jsonify({"message": "No machines spawned. Please spawn machines first."}) - +def SAVE_CONFIG_ROUTE(): # [NOTE] needs testing, [IMPROVEMENT] refactor or split into separate routes for each config option & add error handling config_option = request.form.get('configOption') if config_option == 'nginx': nginx_port = request.form.get('nginxPort') @@ -391,6 +423,11 @@ def SAVE_CONFIG_ROUTE(): # [NOTE] needs testing, [IMPROVEMENT] add error handlin with open('nginx_config.yml', 'w') as f: yaml.dump(config, f) + # Get the list of spawned containers + machine_info = session.get('machine_info', []) + if not machine_info: + return jsonify({"message": "No machines spawned. Please spawn machines first."}) + # Create a temporary inventory file with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.yml') as temp_inventory: inventory = { @@ -561,6 +598,7 @@ def SAVE_CONFIG_ROUTE(): # [NOTE] needs testing, [IMPROVEMENT] add error handlin else: return jsonify({"message": "Invalid configuration option."}) + ## [NOTE] SOME IMPORTANT POINTS (FOR ROUTES): ## @@ -596,4 +634,4 @@ def handle_sigterm(signum, frame): ## MAIN ENTRY POINT ## if __name__ == '__main__': - app.run(debug=True) # start the Flask app in debug mode, [NOTE] to disable auto-reload, set 'use_reloader=False' + app.run(debug=True, use_reloader=False) # start the Flask app in debug mode (with auto-reload disabled) From 11d185d74a2eb107cbda61eaf059853fa675cb3b Mon Sep 17 00:00:00 2001 From: Muhammad Umer Date: Sat, 7 Dec 2024 00:36:37 +0500 Subject: [PATCH 4/4] Updated logs, added toggle feature.html --- templates/logs.html | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/templates/logs.html b/templates/logs.html index 36f1092..e09f135 100644 --- a/templates/logs.html +++ b/templates/logs.html @@ -36,6 +36,12 @@

Application Logs

+ +
+ + +
+
{% if logs %} @@ -70,6 +76,28 @@

Applicati } } } + + // Toggle button functionality + const systemToggle = document.getElementById('systemToggle'); + const machineToggle = document.getElementById('machineToggle'); + + systemToggle.addEventListener('click', () => { + systemToggle.classList.add('bg-primary', 'text-white'); + machineToggle.classList.remove('bg-primary', 'text-white'); + machineToggle.classList.add('bg-gray-300', 'text-gray-700'); + systemToggle.classList.remove('bg-gray-300', 'text-gray-700'); + systemToggle.innerText = "System Monitoring"; + machineToggle.innerText = "Machine Monitoring"; + }); + + machineToggle.addEventListener('click', () => { + machineToggle.classList.add('bg-primary', 'text-white'); + systemToggle.classList.remove('bg-primary', 'text-white'); + systemToggle.classList.add('bg-gray-300', 'text-gray-700'); + machineToggle.classList.remove('bg-gray-300', 'text-gray-700'); + machineToggle.innerText = "Machine Monitoring"; + systemToggle.innerText = "System Monitoring"; + });