Compare commits
7 Commits
4751d96bf4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 280066436a | |||
| 099492e3a6 | |||
| 0e694894ca | |||
| a483c5eae1 | |||
| 0435dabd87 | |||
| 8411578068 | |||
| d71bb9b6d7 |
@ -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
|
||||||
@ -7,7 +7,7 @@ usage() {
|
|||||||
echo "Windows Ansible Dynamic Inventory File Generation Script"
|
echo "Windows Ansible Dynamic Inventory File Generation Script"
|
||||||
echo "Usage: $0 -i IP_LIST -u JENKINS_USER -g JENKINS_GROUP -w WINDOWS_USER -p ANSIBLE_PASSWORD [-a SERVER_SUBNET_GROUP] [-s] [-v] [-e]"
|
echo "Usage: $0 -i IP_LIST -u JENKINS_USER -g JENKINS_GROUP -w WINDOWS_USER -p ANSIBLE_PASSWORD [-a SERVER_SUBNET_GROUP] [-s] [-v] [-e]"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -i IP_LIST Comma-separated list of IPs"
|
echo " -i IP_LIST Comma-separated list of IPs. Will not fail if blank, but why 0_o"
|
||||||
echo " -u JENKINS_USER Jenkins user"
|
echo " -u JENKINS_USER Jenkins user"
|
||||||
echo " -g JENKINS_GROUP Jenkins primary group"
|
echo " -g JENKINS_GROUP Jenkins primary group"
|
||||||
echo " -a SERVER_SUBNET_GROUP Jenkins group for SSH access, need to pass something when called"
|
echo " -a SERVER_SUBNET_GROUP Jenkins group for SSH access, need to pass something when called"
|
||||||
@ -62,7 +62,7 @@ while getopts ":i:u:w:p:g:a:svq" opt; do
|
|||||||
done
|
done
|
||||||
shift $((OPTIND -1))
|
shift $((OPTIND -1))
|
||||||
# Check if all required options are provided
|
# Check if all required options are provided
|
||||||
if [ -z "$IP_LIST" ] || [ -z "$JENKINS_USER" ] || [ -z "$JENKINS_GROUP" ] || [ -z "$WINDOWS_USER" ] || [ -z "$ANSIBLE_PASSWORD" ]; then
|
if [ -z "$JENKINS_USER" ] || [ -z "$JENKINS_GROUP" ] || [ -z "$WINDOWS_USER" ] || [ -z "$ANSIBLE_PASSWORD" ]; then
|
||||||
usage
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -112,14 +112,38 @@ all:
|
|||||||
"
|
"
|
||||||
|
|
||||||
# Loop through each IP in the comma-separated list
|
# Loop through each IP in the comma-separated list
|
||||||
|
# skip if restricted user and subnet
|
||||||
IFS=',' read -ra IPS <<< "$IP_LIST"
|
IFS=',' read -ra IPS <<< "$IP_LIST"
|
||||||
for IP in "${IPS[@]}"; do
|
for IP in "${IPS[@]}"; do
|
||||||
ip_check=$(curl -s http://172.25.100.15:15010/ip_check?ip=${IP} | jq .in_subnets)
|
ip_check=$(curl -s http://172.25.100.15:15010/ip_check?ip=${IP} | jq .in_subnets)
|
||||||
echo $ip_check
|
# if this is a restricted subnet, then check the group
|
||||||
|
if $ip_check; then
|
||||||
inventory_content+=" ${IP}:
|
if ! $be_quiet; then
|
||||||
|
echo "Subnet restricted, checking group membership"
|
||||||
|
fi
|
||||||
|
if [ "$allsubnet_group" == "$SERVER_SUBNET_GROUP" ]; then
|
||||||
|
if ! $be_quiet; then
|
||||||
|
echo "IP Check Passed, adding endpoint ${IP} to inventory"
|
||||||
|
fi
|
||||||
|
inventory_content+=" ${IP}:
|
||||||
|
ansible_host: ${IP}
|
||||||
|
|
||||||
|
"
|
||||||
|
else
|
||||||
|
if ! $be_quiet; then
|
||||||
|
echo "Warning: User ${JENKINS_USER} not member of ${SERVER_SUBNET_GROUP}!"
|
||||||
|
echo "Auth Check Failed for endpoint ${IP}, not adding to inventory"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# if the subnet is not restricted, just add the endpoint to the inventory
|
||||||
|
else
|
||||||
|
if ! $be_quiet; then
|
||||||
|
echo "Unrestricted subnet, adding endpoint ${IP} to inventory"
|
||||||
|
fi
|
||||||
|
inventory_content+=" ${IP}:
|
||||||
ansible_host: ${IP}
|
ansible_host: ${IP}
|
||||||
"
|
"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
inventory_content+=" vars:
|
inventory_content+=" vars:
|
||||||
|
|||||||
75
jenkins/Jenkinsfile.disk_service
Normal file
75
jenkins/Jenkinsfile.disk_service
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
// Define parameters
|
||||||
|
parameters {
|
||||||
|
string(name: 'host_ip', description: 'Target System Address')
|
||||||
|
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')
|
||||||
|
// booleanParam(name: 'config_matt', defaultValue: true, description: 'config matt profile')
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
ANSIBLE_FORCE_COLOR = '1'
|
||||||
|
ansible_service_windows = credentials(' ansible-service-windows')
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Generate Inventory File') {
|
||||||
|
steps {
|
||||||
|
|
||||||
|
// Generate the dynamic inventory file
|
||||||
|
// Usage: $0 -i IP_LIST -u JENKINS_USER -g JENKINS_GROUP -w WINDOWS_USER -p ANSIBLE_PASSWORD [-a SERVER_SUBNET_GROUP] [-s] [-v] [-e]"
|
||||||
|
sh """
|
||||||
|
|
||||||
|
jenkins_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep -v \$SERVER_SUBNET_GROUP | grep Jenkins | head -n 1)
|
||||||
|
jenkins_subnet_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep -e authenticated -e \$SERVER_SUBNET_GROUP | sort -rf | head -n 1)
|
||||||
|
jenkins_user=\$(echo ${env.BUILD_USER})
|
||||||
|
cd /var/jenkins_home/ansible-windows
|
||||||
|
chmod +x /var/jenkins_home/ansible-windows/inventory/inventory.sh
|
||||||
|
/var/jenkins_home/ansible-windows/inventory/inventory.sh -a \$jenkins_subnet_group -g \$jenkins_group -u \$jenkins_user -w ${env.ansible_service_windows_USR} -p ${env.ansible_service_windows_PSW} -i ${params.host_ip}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Ansible Playbook') {
|
||||||
|
steps {
|
||||||
|
sh """
|
||||||
|
echo Generate Hash
|
||||||
|
echo ${params.host_ip}
|
||||||
|
hash=\$(echo -n ${params.host_ip} | md5sum | cut -c 1-8)
|
||||||
|
|
||||||
|
inventory_file="/var/jenkins_home/ansible-windows/.inv/inventory-\$hash.yml"
|
||||||
|
playbook_file="/var/jenkins_home/ansible-windows/playbooks/disk_service.yaml"
|
||||||
|
|
||||||
|
cd /var/jenkins_home/ansible-windows
|
||||||
|
|
||||||
|
ansible-playbook -i \$inventory_file \$playbook_file \
|
||||||
|
--ssh-common-args='-o StrictHostKeyChecking=no' \
|
||||||
|
--extra-vars "api_service_port=${params.api_service_port}"
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
// Remove dynamic Inventory file
|
||||||
|
sh """
|
||||||
|
hash=\$(echo -n "${params.host_ip}" | md5sum | cut -c 1-8)
|
||||||
|
inventory_file="/var/jenkins_home/ansible-windows/.inv/inventory-\$hash.yml"
|
||||||
|
rm \$inventory_file
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
73
jenkins/Jenkinsfile.server_init
Normal file
73
jenkins/Jenkinsfile.server_init
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
// Define parameters
|
||||||
|
parameters {
|
||||||
|
string(name: 'host_ip', description: 'Target System Address')
|
||||||
|
// 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')
|
||||||
|
// booleanParam(name: 'config_matt', defaultValue: true, description: 'config matt profile')
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
ANSIBLE_FORCE_COLOR = '1'
|
||||||
|
ansible_service_windows = credentials(' ansible-service-windows')
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Generate Inventory File') {
|
||||||
|
steps {
|
||||||
|
|
||||||
|
// Generate the dynamic inventory file
|
||||||
|
// Usage: $0 -i IP_LIST -u JENKINS_USER -g JENKINS_GROUP -w WINDOWS_USER -p ANSIBLE_PASSWORD [-a SERVER_SUBNET_GROUP] [-s] [-v] [-e]"
|
||||||
|
sh """
|
||||||
|
|
||||||
|
jenkins_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep -v \$SERVER_SUBNET_GROUP | grep Jenkins | head -n 1)
|
||||||
|
jenkins_subnet_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep -e authenticated -e \$SERVER_SUBNET_GROUP | sort -rf | head -n 1)
|
||||||
|
jenkins_user=\$(echo ${env.BUILD_USER})
|
||||||
|
cd /var/jenkins_home/ansible-windows
|
||||||
|
chmod +x /var/jenkins_home/ansible-windows/inventory/inventory.sh
|
||||||
|
/var/jenkins_home/ansible-windows/inventory/inventory.sh -a \$jenkins_subnet_group -g \$jenkins_group -u \$jenkins_user -w ${env.ansible_service_windows_USR} -p ${env.ansible_service_windows_PSW} -i ${params.host_ip}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Ansible Playbook') {
|
||||||
|
steps {
|
||||||
|
sh """
|
||||||
|
echo Generate Hash
|
||||||
|
echo ${params.host_ip}
|
||||||
|
hash=\$(echo -n ${params.host_ip} | md5sum | cut -c 1-8)
|
||||||
|
|
||||||
|
inventory_file="/var/jenkins_home/ansible-windows/.inv/inventory-\$hash.yml"
|
||||||
|
playbook_file="/var/jenkins_home/ansible-windows/playbooks/server_init.yaml"
|
||||||
|
|
||||||
|
cd /var/jenkins_home/ansible-windows
|
||||||
|
|
||||||
|
ansible-playbook -i \$inventory_file \$playbook_file \
|
||||||
|
--ssh-common-args='-o StrictHostKeyChecking=no'
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
// Remove dynamic Inventory file
|
||||||
|
sh """
|
||||||
|
hash=\$(echo -n "${params.host_ip}" | md5sum | cut -c 1-8)
|
||||||
|
inventory_file="/var/jenkins_home/ansible-windows/.inv/inventory-\$hash.yml"
|
||||||
|
rm \$inventory_file
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
playbooks/disk_service.yaml
Normal file
15
playbooks/disk_service.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
- name: Set up disk inventory service
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
become_method: runas
|
||||||
|
|
||||||
|
roles:
|
||||||
|
|
||||||
|
- show_user_vars
|
||||||
|
|
||||||
|
- cosmos_init
|
||||||
|
|
||||||
|
- storage_api
|
||||||
|
|
||||||
|
...
|
||||||
13
playbooks/server_init.yaml
Normal file
13
playbooks/server_init.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
- name: Matt-Cloud Windows VM Server Initialze
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
become_method: runas
|
||||||
|
|
||||||
|
roles:
|
||||||
|
|
||||||
|
- show_user_vars
|
||||||
|
|
||||||
|
- cosmos_init
|
||||||
|
|
||||||
|
...
|
||||||
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
# https://us.fanntik.top/product/fanttik-e1-max-precision-electric-screwdriver-5/
|
- name: Ansible Windows Test
|
||||||
- name: Ansible Test
|
|
||||||
hosts: all
|
hosts: all
|
||||||
become: yes
|
become: yes
|
||||||
become_method: runas
|
become_method: runas
|
||||||
|
|||||||
17
roles/cosmos_init/defaults/main.yaml
Normal file
17
roles/cosmos_init/defaults/main.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
windows_base_packages:
|
||||||
|
- googlechrome
|
||||||
|
- 7zip
|
||||||
|
- vlc
|
||||||
|
- windirstat
|
||||||
|
- putty
|
||||||
|
|
||||||
|
windows_features:
|
||||||
|
- NET-Framework-Features
|
||||||
|
- Telnet-Client
|
||||||
|
- Windows-Server-Backup
|
||||||
|
- RSAT-Print-Services
|
||||||
|
- RSAT-File-Services
|
||||||
|
|
||||||
|
...
|
||||||
48
roles/cosmos_init/tasks/main.yaml
Normal file
48
roles/cosmos_init/tasks/main.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Install Server Services
|
||||||
|
ansible.windows.win_feature:
|
||||||
|
name:
|
||||||
|
- "{{ windows_features_item }}"
|
||||||
|
state: present
|
||||||
|
loop: "{{ windows_features }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: windows_features_item
|
||||||
|
|
||||||
|
- name: Disable ms_tcpip6 of all the Interface
|
||||||
|
community.windows.win_net_adapter_feature:
|
||||||
|
interface: '*'
|
||||||
|
state: disabled
|
||||||
|
component_id:
|
||||||
|
- ms_tcpip6
|
||||||
|
|
||||||
|
- name: disable IE Enhanced Security
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: disable for user
|
||||||
|
win_shell: 'Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0'
|
||||||
|
|
||||||
|
- name: disable for admin
|
||||||
|
win_shell: 'Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0'
|
||||||
|
|
||||||
|
- name: disable automatic updates
|
||||||
|
ansible.windows.win_regedit:
|
||||||
|
path: HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU
|
||||||
|
name: NoAutoUpdate
|
||||||
|
data: 1
|
||||||
|
|
||||||
|
- name: disable Firewall
|
||||||
|
win_shell: "netsh advfirewall set allprofiles state off"
|
||||||
|
|
||||||
|
|
||||||
|
...
|
||||||
@ -9,4 +9,14 @@
|
|||||||
debug:
|
debug:
|
||||||
msg: "Hostname: {{ hostname_output.stdout_lines[0] }}"
|
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 }}"
|
||||||
|
|
||||||
|
|
||||||
...
|
...
|
||||||
@ -4,16 +4,11 @@
|
|||||||
- name: show user vars
|
- name: show user vars
|
||||||
debug:
|
debug:
|
||||||
msg:
|
msg:
|
||||||
- "User email:"
|
- "User email............ {{ jenkins_user}}"
|
||||||
- "{{ jenkins_user}}"
|
- "Jenkins Group......... {{ jenkins_group}}"
|
||||||
- "Jenkins Group:"
|
- "SERVER_SUBNET_GROUP... {{ SERVER_SUBNET_GROUP }}"
|
||||||
- "{{ jenkins_group}}"
|
- "subnet_group_check.... {{ subnet_group_check }}"
|
||||||
- "SERVER_SUBNET_GROUP:"
|
- "Host IP............... {{ ansible_ssh_host }}"
|
||||||
- "{{ SERVER_SUBNET_GROUP }}"
|
|
||||||
- "subnet_group_check:"
|
|
||||||
- "{{ subnet_group_check }}"
|
|
||||||
- "Host IP:"
|
|
||||||
- "{{ ansible_ssh_host }}"
|
|
||||||
|
|
||||||
|
|
||||||
...
|
...
|
||||||
3
roles/storage_api/README.md
Normal file
3
roles/storage_api/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This role will install an API at port 5000 to get disk capacity data and disk health
|
||||||
|
|
||||||
|
This is used with the storage site visible to members of the disk-api group at groups.matt-cloud.com
|
||||||
21
roles/storage_api/defaults/main.yaml
Normal file
21
roles/storage_api/defaults/main.yaml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
cosmos_root_folder: "C:\\programdata\\cosmos"
|
||||||
|
|
||||||
|
python_service_root: "{{ cosmos_root_folder }}\\python"
|
||||||
|
|
||||||
|
storage_api_root: "{{ python_service_root }}\\disk_api"
|
||||||
|
|
||||||
|
python_venv: "{{storage_api_root}}\\venv"
|
||||||
|
|
||||||
|
python_venv_bin: "{{ python_venv }}\\Scripts\\python.exe"
|
||||||
|
|
||||||
|
python_venv_pip_bin: "{{ python_venv }}\\Scripts\\pip.exe"
|
||||||
|
|
||||||
|
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)
|
|
||||||
2
roles/storage_api/files/requirements.txt
Normal file
2
roles/storage_api/files/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Flask
|
||||||
|
psutil
|
||||||
19
roles/storage_api/tasks/main.yaml
Normal file
19
roles/storage_api/tasks/main.yaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Disk API Windows Service
|
||||||
|
###############################################
|
||||||
|
|
||||||
|
- name: set up python venv
|
||||||
|
include_tasks: python_venv.yaml
|
||||||
|
|
||||||
|
- name: build python exe
|
||||||
|
include_tasks: python_service.yaml
|
||||||
|
|
||||||
|
- name: set up nssm service
|
||||||
|
include_tasks: nssm.yaml
|
||||||
|
|
||||||
|
- name: set up scheduled task
|
||||||
|
include_tasks: update_task.yaml
|
||||||
|
|
||||||
|
...
|
||||||
49
roles/storage_api/tasks/nssm.yaml
Normal file
49
roles/storage_api/tasks/nssm.yaml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- 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
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install disk_api service
|
||||||
|
community.windows.win_nssm:
|
||||||
|
name: "{{ disk_service_name }}"
|
||||||
|
application: "{{ storage_api_root }}\\dist\\disk_service.exe"
|
||||||
|
|
||||||
|
- name: Set disk_api service startup auto and start
|
||||||
|
ansible.windows.win_service:
|
||||||
|
name: "{{ disk_service_name }}"
|
||||||
|
start_mode: auto
|
||||||
|
state: started
|
||||||
|
register: disk_service_status
|
||||||
|
|
||||||
|
- name: Show service result
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "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 }}"
|
||||||
|
|
||||||
|
...
|
||||||
47
roles/storage_api/tasks/python_service.yaml
Normal file
47
roles/storage_api/tasks/python_service.yaml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- 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: 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_template:
|
||||||
|
src: disk_service.py
|
||||||
|
dest: "{{ storage_api_root }}\\disk_service.py"
|
||||||
|
|
||||||
|
- name: install pyinstaller
|
||||||
|
win_shell: "{{ python_venv_bin }} -m pip install pyinstaller"
|
||||||
|
|
||||||
|
- name: compile binary
|
||||||
|
become: no
|
||||||
|
win_shell: "{{ python_venv }}\\Scripts\\pyinstaller.exe -F {{ storage_api_root }}\\disk_service.py"
|
||||||
|
args:
|
||||||
|
chdir: "{{ storage_api_root }}"
|
||||||
|
|
||||||
|
- name: Open up port 5000
|
||||||
|
community.windows.win_firewall_rule:
|
||||||
|
name: _ansible_python_disk_service
|
||||||
|
description: "Firewall rule to allow traffic for Disk info API"
|
||||||
|
localport: 5000
|
||||||
|
action: allow
|
||||||
|
direction: in
|
||||||
|
protocol: tcp
|
||||||
|
state: present
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
...
|
||||||
31
roles/storage_api/tasks/python_venv.yaml
Normal file
31
roles/storage_api/tasks/python_venv.yaml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Ensure Python is installed
|
||||||
|
win_chocolatey:
|
||||||
|
name: python
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create venv folder
|
||||||
|
ansible.windows.win_file:
|
||||||
|
path: "{{ python_venv }}"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Copy requirements.txt
|
||||||
|
ansible.windows.win_copy:
|
||||||
|
src: requirements.txt
|
||||||
|
dest: "{{ python_venv }}\\requirements.txt"
|
||||||
|
|
||||||
|
- name: Create virtual environment
|
||||||
|
win_shell: "python -m venv {{ python_venv }}"
|
||||||
|
|
||||||
|
- name: Upgrade pip in the virtual environment
|
||||||
|
win_shell: "{{ python_venv_bin }} -m pip install --upgrade pip"
|
||||||
|
args:
|
||||||
|
chdir: "{{ python_venv }}"
|
||||||
|
|
||||||
|
- name: Install Python dependencies from requirements.txt
|
||||||
|
win_shell: "{{ python_venv_bin }} -m 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
|
||||||
|
|
||||||
|
...
|
||||||
117
roles/storage_api/templates/disk_service.py
Normal file
117
roles/storage_api/templates/disk_service.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
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 }})
|
||||||
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
ip_check_folder: "/opt/cosmos/ip_check"
|
|
||||||
|
|
||||||
|
|
||||||
...
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
- name: Subnet Security Check
|
|
||||||
#when: 'SERVER_SUBNET_GROUP not in subnet_group_check'
|
|
||||||
include_tasks: user_check.yaml
|
|
||||||
|
|
||||||
...
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
---
|
|
||||||
- name: user check
|
|
||||||
delegate_to: localhost
|
|
||||||
block:
|
|
||||||
|
|
||||||
- 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 }}"
|
|
||||||
|
|
||||||
# Create venv Folder
|
|
||||||
- name: create ip venv folder
|
|
||||||
file:
|
|
||||||
path: "{{ ip_check_folder }}"
|
|
||||||
state: directory
|
|
||||||
#mode: '0755'
|
|
||||||
|
|
||||||
# Copy venv files
|
|
||||||
- name: copy ip venv files
|
|
||||||
copy:
|
|
||||||
src: subnet_check/
|
|
||||||
dest: "{{ ip_check_folder }}"
|
|
||||||
#mode: 0644
|
|
||||||
|
|
||||||
- name: extract venv
|
|
||||||
unarchive:
|
|
||||||
src: /var/jenkins_home/ansible-files/programs/ip_check_venv.tar.gz
|
|
||||||
dest: "{{ ip_check_folder }}"
|
|
||||||
#mode: 0644
|
|
||||||
|
|
||||||
## build venv
|
|
||||||
## commenting and using pre-made archived env to save time
|
|
||||||
#- name: build venv
|
|
||||||
# pip:
|
|
||||||
# virtualenv: "{{ ip_check_folder }}/venv"
|
|
||||||
# requirements: "{{ ip_check_folder }}/requirements.txt"
|
|
||||||
# virtualenv_command: python3 -m venv
|
|
||||||
# state: present
|
|
||||||
|
|
||||||
# check if IP is restricted
|
|
||||||
- name: check for restricted IP
|
|
||||||
shell: "{{ ip_check_folder }}/venv/bin/python {{ ip_check_folder }}/ip_check.py {{ ansible_ssh_host }}"
|
|
||||||
args:
|
|
||||||
chdir: "{{ ip_check_folder }}"
|
|
||||||
register: restricted_ip_check
|
|
||||||
|
|
||||||
- name: display output of this
|
|
||||||
debug:
|
|
||||||
msg:
|
|
||||||
- "{{ restricted_ip_check.cmd }}"
|
|
||||||
- "{{ restricted_ip_check.stdout_lines }}"
|
|
||||||
|
|
||||||
- name: end play if not admin
|
|
||||||
when: restricted_ip_check.stdout_lines[0] | bool
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: display warning
|
|
||||||
debug:
|
|
||||||
msg: "Warning: Your user account is not authorized to run playbooks on this subnet."
|
|
||||||
- meta: end_play
|
|
||||||
|
|
||||||
...
|
|
||||||
Reference in New Issue
Block a user