From 36917ffa392fd45c8dd6720dbce378b6f66dc9bc Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 30 Mar 2026 19:59:39 -0700 Subject: [PATCH] sync for dev 033026 --- defaults/main.yaml | 11 ++-- files/api/Cosmos_Settings.py | 8 +-- files/api/app.py | 42 +++++++++---- files/api/descriptors.json | 6 +- files/docker/Dockerfile | 91 ++++++++++++++++++++++++++++ files/docker/Dockerfile-1 | 48 +++++++++++++++ files/docker/entrypoint.sh | 6 ++ files/docker/supervisord.conf | 22 +++++++ files/server/server.php | 2 +- files/server/system_metrics.js | 32 ---------- files/web/html/index.php | 2 +- tasks/api.yaml | 9 +++ tasks/init.yaml | 10 ++- tasks/main.yaml | 7 +++ tasks/web.yaml | 65 +++++++++++++------- templates/docker-compose-php.yaml | 3 +- templates/docker-compose-single.yaml | 35 +++++++++++ templates/service_template.service | 2 +- 18 files changed, 317 insertions(+), 84 deletions(-) create mode 100644 files/docker/Dockerfile create mode 100644 files/docker/Dockerfile-1 create mode 100644 files/docker/entrypoint.sh create mode 100644 files/docker/supervisord.conf create mode 100644 templates/docker-compose-single.yaml diff --git a/defaults/main.yaml b/defaults/main.yaml index 13e3539..b952ae5 100644 --- a/defaults/main.yaml +++ b/defaults/main.yaml @@ -56,6 +56,7 @@ REAL_API_KEY: "{{ lookup('password', '/dev/null length=64 chars=ascii_letters,di # dashboard vars service_control_web_folder: "{{ service_folder }}/web" +service_control_docker_folder: "{{ service_folder }}/docker" public_dashboard: true custom_port: "80" web_src: "/web" @@ -63,19 +64,21 @@ web_src: "/web" # other vars quick_refresh: false x64_arch: true +refresh_special: false +special_server: "none" # cosmostat_settings, will be for special_server defaults noisy_test: false -debug_output: true +debug_output: false secure_api: true push_redis: false run_background : true log_output: true update_frequency: "1" -cosmostat_server: true -cosmostat_server_api: "https://cosmostat.testy-cal.com/" +cosmostat_server: false +cosmostat_server_api: "https://cosmostat.matt-cloud.com/" local_api_address: "http://{{ cosmostat_server_ip }}:{{ custom_api_port }}/" -cosmostat_server_reporter: false +cosmostat_server_reporter: true # setting this to true for default install disable_local_api: true ... \ No newline at end of file diff --git a/files/api/Cosmos_Settings.py b/files/api/Cosmos_Settings.py index 21c8d75..a29aed6 100644 --- a/files/api/Cosmos_Settings.py +++ b/files/api/Cosmos_Settings.py @@ -21,7 +21,8 @@ app_settings = { "REAL_API_KEY": ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(256)), "disable_local_api": False, "local_api_address": "http://10.200.27.20:5000/", - "cosmostat_server_ip": "10.200.27.20" + "cosmostat_server_ip": "10.200.27.20", + "api_bind_ip": "192.168.37.1" } with open('cosmostat_settings.yaml', 'r') as f: @@ -71,10 +72,7 @@ def service_gateway_ip(): return result def redis_gateway_ip(): - if cosmostat_settings["secure_api"]: - return cosmostat_bind_ip() - else: - return "0.0.0.0" + return cosmostat_bind_ip() def cosmostat_server_api(): return cosmostat_settings["cosmostat_server_api"] diff --git a/files/api/app.py b/files/api/app.py index cb72b04..b49d958 100644 --- a/files/api/app.py +++ b/files/api/app.py @@ -170,11 +170,10 @@ def get_php_summary(): system_components.append(this_component) if run_cosmostat_server(): - print(cosmostat_client.name) client_uuid = cosmostat_server.get_uuid_from_hostname(cosmostat_client.name) - print(client_uuid) data_timestamp = cosmostat_server.get_system(client_uuid) - print(data_timestamp) + log_string = f"Cosmostat Client Name: {cosmostat_client.name} - UUID: {client_uuid}- Timestamp: {data_timestamp}" + log_data(log_output = log_string, log_level = "log_output") component_age = { "component_name": "Data Timestamp", "info_strings": f"Data is {data_timestamp} seconds old" @@ -221,7 +220,10 @@ def create_client(): if not cosmostat_server.check_uuid(payload["uuid"]): result = run_create_client(payload) else: - result = {"message": "object already exists, skipping creation"} + result = { + "message": "object already exists, skipping creation", + "system_exists": "True" + } return jsonify(result), 200 # api to validate Cosmostat Class @@ -280,6 +282,7 @@ def run_update_client(this_client): return { "status": update_status, + "client_updated": "True", "uuid": this_client["uuid"], "redis_data": this_client, "timestamp_update": timestamp_update @@ -296,6 +299,7 @@ def run_create_client(this_client): update_status = f'created client {this_client["short_id"]}' return { "status": update_status, + "client_created": "True", "uuid": this_client["uuid"], "client_properties": this_client, "timestamp_update": timestamp_update @@ -372,13 +376,15 @@ def get_client_details(): # Cosmostat Client Reporter def client_update(): api_url = f"{cosmostat_server_api()}update_client" - print(api_url) payload = get_client_payload(get_client_redis_data(human_readable = False), "redis_data") + log_data(log_output = f"API Update Called - {api_url}", log_level = "debug_output") log_data(log_output = "client_update - redis data from local client:", log_level = "noisy_test") log_data(log_output = payload, log_level = "noisy_test") # execute API call result = client_submission_handler(api_url, payload) - client_api_initialize() + if not result or not result.get('client_updated'): + log_data(log_output = f"Client not updated, initializing", log_level = "log_output") + result = client_api_initialize() return result # Cosmostat Client Initializer @@ -434,6 +440,7 @@ if __name__ == '__main__': ###################################### ### Main Functions ###################################### + log_data(log_output = f"Main Function Start", log_level = "log_output") # instantiate and return the Client System object def new_cosmos_client(): @@ -480,9 +487,13 @@ if __name__ == '__main__': ###################################### # local client System Class Object + log_data(log_output = f"Cosmostat Client Start", log_level = "log_output") cosmostat_client = new_cosmos_client() + cosmostat_client.update_system_state() + # remote client reporter if app_settings["cosmostat_server_reporter"] and not app_settings["cosmostat_server"]: + log_data(log_output = f"Initialize Client Reporter", log_level = "log_output") client_api_initialize() ###################################### @@ -491,15 +502,19 @@ if __name__ == '__main__': cosmostat_server = None if run_cosmostat_server(): + log_data(log_output = f"Cosmostat Server Start", log_level = "log_output") cosmostat_server = new_cosmostat_server() this_client = get_client_payload(get_php_summary(), "client_properties") timestamp_update = cosmostat_server.add_system(system_dictionary = this_client) + # moving this here so all the bits exist + client_update() ###################################### # send initial stats update to redis ###################################### if app_settings["push_redis"] and not app_settings["disable_local_api"]: + log_data(log_output = f"Initial Redis Push", log_level = "log_output") update_redis_server() ###################################### @@ -507,6 +522,7 @@ if __name__ == '__main__': ###################################### if app_settings["run_background"] and not app_settings["disable_local_api"]: + log_data(log_output = f"Background Function Initializing", log_level = "log_output") log_data(log_output = "Loading flask background subroutine...", log_level = "log_output") scheduler.add_job(id='background_loop', @@ -523,16 +539,18 @@ if __name__ == '__main__': ###################################### # Flask API ###################################### - print(f"gateway: {service_gateway_ip()} - port: {service_api_port()}") + log_data(log_output = f"gateway: {service_gateway_ip()} - port: {service_api_port()}", log_level = "log_output") if not app_settings["disable_local_api"]: + log_data(log_output = f"Main API Start", log_level = "log_output") app.run(debug=False, host=service_gateway_ip(), port=service_api_port()) else: # if local API disabled, phone home if configured - print("Internal API Disabled.") + log_data(log_output = f"Internal API Disabled", log_level = "log_output") + cosmostat_client.update_system_state() + client_update() while True: - if int(time.time()) % 5 == 0 and not cosmostat_client.check_system_timer(): - cosmostat_client.update_system_state() - client_update() - time.sleep(0.5) + cosmostat_client.update_system_state() + client_update() + time.sleep(5) \ No newline at end of file diff --git a/files/api/descriptors.json b/files/api/descriptors.json index 7e451da..127dcab 100644 --- a/files/api/descriptors.json +++ b/files/api/descriptors.json @@ -91,7 +91,8 @@ "Clock Speed" ], "php_extra" :[ - "CPU Model" + "CPU Model", + "Core Count" ] }, { @@ -236,12 +237,13 @@ "name": "DVD", "description": "{Device Path} is a DVD or Virtual DVD drive.", "multi_check": "True", - "device_list": "lsblk -d -o NAME,SIZE | grep sr0| awk '{print $1}'", + "device_list": "lsblk -d -o NAME,SIZE | grep sr0 | awk '{print $1}'", "properties": { "Device Name": "echo {this_device}", "Device Path": "lsblk -d -o NAME,SIZE | grep -v -e 0B -e NAME | awk '{{print \"/dev/\"$1}}' | grep {this_device}", "Total Capacity": "lsblk -d -o NAME,SIZE | grep {this_device} | awk '{{print $2}}'" }, + "precheck": "lspci | grep sr0 | wc -l", "metrics": { "placeholder": "" } diff --git a/files/docker/Dockerfile b/files/docker/Dockerfile new file mode 100644 index 0000000..bf1c022 --- /dev/null +++ b/files/docker/Dockerfile @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------ +# 1. Base image +# ------------------------------------------------------------------ +# We use a slim Debian base so we can use apt‑get to pull every +# component in one go. Debian Bookworm contains all the +# packages we need (nodejs 18, redis, nginx, php8‑fpm, etc.). +# ------------------------------------------------------------------ +FROM php:8.0-apache + +# ------------------------------------------------------------------ +# 2. Build arguments – handy if you want to change the port numbers +# without touching the Dockerfile +# ------------------------------------------------------------------ +ARG REDIS_PORT=6379 +ARG NODE_PORT=3000 +ARG PHP_PORT=8080 +ARG NGX_PORT=80 + +ENV REDIS_PORT=${REDIS_PORT} +ENV NODE_PORT=${NODE_PORT} +ENV PHP_PORT=${PHP_PORT} +ENV NGX_PORT=${NGX_PORT} + +# ------------------------------------------------------------------ +# 3. Install all the system packages we need +# ------------------------------------------------------------------ +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + curl gnupg ca-certificates \ + nodejs npm \ + redis-server \ + nginx \ + supervisor \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# ------------------------------------------------------------------ +# 4. Prepare the working directories +# ------------------------------------------------------------------ +# Node app +WORKDIR /app +COPY web/node_server/package.json ./ +RUN npm install +COPY web/node_server/ ./ + +# Web‑dashboard static files +COPY web/html /var/www/html/ + +# API settings file +COPY cosmostat_settings.yaml /app/cosmostat_settings.yaml + +# Nginx config – you can keep the same file you used for the +# proxy service in the compose file. It will proxy 3000 (WS) +# and 8080 (PHP) to the local container. +COPY web/proxy/nginx.conf /etc/nginx/nginx.conf + +# ------------------------------------------------------------------ +# 5. Supervisord configuration +# ------------------------------------------------------------------ +# Create a minimal supervisord.conf that will launch the four +# services from the same container. +RUN mkdir -p /etc/supervisor/conf.d && \ + cat > /etc/supervisor/conf.d/supervisord.conf < `rgb(${r},${g},${b})`; - const T20 = 20 * 1000; const T40 = 40 * 1000; const T60 = 60 * 1000; - function getFreshnessColor(ageMs) { if (ageMs <= T20) { return toRgb(...GREEN); @@ -54,19 +49,16 @@ } return toRgb(...RED); } - function safeSetText(id, txt) { const el = document.getElementById(id); if (el) el.textContent = txt; } - /* ========================================================== Get the short_id from the query string ========================================================== */ function getSelectedId() { return new URLSearchParams(window.location.search).get('host') || ''; } - /* ========================================================== Sidebar building - uses short_id for status key ========================================================== */ @@ -75,30 +67,24 @@ const current = Array.from(ul.children).map(li => li.dataset.id); const newIds = systemList.map(s => s.short_id); if (arraysEqual(current, newIds)) return; - const selected = getSelectedId().toLowerCase(); ul.innerHTML = ''; // reset list - systemList.forEach(item => { const li = document.createElement('li'); - // status dot - keyed by short_id const status = document.createElement('span'); status.className = 'host-status'; status.dataset.id = item.short_id; - // link - display hostname, encode short_id in URL const a = document.createElement('a'); a.href = '?host=' + encodeURIComponent(item.short_id); a.textContent = item.hostname; if (item.short_id.toLowerCase() === selected) a.classList.add('active'); - li.appendChild(status); li.appendChild(a); ul.appendChild(li); }); } - /* ========================================================== Update status colors every second ========================================================== */ @@ -113,9 +99,7 @@ if (span) span.style.backgroundColor = color; }); } - setInterval(updateStatusColors, 1000); - /* ========================================================== Utility helpers ========================================================== */ @@ -124,7 +108,6 @@ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; } - function renderGenericTable(containerId, data, emptyMsg) { const container = document.getElementById(containerId); if (!Array.isArray(data) || !data.length) { @@ -138,7 +121,6 @@ container.innerHTML = ''; container.appendChild(table); } - function mergeRowsByName(rows) { const groups = {}; // { Source: { Metric: [], Data: [] } } rows.forEach(r => { @@ -156,7 +138,6 @@ Data: g.Data, })); } - function orderRows(rows) { const priority = ['System', 'CPU', 'RAM']; const map = {}; @@ -167,7 +148,6 @@ return ai - bi; }); } - function buildTable(rows) { const cols = ['Source', 'Metric', 'Data']; const table = document.createElement('table'); @@ -200,12 +180,10 @@ }); return table; } - /* ========================================================== Handle incoming data ========================================================== */ let lastUpdate = Date.now(); - function handleSummary(raw) { lastUpdate = Date.now(); // reset watchdog let payload; @@ -215,25 +193,20 @@ return; } } else payload = raw; - if (!Array.isArray(payload) || !payload.length) { safeSetText('client_summary', 'No data available'); return; } - // Build the list first (so elements exist) buildList(payload); - // Store the timestamp for every short_id payload.forEach(hostObj => { if (hostObj.short_id && hostObj.data_timestamp) { hostTimestamps[hostObj.short_id] = hostObj.data_timestamp; // seconds } }); - // Immediately update colors for the current view updateStatusColors(); - // Metric table for selected host const selectedId = getSelectedId(); const hostObj = payload.find(h => h.short_id === selectedId) || payload[0]; @@ -242,7 +215,6 @@ : []; renderGenericTable('host_metrics', hostData, 'No Stats available'); } - /* ========================================================== Socket event wiring ========================================================== */ @@ -258,7 +230,6 @@ safeSetText('client_summary', `Re-connected (attempt ${attempt})`); requestSummary(); }); - /* ========================================================== Request logic ========================================================== */ @@ -266,7 +237,6 @@ if (!socket.connected) return; // guard against stale emits socket.emit('get_client_summary'); // server will reply via client_summary } - /* ========================================================== Recursive polling ========================================================== */ @@ -279,7 +249,6 @@ socket.on('connect', () => { if (!pollTimer) pollLoop(); }); - /* ========================================================== Watchdog - force reconnect if no data for 15 s ========================================================== */ @@ -291,7 +260,6 @@ setTimeout(watchdog, 5000); } watchdog(); - /* ========================================================== Keep the 'active' link in sync when the URL changes ========================================================== */ diff --git a/files/web/html/index.php b/files/web/html/index.php index 1fe5e56..300f59b 100644 --- a/files/web/html/index.php +++ b/files/web/html/index.php @@ -39,7 +39,7 @@