client api updated
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
Flask
|
Flask
|
||||||
flask_apscheduler
|
flask_apscheduler
|
||||||
psutil
|
psutil
|
||||||
requests
|
requests
|
||||||
|
pywin32
|
||||||
|
wmi
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 ISO‑8601 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 }})
|
||||||
|
|||||||
Reference in New Issue
Block a user