storage api python service templated and port added to jenkinsfile

This commit is contained in:
2025-10-26 20:19:08 -07:00
parent a483c5eae1
commit 0e694894ca
16 changed files with 338 additions and 70 deletions

View File

@ -0,0 +1,11 @@
---
windows_base_packages:
- googlechrome
- 7zip
- vlc
- windirstat
...

View File

@ -0,0 +1,15 @@
---
- name: Install base packages
win_chocolatey:
name:
- "{{ windows_base_packages_item }}"
state: present
loop: "{{ windows_base_packages }}"
loop_control:
loop_var: windows_base_packages_item
...

View File

@ -9,4 +9,14 @@
debug:
msg: "Hostname: {{ hostname_output.stdout_lines[0] }}"
- name: Test API
win_shell: "C:\\Windows\\system32\\curl --silent http://{{ ansible_ssh_host }}:5000/disk"
register: api_test_output
- name: Show Test Results
debug:
msg: "{{ api_test_output.stdout_lines }}"
...

View File

@ -0,0 +1,109 @@
from flask import Flask, jsonify
import subprocess
import os
import socket
import psutil
hostname = socket.gethostname()
app = Flask(__name__)
# refresh the text file
def refresh_crystal_info():
try:
# Run CrystalDiskInfo command to generate DiskInfo.txt
return subprocess.run(["DiskInfo64.exe", "/CopyExit"])
except Exception as e:
return {"error": str(e)}
def get_crystal_disk_info():
try:
# Read the generated DiskInfo.txt file
with open("DiskInfo.txt", "r") as file:
output = file.read()
# Initialize a list to hold each drive's data
drives = []
# Split the file content into sections for each drive
drive_sections = output.split('----------------------------------------------------------------------------')
for section in drive_sections:
lines = section.strip().splitlines()
data = {
"Hostname": None,
"Disk Size": None,
"Model": None,
"Serial Number": None,
"Firmware": None,
"Temperature": None,
"Health Status": None,
"Power On Hours": None,
"Power On Count": None,
"Host Writes": None,
"Wear Level Count": None,
"Drive Letter": None
}
for line in lines:
if "Model" in line:
if ":" in line:
data["Model"] = line.split(":", 1)[1].strip()
elif "Serial Number" in line:
if ":" in line:
data["Serial Number"] = line.split(":", 1)[1].strip()
elif "Firmware" in line:
if ":" in line:
data["Firmware"] = line.split(":", 1)[1].strip()
elif "Temperature" in line:
if ":" in line:
data["Temperature"] = line.split(":", 1)[1].strip()
elif "Health Status" in line:
if ":" in line:
data["Health Status"] = line.split(":", 1)[1].strip()
elif "Power On Hours" in line:
if ":" in line:
data["Power On Hours"] = line.split(":", 1)[1].strip()
elif "Power On Count" in line:
if ":" in line:
data["Power On Count"] = line.split(":", 1)[1].strip()
elif "Host Writes" in line:
if ":" in line:
data["Host Writes"] = line.split(":", 1)[1].strip()
elif "Wear Level Count" in line:
if ":" in line:
data["Wear Level Count"] = line.split(":", 1)[1].strip()
elif "Drive Letter" in line:
if ":" in line:
data["Drive Letter"] = line.split(":", 1)[1].strip()
elif "Disk Size" in line:
if ":" in line:
data["Disk Size"] = line.split(":", 1)[1].strip()
if any(value is not None for value in data.values()):
drives.append(data)
data["Hostname"] = hostname
if not drives:
raise ValueError("No drive data found")
return {"drives": drives}
except Exception as e:
refresh_crystal_info()
return {"error": str(e)}
def bytes_to_human_readable(bytes):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0:
return f"{bytes:.2f} {unit}"
bytes /= 1024.0
@app.route('/udpate_cache', methods=['GET'])
def udpate_cache():
return jsonify(refresh_crystal_info())
@app.route('/drive_health', methods=['GET'])
def drive_health():
return jsonify(get_crystal_disk_info())
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -4,16 +4,11 @@
- name: show user vars
debug:
msg:
- "User email:"
- "{{ jenkins_user}}"
- "Jenkins Group:"
- "{{ jenkins_group}}"
- "SERVER_SUBNET_GROUP:"
- "{{ SERVER_SUBNET_GROUP }}"
- "subnet_group_check:"
- "{{ subnet_group_check }}"
- "Host IP:"
- "{{ ansible_ssh_host }}"
- "User email............ {{ jenkins_user}}"
- "Jenkins Group......... {{ jenkins_group}}"
- "SERVER_SUBNET_GROUP... {{ SERVER_SUBNET_GROUP }}"
- "subnet_group_check.... {{ subnet_group_check }}"
- "Host IP............... {{ ansible_ssh_host }}"
...

View File

@ -0,0 +1 @@
This role will install an API at port 5000 to get disk capacity data

View File

@ -1,7 +1,5 @@
---
#python_venv: "C:\Python39\Scripts"
cosmos_root_folder: "C:\\programdata\\cosmos"
python_service_root: "{{ cosmos_root_folder }}\\python"
@ -18,4 +16,6 @@ nssm_folder: "{{ cosmos_root_folder }}\\nssm"
disk_service_name: "disk_api"
api_service_port: "5000"
...

View File

@ -1,34 +0,0 @@
from flask import Flask, jsonify
import psutil
app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
def bytes_to_human_readable(bytes):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0:
return f"{bytes:.2f} {unit}"
bytes /= 1024.0
def get_disk_info():
disk_info = []
partitions = psutil.disk_partitions()
for partition in partitions:
usage = psutil.disk_usage(partition.mountpoint)
disk_info.append({
'device': partition.device.replace('\\\\', '\\').rstrip('\\'),
#'mountpoint': partition.mountpoint,
#'fstype': partition.fstype,
'total': bytes_to_human_readable(usage.total),
'used': bytes_to_human_readable(usage.used),
'free': bytes_to_human_readable(usage.free),
'percent': usage.percent
})
return disk_info
@app.route('/disk', methods=['GET'])
def disk():
return jsonify(get_disk_info())
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -12,7 +12,8 @@
- name: set up nssm service
include_tasks: nssm.yaml
- name: set up scheduled task
include_tasks: update_task.yaml
...

View File

@ -1,5 +1,15 @@
---
- name: Copy CrystalDiskInfo archive
ansible.windows.win_copy:
src: /var/jenkins_home/ansible-files/programs/CrystalDiskInfo.zip
dest: "{{ storage_api_root }}\\CrystalDiskInfo.zip"
- name: Extract CrystalDiskInfo archive
community.windows.win_unzip:
src: "{{ storage_api_root }}\\CrystalDiskInfo.zip"
dest: "{{ storage_api_root }}\\dist\\"
- name: Install nssm
win_chocolatey:
name: nssm
@ -20,6 +30,20 @@
- name: Show service result
debug:
msg:
- "{{ disk_service_status }}"
- "name........ {{ disk_service_status.name }}"
- "exists...... {{ disk_service_status.exists }}"
- "path........ {{ disk_service_status.path }}"
- "start_mode.. {{ disk_service_status.start_mode }}"
- "state....... {{ disk_service_status.state }}"
- "username.... {{ disk_service_status.username }}"
- "failed...... {{ disk_service_status.failed }}"
- name: Test API
win_shell: "C:\\Windows\\system32\\curl http://{{ ansible_ssh_host }}:5000/disk"
register: api_test_output
- name: Show Test Results
debug:
msg: "{{ api_test_output.stdout_lines }}"
...

View File

@ -1,17 +1,26 @@
---
- name: Create directory structure
- name: Create service working folder
ansible.windows.win_file:
path: "{{ storage_api_root }}"
state: directory
- name: Stop service if running
ignore_errors: yes
ansible.windows.win_service:
name: "{{ disk_service_name }}"
state: paused
state: stopped
- name: Check hostname
ansible.windows.win_command: hostname
register: hostname_output
- name: display hostname
debug:
msg: "Hostname: {{ hostname_output.stdout_lines[0] }}"
- name: Copy disk_service.py
ansible.windows.win_copy:
ansible.windows.win_template:
src: disk_service.py
dest: "{{ storage_api_root }}\\disk_service.py"
@ -35,6 +44,4 @@
state: present
enabled: true
...

View File

@ -28,16 +28,4 @@
args:
chdir: "{{ python_venv }}"
#- name: Upgrade pip in the virtual environment
# win_shell: "{{ python_venv }}\\pip install --upgrade pip"
# args:
# chdir: "{{ python_venv }}"
#
#- name: Install Python dependencies from requirements.txt
# win_shell: "{{ python_venv }}\\pip install -r {{ python_venv }}\\requirements.txt"
# args:
# chdir: "{{ python_venv }}"
...

View File

@ -0,0 +1,20 @@
---
- name: Create Scheduled Task to run crystaldiskinfo
win_scheduled_task:
name: Update CrystalDiskInfo Cache File
username: SYSTEM
actions:
- path: "{{ storage_api_root }}\\dist\\DiskInfo64.exe"
arguments: |
/CopyExit
triggers:
- type: registration
- type: daily
start_boundary: '{{ ansible_date_time.date }}T01:00:00'
- type: boot
state: present
enabled: yes
...

View File

@ -0,0 +1,118 @@
from flask import Flask, jsonify
import psutil
import os
app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
# Bits to Bytes etc
def bytes_to_human_readable(bytes):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0:
return f"{bytes:.2f} {unit}"
bytes /= 1024.0
# Parse cache and return results
def get_crystal_disk_info():
try:
# Read the generated DiskInfo.txt file
with open("DiskInfo.txt", "r") as file:
output = file.read()
# Initialize a list to hold each drive's data
drives = []
# Split the file content into sections for each drive
drive_sections = output.split('----------------------------------------------------------------------------')
for section in drive_sections:
lines = section.strip().splitlines()
data = {
"Hostname": None,
"Disk Size": None,
"Model": None,
"Serial Number": None,
"Firmware": None,
"Temperature": None,
"Health Status": None,
"Power On Hours": None,
"Power On Count": None,
"Host Writes": None,
"Wear Level Count": None,
"Drive Letter": None
}
for line in lines:
if "Model" in line:
if ":" in line:
data["Model"] = line.split(":", 1)[1].strip()
elif "Serial Number" in line:
if ":" in line:
data["Serial Number"] = line.split(":", 1)[1].strip()
elif "Firmware" in line:
if ":" in line:
data["Firmware"] = line.split(":", 1)[1].strip()
elif "Temperature" in line:
if ":" in line:
data["Temperature"] = line.split(":", 1)[1].strip()
elif "Health Status" in line:
if ":" in line:
data["Health Status"] = line.split(":", 1)[1].strip()
elif "Power On Hours" in line:
if ":" in line:
data["Power On Hours"] = line.split(":", 1)[1].strip()
elif "Power On Count" in line:
if ":" in line:
data["Power On Count"] = line.split(":", 1)[1].strip()
elif "Host Writes" in line:
if ":" in line:
data["Host Writes"] = line.split(":", 1)[1].strip()
elif "Wear Level Count" in line:
if ":" in line:
data["Wear Level Count"] = line.split(":", 1)[1].strip()
elif "Drive Letter" in line:
if ":" in line:
data["Drive Letter"] = line.split(":", 1)[1].strip()
elif "Disk Size" in line:
if ":" in line:
data["Disk Size"] = line.split(":", 1)[1].strip()
if any(value is not None for value in data.values()):
drives.append(data)
data["Hostname"] = {{ hostname_output.stdout_lines[0] }}
if not drives:
raise ValueError("No drive data found")
return {"drives": drives}
except Exception as e:
return {"error": str(e)}
# Disk Info Function
def get_disk_info():
disk_info = []
partitions = psutil.disk_partitions()
for partition in partitions:
usage = psutil.disk_usage(partition.mountpoint)
disk_info.append({
'device': partition.device.replace('\\\\', '\\').rstrip('\\'),
#'mountpoint': partition.mountpoint,
#'fstype': partition.fstype,
'total': bytes_to_human_readable(usage.total),
'used': bytes_to_human_readable(usage.used),
'free': bytes_to_human_readable(usage.free),
'percent': usage.percent
})
return disk_info
# Flask endpoints
@app.route('/disk', methods=['GET'])
def disk():
return jsonify(get_disk_info())
@app.route('/health', methods=['GET'])
def drive_health():
return jsonify(get_crystal_disk_info())
if __name__ == '__main__':
app.run(host='0.0.0.0', port={{ api_service_port }})
udp