storage api python service templated and port added to jenkinsfile
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
These will be my windows ansible environment
|
||||
Matt-Cloud Windows Ansible Platform
|
||||
|
||||
I need to put it up on Github to sync jenkinsfiles
|
||||
This will run in my jenkins instance at https://jenkins.matt-cloud.com
|
||||
|
||||
Since I am building this out after getting experienced in ansible & jenkins, I will set it up securely so I can share it all
|
||||
@ -4,7 +4,7 @@ pipeline {
|
||||
// Define parameters
|
||||
parameters {
|
||||
string(name: 'host_ip', description: 'Target System Address')
|
||||
// string(name: 'new_hostname', description: 'Update Hostname')
|
||||
string(name: 'api_service_port', defaultValue: "5000", description: 'API Service Port, probably don\'t change this')
|
||||
// reference for later
|
||||
// choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: 'Environment to deploy to')
|
||||
// booleanParam(name: 'rename_host', defaultValue: true, description: 'When checked hostname will be renamed')
|
||||
@ -53,7 +53,8 @@ pipeline {
|
||||
cd /var/jenkins_home/ansible-windows
|
||||
|
||||
ansible-playbook -i \$inventory_file \$playbook_file \
|
||||
--ssh-common-args='-o StrictHostKeyChecking=no'
|
||||
--ssh-common-args='-o StrictHostKeyChecking=no' \
|
||||
--extra-vars "api_service_port=${params.api_service_port}"
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
11
roles/cosmos_init/defaults/main.yaml
Normal file
11
roles/cosmos_init/defaults/main.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
|
||||
windows_base_packages:
|
||||
- googlechrome
|
||||
- 7zip
|
||||
- vlc
|
||||
- windirstat
|
||||
|
||||
|
||||
|
||||
...
|
||||
15
roles/cosmos_init/tasks/main.yaml
Normal file
15
roles/cosmos_init/tasks/main.yaml
Normal 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
|
||||
|
||||
|
||||
|
||||
...
|
||||
@ -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 }}"
|
||||
|
||||
|
||||
...
|
||||
109
roles/health_api/files/app.py
Normal file
109
roles/health_api/files/app.py
Normal 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)
|
||||
@ -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 }}"
|
||||
|
||||
|
||||
...
|
||||
1
roles/storage_api/README.md
Normal file
1
roles/storage_api/README.md
Normal file
@ -0,0 +1 @@
|
||||
This role will install an API at port 5000 to get disk capacity data
|
||||
@ -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"
|
||||
|
||||
...
|
||||
@ -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)
|
||||
@ -12,7 +12,8 @@
|
||||
|
||||
- name: set up nssm service
|
||||
include_tasks: nssm.yaml
|
||||
|
||||
- name: set up scheduled task
|
||||
include_tasks: update_task.yaml
|
||||
|
||||
|
||||
|
||||
...
|
||||
@ -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 }}"
|
||||
|
||||
...
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
...
|
||||
@ -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 }}"
|
||||
|
||||
...
|
||||
20
roles/storage_api/tasks/update_task.yaml
Normal file
20
roles/storage_api/tasks/update_task.yaml
Normal 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
|
||||
|
||||
...
|
||||
118
roles/storage_api/templates/disk_service.py
Normal file
118
roles/storage_api/templates/disk_service.py
Normal 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
|
||||
Reference in New Issue
Block a user