diff --git a/files/api/DriveServer.py b/files/api/DriveServer.py deleted file mode 100644 index 9a269b9..0000000 --- a/files/api/DriveServer.py +++ /dev/null @@ -1,4 +0,0 @@ -# This is the class definition for the remote Storage Systems -# There will be a StorageLinux and StorageWindows Class, as well as the LocalServer Class -# The LocalServer class will mostly be a List of Storage server objects and Class functions for interacting with them -# The actual Storage server Classes will mostly just be collections of variables diff --git a/files/api/StorageApi.py b/files/api/StorageApi.py deleted file mode 100644 index b06fc69..0000000 --- a/files/api/StorageApi.py +++ /dev/null @@ -1,6 +0,0 @@ -### This file contains the flask routes for interfacing with the DriveServer objects -### I need routes for adding/updating windows/linux hosts, as well as a query route -### There needs to also be a Redis handler, meaning also that this will render with PHP -### but have javascript to update if any Redis data happens -### This won't happen a lot, but it will happen occasionally - diff --git a/files/api/new_descriptors.json b/files/api/new_descriptors.json index b7f3250..1c4caba 100644 --- a/files/api/new_descriptors.json +++ b/files/api/new_descriptors.json @@ -1,6 +1,7 @@ [ { - "notes:": "this is both a scratch file and a reference for new component descriptors" + "notes:": "this is both a scratch file and a reference for new component descriptors", + "descriptor descriptor": "the reference descriptor is the next key" }, { "name": "", diff --git a/files/api/archive.py b/files/archive/archive.py similarity index 100% rename from files/api/archive.py rename to files/archive/archive.py diff --git a/files/vizz/docker/Dockerfile b/files/archive/vizz/docker/Dockerfile similarity index 100% rename from files/vizz/docker/Dockerfile rename to files/archive/vizz/docker/Dockerfile diff --git a/files/vizz/docker/apache_ports.conf b/files/archive/vizz/docker/apache_ports.conf similarity index 100% rename from files/vizz/docker/apache_ports.conf rename to files/archive/vizz/docker/apache_ports.conf diff --git a/files/vizz/docker/apache_vhost.conf b/files/archive/vizz/docker/apache_vhost.conf similarity index 100% rename from files/vizz/docker/apache_vhost.conf rename to files/archive/vizz/docker/apache_vhost.conf diff --git a/files/vizz/docker/supervisord.conf b/files/archive/vizz/docker/supervisord.conf similarity index 100% rename from files/vizz/docker/supervisord.conf rename to files/archive/vizz/docker/supervisord.conf diff --git a/files/vizz/docker/web/html/index.php b/files/archive/vizz/docker/web/html/index.php similarity index 100% rename from files/vizz/docker/web/html/index.php rename to files/archive/vizz/docker/web/html/index.php diff --git a/files/vizz/docker/web/html/src/redis.js b/files/archive/vizz/docker/web/html/src/redis.js similarity index 100% rename from files/vizz/docker/web/html/src/redis.js rename to files/archive/vizz/docker/web/html/src/redis.js diff --git a/files/vizz/docker/web/html/src/styles.css b/files/archive/vizz/docker/web/html/src/styles.css similarity index 100% rename from files/vizz/docker/web/html/src/styles.css rename to files/archive/vizz/docker/web/html/src/styles.css diff --git a/files/vizz/docker/web/html/src/system_metrics.js b/files/archive/vizz/docker/web/html/src/system_metrics.js similarity index 100% rename from files/vizz/docker/web/html/src/system_metrics.js rename to files/archive/vizz/docker/web/html/src/system_metrics.js diff --git a/files/vizz/docker/web/html/test.php b/files/archive/vizz/docker/web/html/test.php similarity index 100% rename from files/vizz/docker/web/html/test.php rename to files/archive/vizz/docker/web/html/test.php diff --git a/files/vizz/docker/web/node_server/package.json b/files/archive/vizz/docker/web/node_server/package.json similarity index 100% rename from files/vizz/docker/web/node_server/package.json rename to files/archive/vizz/docker/web/node_server/package.json diff --git a/files/vizz/docker/web/node_server/server.js b/files/archive/vizz/docker/web/node_server/server.js similarity index 100% rename from files/vizz/docker/web/node_server/server.js rename to files/archive/vizz/docker/web/node_server/server.js diff --git a/files/vizz/docker/web/proxy/nginx.conf b/files/archive/vizz/docker/web/proxy/nginx.conf similarity index 100% rename from files/vizz/docker/web/proxy/nginx.conf rename to files/archive/vizz/docker/web/proxy/nginx.conf diff --git a/files/docker/apis/StorageSummary/Routes.py b/files/docker/apis/StorageSummary/Routes.py index 2fe819b..a574430 100644 --- a/files/docker/apis/StorageSummary/Routes.py +++ b/files/docker/apis/StorageSummary/Routes.py @@ -12,7 +12,6 @@ from requests import RequestException, Response # import needed Class Libraries from Storage import * from Helpers import * -#SummaryServer = DriveHealthServer(get_hostname()) SummaryServer = load_state() @@ -20,12 +19,12 @@ if SummaryServer is None: SummaryServer = DriveHealthServer(get_hostname()) print("Created new SummaryServer") -# declare flask apps +# declare flask app app = Flask(__name__) -#scheduler = APScheduler() - +############################ # Flask routes +############################ # client update @app.route('/storage_client_update', methods=['POST']) @@ -49,7 +48,6 @@ def storage_client_delete(): print(result) return jsonify(result) - # client details @app.route('/client_details', methods=['GET']) def client_details(): @@ -86,20 +84,15 @@ def test_route(): "DriveHealthServer": f"{SummaryServer}" }) - # test route 2 @app.route('/test_storage_summary', methods=['GET']) def test_storage_summary(): - return jsonify({ - "message": "Hello world!", - "hostname": get_hostname(), - "DriveHealthServer": f"{SummaryServer}" - }) - - + return test_route() +############################ # Route Helpers +############################ # helper function for client_update route # handles the submission data from the flask route @@ -112,6 +105,12 @@ def client_update_helper(payload: dict): result = client_processor(processed_payload) return result +# client processing function, add/update logic in Class Methods +def client_processor(client_dict: dict): + result = SummaryServer.process_client_data(client_dict) + save_state(SummaryServer) + return result + # handle submission from remove route def client_remove_helper(payload: dict): result = None @@ -147,30 +146,12 @@ def post_processor(client_dict: dict, required_keys: dict): client_dict["processed_at"] = time.time() return client_dict -# Main functions - -# client processing function, add/update logic in Class Methods -def client_processor(client_dict: dict): - result = SummaryServer.process_client_data(client_dict) - save_state(SummaryServer) - return result - -def background_loop(): - return True +############################ +# Main function +############################ def run_main(): - #if SummaryServer is none: - - #atexit.register(lambda: save_state(SummaryServer)) test - # Flask scheduler for background loop, run if requested - #scheduler.add_job(id='background_loop', - # func=background_loop, - # trigger='interval', - # seconds=60) - #scheduler.init_app(app) - #scheduler.start() # Flask API - background_loop() app.run(debug=False, host='0.0.0.0', port=5001) diff --git a/files/docker/apis/StorageSummary/__pycache__/Routes.cpython-313.pyc b/files/docker/apis/StorageSummary/__pycache__/Routes.cpython-313.pyc index 36b9b84..ed1dd4f 100644 Binary files a/files/docker/apis/StorageSummary/__pycache__/Routes.cpython-313.pyc and b/files/docker/apis/StorageSummary/__pycache__/Routes.cpython-313.pyc differ diff --git a/files/docker/apis/StorageSummary/__pycache__/Storage.cpython-313.pyc b/files/docker/apis/StorageSummary/__pycache__/Storage.cpython-313.pyc index 89e6d45..60b39a4 100644 Binary files a/files/docker/apis/StorageSummary/__pycache__/Storage.cpython-313.pyc and b/files/docker/apis/StorageSummary/__pycache__/Storage.cpython-313.pyc differ diff --git a/files/docker/apis/StorageSummary/app.py b/files/docker/apis/StorageSummary/app.py index be1de3c..c036897 100644 --- a/files/docker/apis/StorageSummary/app.py +++ b/files/docker/apis/StorageSummary/app.py @@ -1,4 +1,5 @@ # main function for storage API +# this is the barest of bones to load the api # import class libraries from Routes import * diff --git a/files/docker/apis/StorageSummary/storage_api_state.pkl b/files/docker/apis/StorageSummary/storage_api_state.pkl new file mode 100644 index 0000000..206cff5 Binary files /dev/null and b/files/docker/apis/StorageSummary/storage_api_state.pkl differ diff --git a/files/docker/web/html/index.php b/files/docker/web/html/index.php index bfe7cf4..fe07203 100644 --- a/files/docker/web/html/index.php +++ b/files/docker/web/html/index.php @@ -9,7 +9,7 @@ date_default_timezone_set('America/Los_Angeles'); $remove_hosts = []; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { if ($_POST['action'] === 'remove') { - // The Remove form sends a comma‑separated string of short_id’s + // The Remove form sends a comma-separated string of short_id’s if (!empty($_POST['remove_hosts'])) { $remove_hosts = array_filter( explode(',', $_POST['remove_hosts']), @@ -156,7 +156,7 @@ if (!in_array($mode, $validModes, true)) { $apiConfig = [ 'cosmostat' => ['bind' => '10.200.27.20', 'port' => '5000'], /*'gali' => ['bind' => '10.200.27.20', 'port' => '5000'], // same as cosmostat*/ - 'drive_health' => ['bind' => '0.0.0.0', 'port' => '5001'], // new API + 'drive_health' => ['bind' => '172.25.1.18', 'port' => '5001'], // new API ]; /* ---------- Helper: fetch client details ---------- */ @@ -237,9 +237,6 @@ $systemComponents = $properties['system_components'] ?? []; $selectedHost = $clients[$selectedIdx]['hostname'] ?? 'Unknown'; - -/* ---- ---- */ - /* ---- Sidebar Renderer ---- */ function renderSidebar(string $mode){ @@ -255,74 +252,54 @@ function renderSidebar(string $mode){ @@ -344,7 +321,8 @@ function renderMainContent(string $mode){ echo '

No hosts selected.

'; return; } - echo '
'; + echo ' +
'; foreach ($selectedHosts as $sid) { // Find the client that matches this short_id $c = null; @@ -357,41 +335,49 @@ function renderMainContent(string $mode){ if ($c === null) continue; // safety $hostname = $c['name'] ?? 'Unknown'; - echo '
- '; - echo '

Drive Health - ' . h($hostname) . '

-

IP: '.h($c['ip']).'
- Timestamp: '.date('F j, Y g:i a', (int) $c['timestamp']).'

- '; + echo ' +
+

Drive Health - ' . h($hostname) . '

+

IP: '.h($c['ip']).'
+ Timestamp: '.date('F j, Y g:i a', (int) $c['timestamp']).'

+ '; if (isset($c['drives']) && is_array($c['drives']) && count($c['drives']) > 0) { - echo ''; - echo ' - '; - echo ' - '; + echo ' +
Drive LetterDisk IDHealth StatusModelCapacityPower On HoursHost WritesWear Level
+ + + + '; foreach ($c['drives'] as $drive) { - echo ' - '; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ' - '; + $wear_gb = ''; + if (isset($drive['host_writes'])){ + $wear_gb = $drive['host_writes'].' wear'; + } + echo ' + + + + + + + + '; } - echo '
Drive LetterDisk IDHealth MetricsModelSerialCapacity
' . h($drive['drive_letter'] ?? '') . '' . h("".$drive['disk_id'] ?? '') . '' . h($drive['health_status'] ?? '') . '' . h($drive['model'] ?? '') . '' . h($drive['capacity'] ?? '') . '' . h($drive['power_on_hours'] ?? '') . '' . h($drive['host_writes'] ?? '') . '' . h($drive['wear_level'] ?? '') . '
' . h($drive['drive_letter'] ?? '') . '' . h("".$drive['disk_id'] ?? '') . '' . + h($drive['health_status'] ?? '').'
'. + h($drive['power_on_hours'] ?? '').'
'. + h($wear_gb ?? ''). + '
' . h($drive['model'] ?? '') . '' . h($drive['serial'] ?? '') . '' . h($drive['capacity'] ?? '') . '
- '; + echo ' + '; } else { echo '

No drive data available for this host.

'; } - echo '
'; + echo ' +
'; } - echo '
'; + echo ' +
'; return; } ?> @@ -477,10 +463,12 @@ function renderMainContent(string $mode){
- - - - + + + + + +
@@ -507,11 +495,11 @@ function renderMainContent(string $mode){ diff --git a/files/docker/web/html/src/styles.css b/files/docker/web/html/src/styles.css index 0140e04..221f96a 100644 --- a/files/docker/web/html/src/styles.css +++ b/files/docker/web/html/src/styles.css @@ -6,12 +6,25 @@ --bg-body: #2c3e50; /* main page background */ --bg-card: #34495e; /* card / panel background */ --bg-sidebar: #3d566e; /* sidebar background (slightly lighter) */ - /* Accent / link colour */ + /* Accent / link color */ --clr-accent: #3498db; /* blue accent for links */ - /* Text colour */ + /* Text color */ --clr-text: #ecf0f1; /* light whiteish text */ /* Borders / accents */ - --clr-border: #1f2b38; /* dark border colour */ + --clr-border: #1f2b38; /* dark border color */ + /* button colors*/ + --clr-btn-bg: var(--bg-card); /* default button background */ + --clr-btn-text: var(--clr-text); /* button text color */ + --clr-btn-border: var(--clr-border); /* border color */ + --clr-btn-accent: var(--clr-accent); /* hover/focus color */ + --clr-btn-shadow: rgba(0,0,0,0.3); + /* select colors */ + --clr-select-bg: var(--bg-card); /* dropdown background */ + --clr-select-text: var(--clr-text); /* option text colour */ + --clr-select-border: var(--clr-border); + --clr-select-accent: var(--clr-accent);/* focus / hover border */ + --clr-select-shadow: rgba(0,0,0,.3); + } * { box-sizing: border-box; } @@ -84,7 +97,7 @@ table, th, td { } th, td { padding: 10px; } -/* Alternate row colour for metrics table */ +/* Alternate row color for metrics table */ #host_metrics_table tbody tr td:nth-of-type(even) { background: #3e5c78; /* slight contrast */ } @@ -154,7 +167,7 @@ li { margin-bottom: 10px; color: var(--clr-text); } margin-left: 6px; margin-right: 8px; vertical-align: middle; - background: #808080; /* default – unknown / no timestamp */ + background: #808080; /* default - unknown / no timestamp */ transition: background-color 1s linear; /* smooth fade */ } @@ -258,3 +271,127 @@ li { margin-bottom: 10px; color: var(--clr-text); } } +/* lets make buttons nice*/ + +/* 2. Base button style */ +.btn { + display: inline-block; + padding: .55rem 1.25rem; + font-size: .9rem; + font-weight: 500; + line-height: 1.4; + color: var(--clr-btn-text); + background: var(--clr-btn-bg); + border: 1px solid var(--clr-btn-border); + border-radius: .25rem; + cursor: pointer; + transition: background .15s ease, box-shadow .15s ease, transform .075s ease; + text-align: center; + user-select: none; + vertical-align: middle; +} + +/* 3. Primary button - accent color */ +.btn-primary { + background: var(--clr-btn-accent); + border-color: var(--clr-btn-accent); + color: #fff; /* brighter text for dark button */ +} + +/* 4. Secondary button - subtle background */ +.btn-secondary { + background: transparent; + border-color: var(--clr-btn-accent); + color: var(--clr-btn-accent); +} + +/* 5. Hover & focus states */ +.btn:hover, +.btn:focus-visible { + background: #2b3f4c; /* a slightly darker shade of card bg */ + box-shadow: 0 3px 8px var(--clr-btn-shadow); + transform: translateY(-1px); + outline: 0; +} + +/* 6. Disabled state */ +.btn:disabled, +.btn[aria-disabled="true"] { + opacity: .55; + cursor: not-allowed; + background: var(--bg-card); + border-color: var(--clr-border); +} + +/* 7. Extra - block‑level button */ +.btn-block { + display: block; + width: 100%; + text-align: center; +} + +/* let's also make the style */ +.select-dark { + appearance: none; /* hide native arrow in Chrome/Edge/FF */ + -webkit-appearance: none; + -moz-appearance: none; + + display: inline-block; + padding: .45rem 1.25rem .45rem .75rem; + font-size: .9rem; + line-height: 1.4; + color: var(--clr-select-text); + background: var(--clr-select-bg); + border: 1px solid var(--clr-select-border); + border-radius: .25rem; + cursor: pointer; + position: relative; + min-width: 150px; /* optional - forces a decent width */ + + /* Custom arrow - positioned after the element */ + background-image: linear-gradient(45deg, transparent 50%, var(--clr-select-accent) 50%), + linear-gradient(135deg, var(--clr-select-accent) 50%, transparent 50%), + linear-gradient(to right, var(--clr-select-border), var(--clr-select-border)); + background-position: + calc(100% - 20px) calc(50% + 2px), + calc(100% - 15px) calc(50% + 2px), + calc(100% - 3px) 50%; + background-size: + 5px 5px, + 5px 5px, + 1px 1.5rem; + background-repeat: no-repeat; +} + +/* Hover & focus - subtle accent border */ +.select-dark:hover, +.select-dark:focus-visible { + border-color: var(--clr-select-accent); + box-shadow: 0 0 3px var(--clr-select-shadow); + outline: none; /* rely on the box-shadow for focus */ +} + +/* Disabled state - greys out the control */ +.select-dark:disabled, +.select-dark[aria-disabled="true"] { + opacity: .55; + cursor: not-allowed; + background: var(--bg-card); + border-color: var(--clr-border); +} + +/*