client api updated

This commit is contained in:
2026-04-26 14:36:25 -07:00
parent b4839fde66
commit 44c492313c
4 changed files with 134 additions and 14 deletions

View File

@ -2,3 +2,5 @@ Flask
flask_apscheduler flask_apscheduler
psutil psutil
requests requests
pywin32
wmi

View File

@ -41,7 +41,7 @@
- "failed...... {{ disk_service_status.failed }}" - "failed...... {{ disk_service_status.failed }}"
- name: Test API - name: Test API
win_shell: "C:\\Windows\\system32\\curl http://{{ ansible_ssh_host }}:5000/disk" win_shell: "C:\\Windows\\system32\\curl http://{{ ansible_ssh_host }}:5000/test"
register: api_test_output register: api_test_output
- name: Show Test Results - name: Show Test Results

View File

@ -6,7 +6,7 @@
state: present state: present
- name: Purge venv if asked - name: Purge venv if asked
when: purge_venv | bool when: purge_venv | bool and not refresh_api | bool
block: block:
- name: Remove service - name: Remove service

View File

@ -1,3 +1,4 @@
# this got big...
from flask import Flask, jsonify from flask import Flask, jsonify
from flask_apscheduler import APScheduler from flask_apscheduler import APScheduler
import psutil import psutil
@ -6,12 +7,20 @@ import requests
from requests import RequestException, Response from requests import RequestException, Response
import json import json
from subprocess import check_output from subprocess import check_output
import win32api
from datetime import datetime, timedelta
import sys
from pathlib import Path
import wmi
import pythoncom
import ctypes
from ctypes import wintypes
app = Flask(__name__) app = Flask(__name__)
scheduler = APScheduler() scheduler = APScheduler()
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
# Bits to Bytes etc # human readable bytes
def bytes_to_human_readable(bytes): def bytes_to_human_readable(bytes):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']: for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0: if bytes < 1024.0:
@ -111,8 +120,10 @@ def get_disk_info():
partitions = psutil.disk_partitions() partitions = psutil.disk_partitions()
for partition in partitions: for partition in partitions:
usage = psutil.disk_usage(partition.mountpoint) usage = psutil.disk_usage(partition.mountpoint)
drive_letter = partition.device.replace('\\\\', '\\').rstrip('\\')
disk_info.append({ disk_info.append({
'device': partition.device.replace('\\\\', '\\').rstrip('\\'), 'device': drive_letter,
'label': get_drive_label(drive_letter),
#'mountpoint': partition.mountpoint, #'mountpoint': partition.mountpoint,
'fstype': partition.fstype, 'fstype': partition.fstype,
'total': bytes_to_human_readable(usage.total), 'total': bytes_to_human_readable(usage.total),
@ -122,6 +133,101 @@ def get_disk_info():
}) })
return disk_info return disk_info
# drive label function
def get_drive_label(drive_letter: str) -> str:
result = "none"
root = drive_letter.strip()
if not root.endswith(':'):
root += ':'
if not root.endswith('\\'):
root += '\\'
# Make sure the drive actually exists
if not os.path.exists(root):
# Not a valid drive letter, return None so the caller can decide what to do.
print(f"[DEBUG] Drive '{root}' does not exist.")
result = "drive does not exist 0_o"
# Prepare buffers for the Win32 API call
volume_name_buf = ctypes.create_unicode_buffer(260) # MAX_PATH
fs_name_buf = ctypes.create_unicode_buffer(260)
serial_number = wintypes.DWORD()
max_component_len = wintypes.DWORD()
file_system_flags = wintypes.DWORD()
# Call GetVolumeInformationW
res = ctypes.windll.kernel32.GetVolumeInformationW(
ctypes.c_wchar_p(root), # lpRootPathName
volume_name_buf, # lpVolumeNameBuffer
ctypes.sizeof(volume_name_buf), # nVolumeNameSize
ctypes.byref(serial_number), # lpVolumeSerialNumber
ctypes.byref(max_component_len), # lpMaximumComponentLength
ctypes.byref(file_system_flags), # lpFileSystemFlags
fs_name_buf, # lpFileSystemNameBuffer
ctypes.sizeof(fs_name_buf) # nFileSystemNameSize
)
if res == 0: # The call failed
err = ctypes.get_last_error()
print(f"[ERROR] GetVolumeInformationW failed for '{root}'. "
f"Win32 error code: {err}")
result = "label error"
else:
result = volume_name_buf.value
if volume_name_buf.value == '':
result = "no_label"
return result
# os info function
def get_os_info() -> str:
result = "windows"
pythoncom.CoInitialize()
#try:
wmi_data = wmi.WMI()
os_info = wmi_data.Win32_OperatingSystem()[0]
#return {
# "Name" : os_info.Name,
# "Version" : os_info.Version,
# "BuildNumber": os_info.BuildNumber,
# "InstallDate": os_info.InstallDate,
# "ProductType": int(os_info.ProductType)
#}
# 1. Major version + edition (e.g. "10 Pro")
# os_info.Caption → "Microsoft Windows 10 Pro"
parts = os_info.Caption.split()
major = parts[2] # "10"
edition = parts[3] # "Pro" (for server: "2019", etc.)
major_edition = f"{major} {edition}"
# 2. Build number
build = os_info.BuildNumber
# 3. Install date (WMI gives an ISO8601 string)
# e.g. "20210930142300.000000+000"
install_ts = os_info.InstallDate[:14] # "20210930142300"
dt = datetime.strptime(install_ts, "%Y%m%d%H%M%S")
install_date = f"{dt.month}-{dt.day}-{dt.year}"
result = f"Windows {major_edition} - Build {build} - Installed {install_date}"
#except Exception as e:
# print(e)
# result = "wmi_error"
return result
# server reporter info
def get_server_info() -> dict:
result = {}
drives_dict = get_crystal_disk_info()
data_dict = {
"hostname": "{{ hostname_output.stdout_lines[0] }}",
"os_string": get_os_info(),
"drives": drives_dict["drives"],
"API_KEY": "deadbeef",
"storage_summary": get_disk_info()
}
result = data_dict
return result
# Flask endpoints # Flask endpoints
@app.route('/disk', methods=['GET']) @app.route('/disk', methods=['GET'])
@ -132,18 +238,23 @@ def disk():
def drive_health(): def drive_health():
return jsonify(get_crystal_disk_info()) return jsonify(get_crystal_disk_info())
@app.route('/full', methods=['GET'])
def full_summary():
return jsonify(get_server_info())
@app.route('/test', methods=['GET'])
def test_flask():
return jsonify({
"message": "hello world",
"os_string": get_os_info()
})
def server_reporter(): def server_reporter():
#base_url="https://cosmostat.matt-cloud.com" #base_url="https://cosmostat.matt-cloud.com"
base_url="http://10.200.27.20:5001" base_url="http://10.200.27.20:5001"
url = f"{base_url}/storage_client_update" url = f"{base_url}/storage_client_update"
drives_dict = get_crystal_disk_info() data_dict = get_server_info()
data_dict = {
"hostname": "{{ hostname_output.stdout_lines[0] }}",
"drives": drives_dict["drives"],
"API_KEY": "deadbeef",
"storage_summary": get_disk_info()
}
result = [] result = []
try: try:
response = requests.post(url, json=data_dict) response = requests.post(url, json=data_dict)
@ -170,6 +281,13 @@ if __name__ == '__main__':
return result return result
# gonna try something wild # gonna try something wild
run_date = datetime.now() + timedelta(seconds=15)
scheduler.add_job(id='init_disk_info',
func=update_disk_info,
trigger='date',
run_date=run_date)
scheduler.add_job(id='update_disk_info', scheduler.add_job(id='update_disk_info',
func=update_disk_info, func=update_disk_info,
trigger='interval', trigger='interval',
@ -183,6 +301,6 @@ if __name__ == '__main__':
scheduler.init_app(app) scheduler.init_app(app)
scheduler.start() scheduler.start()
update_disk_info() #update_disk_info()
app.run(host='0.0.0.0', port={{ api_service_port }}) app.run(host='0.0.0.0', port={{ api_service_port }})