Managing storage effectively is a crucial aspect of maintaining robust and efficient IT infrastructure. For IT professionals and Managed Service Providers (MSPs), having tools that provide detailed insights into storage status can greatly enhance their operational efficiency.
One such tool is the bash script designed to retrieve and display Proxmox node storage details. This blog post will explore the script’s functionality, its importance, and its application in real-world scenarios.
Background
In a virtualized environment like Proxmox, understanding the status of node storage is vital for performance optimization, troubleshooting, and capacity planning. Proxmox VE (Virtual Environment) is an open-source server virtualization management solution that provides a powerful platform for managing VMs, containers, storage, and networking.
The provided script leverages Proxmox’s API and NinjaRMM’s CLI tool to gather and present detailed storage information, which can then be saved to custom fields for further analysis or documentation. This script is particularly useful for MSPs who need to monitor multiple client environments and ensure optimal performance and availability.
The Script:
#!/usr/bin/env bash
# Description: Get the details of a Proxmox Node Storage and save it to a multiline and/or WYSIWYG custom field
#
# Release Notes: Fixed 10% width bug.
# By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
# Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
# Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
# Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider.
# Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
# Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
# Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
# EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
# Command line arguments, swap the numbers if you want the multiline custom field to be the second argument
multiline_custom_field=$1 # First argument is the multiline custom field name
wysiwyg_custom_field=$2 # Second argument is the WYSIWYG custom field name
# Check if the multilineCustomField and wysiwygCustomField are set
if [[ -n "${multilineCustomField}" && "${multilineCustomField}" != "null" ]]; then
multiline_custom_field=$multilineCustomField
fi
if [[ -n "${wysiwygCustomField}" && "${wysiwygCustomField}" != "null" ]]; then
wysiwyg_custom_field=$wysiwygCustomField
fi
# Check if the multiline_custom_field and wysiwyg_custom_field are the same
if [[ -n "${multiline_custom_field}" && "${multiline_custom_field}" == "${wysiwyg_custom_field}" ]]; then
echo "[Error] multilineCustomField and wysiwygCustomField cannot be the same custom field."
exit 1
fi
# Check if the multiline_custom_field and wysiwyg_custom_field are set
if [[ -z "${multiline_custom_field}" ]]; then
echo "[Info] multilineCustomField is not set."
fi
if [[ -z "${wysiwyg_custom_field}" ]]; then
echo "[Info] wysiwygCustomField is not set."
fi
# Check that we have the required tools
if ! command -v pvesh &>/dev/null; then
echo "[Error] The Proxmox VE API tool 'pvesh' is required."
exit 1
fi
# Check that we are running as root
if [[ $EUID -ne 0 ]]; then
echo "[Error] This script must be run as root."
exit 1
fi
# Check if ninjarmm-cli command exists
ninjarmm_cli="/opt/NinjaRMMAgent/programdata/ninjarmm-cli"
if [[ -z $ninjarmm_cli ]]; then
echo "[Error] The ninjarmm-cli command does not exist in the default path. Please ensure the NinjaRMM agent is installed before running this script."
exit 1
else
# ninjarmm-cli command exists in the default path
echo -n
fi
function GetThisNodeName() {
# Get the node name
if ! node_name=$(pvesh get /cluster/status --noborder | awk '$6 == 1 {print $2}'); then
echo "[Error] Failed to get the node name."
echo "$node_name"
exit 1
fi
echo "$node_name"
}
# Run the pvesh command to get the status information
if ! storages=$(pvesh get /storage --noborder | tail +2); then
echo "[Error] Failed to get the list of storages."
echo "$storages"
exit 1
fi
# Example Output:
# local
# local-zfs
# storage-nas
function formatStorage() {
echo ""
echo "Storage Status:"
# Loop though the storages and get the status of each
for storage in $storages; do
# Get the status of the storage
if ! storage_status=$(pvesh get /storage/"$storage" --noborder); then
echo "[Error] Failed to get the Storage Status of $storage."
echo "$storage_status"
exit 1
fi
storage_node=$(GetThisNodeName)
# Get the storage name
storage_name=$(echo "$storage_status" | grep -P 'storage\s+' | awk '{print $2}')
# Get the free space
# "$storage_name " is used to avoid matching "local-zfs" when searching for "local"
storage_free_space=$(pvesh get "/nodes/$storage_node/storage" --noborder | grep -P "$storage_name " | awk '{print $5" "$6}')
# Get the total space
storage_total_space=$(pvesh get "/nodes/$storage_node/storage" --noborder | grep -P "$storage_name " | awk '{print $9" "$10}')
echo -n
echo ""
echo "$storage"
echo "-------------"
# Take the output of $storage_status, skip the first line, then use a colon as a separator between the key and value
echo "$storage_status" | tail +2 | awk '{print $1 ": " $2}'
echo "Free: $storage_free_space"
echo "Total: $storage_total_space"
done
}
multiline_output=$(formatStorage)
# Create Storage Status label
storage_table="<h2>Storage Status</h2>"
# Create the Storage Status table
storage_table+="<table style='white-space:nowrap;'><tr><th>Storage Name</th><th>Type</th><th>Path/File System</th><th>Free Space</th><th>Total Space</th><th>Content</th></tr>"
# Loop though the storages and get the status of each
for storage in $storages; do
if ! storage_status=$(pvesh get /storage/"$storage" --noborder); then
echo "[Error] Failed to get the Storage Status of $storage."
echo "$storage_status"
exit 1
fi
# Example Output:
# key value
# content images,rootdir
# digest c14cb4c9bbcf9a062fa8a82b10afe01cb1ed5b8d
# pool rpool/data
# sparse 1
# storage local-zfs
# type zfspool
storage_node=$(GetThisNodeName)
# Get the storage name
storage_name=$(echo "$storage_status" | grep -P 'storage\s+' | awk '{print $2}')
# Get the storage type
storage_type=$(echo "$storage_status" | grep -P 'type\s+' | awk '{print $2}')
# Get the storage pool/path
storage_pool=$(echo "$storage_status" | grep -P 'pool\s+' | awk '{print $2}')
if [[ -z "${storage_pool}" ]]; then
storage_pool=$(echo "$storage_status" | grep -P 'path\s+' | awk '{print $2}')
fi
# Get the storage content
storage_content=$(echo "$storage_status" | grep -P 'content\s+' | awk '{print $2}')
# Get the free space
# "$storage_name " is used to avoid matching "local-zfs" when searching for "local"
storage_free_space=$(pvesh get "/nodes/$storage_node/storage" --noborder | grep -P "$storage_name " | awk '{print $5" "$6}')
# Get the total space
storage_total_space=$(pvesh get "/nodes/$storage_node/storage" --noborder | grep -P "$storage_name " | awk '{print $9" "$10}')
# Add to the Storage Status table
storage_table+="<tr><td>$storage_name</td><td>$storage_type</td><td>$storage_pool</td><td>$storage_free_space</td><td>$storage_total_space</td><td>$storage_content</td></tr>"
done
# Close the Storage Status table
storage_table+="</table>"
# Save the results
result_table="$storage_table"
_exit_code=0
# Save the result to the custom field
if [[ -n "$wysiwyg_custom_field" ]]; then
if [[ -x "$ninjarmm_cli" ]]; then
if hideOutput=$(echo "$result_table" | "$ninjarmm_cli" set --stdin "$wysiwyg_custom_field" 2>&1); then
echo "[Info] Successfully set custom field: $wysiwyg_custom_field"
else
echo "[Error] Failed to set custom field: $wysiwyg_custom_field. Custom Field does not exit or does not have write permissions."
_exit_code=1
fi
else
echo "[Error] NinjaRMM CLI not found or not executable"
_exit_code=1
fi
fi
if [[ -n "$multiline_custom_field" ]]; then
if [[ -x "$ninjarmm_cli" ]]; then
if hideOutput=$(echo "$multiline_output" | "$ninjarmm_cli" set --stdin "$multiline_custom_field" 2>&1); then
echo "[Info] Successfully set custom field: $multiline_custom_field"
else
echo "[Error] Failed to set custom field: $multiline_custom_field. Custom Field does not exit or does not have write permissions."
_exit_code=1
fi
else
echo "[Error] NinjaRMM CLI not found or not executable"
_exit_code=1
fi
fi
# Output the result if no custom fields are set
if [[ -z "${wysiwyg_custom_field}" ]] && [[ -z "${multiline_custom_field}" ]]; then
# Output the result to the Activity Feed
echo "${multiline_output}"
fi
if [[ $_exit_code -eq 1 ]]; then
exit 1
fi
Access over 300+ scripts in the NinjaOne Dojo
Detailed Breakdown
Let’s delve into the script to understand its workings step by step.
- Initialization and Argument Parsing The script begins by parsing command-line arguments to determine the names of the custom fields where the storage details will be stored. It accepts two arguments: one for a multiline custom field and another for a WYSIWYG custom field. These fields are used to store formatted storage information for easier reading and further analysis.
- Custom Field Validation The script checks if the custom fields are set through environment variables or command-line arguments. It ensures that both fields are not the same to prevent conflicts.
- Tool Availability and Permissions Check The script verifies the availability of the pvesh command, which is used to interact with the Proxmox API, and ensures it is run with root privileges.
- NinjaRMM CLI Validation The script checks for the existence of the NinjaRMM CLI tool, which is necessary for updating custom fields.
- Node Name Retrieval The script defines a function to retrieve the Proxmox node name, which is required to query specific storage details.
- Storage Information Retrieval and Formatting The script fetches the storage list and iterates over each storage item to gather detailed status information, including free and total space. This information is formatted for both multiline and WYSIWYG custom fields.
- HTML Table Creation The script constructs an HTML table for the WYSIWYG custom field, summarizing the storage status in a tabular format.
- Saving Results Finally, the script saves the formatted storage information to the specified custom fields using the NinjaRMM CLI tool.
Potential Use Cases
Imagine an MSP managing multiple client environments with Proxmox. They need to regularly check the storage status to ensure there’s enough free space for backups and VM operations. Using this script, the MSP can automate the retrieval of storage information and save it to custom fields for each client, enabling quick access to critical data without manual intervention. This automation reduces the risk of human error and ensures timely updates on storage health.
Comparisons
While this script offers a tailored solution for Proxmox environments integrated with NinjaRMM, other methods to retrieve storage information include using native Proxmox tools like the web interface or custom scripts without NinjaRMM integration.
However, these methods may lack the automation and centralized management features provided by the combination of this script and NinjaRMM, making them less efficient for MSPs managing multiple environments.
Implications
Automating the retrieval and documentation of storage status enhances operational efficiency and reduces the risk of storage-related issues going unnoticed. This proactive approach to storage management can prevent downtime and ensure that IT infrastructure remains healthy and performant.
Recommendations
- Ensure that the Proxmox API tool (pvesh) and NinjaRMM CLI tool are correctly installed and configured before running the script.
- Regularly review and update custom fields to keep storage information current and accurate.
- Test the script in a non-production environment to ensure it meets your needs and integrates seamlessly with your existing processes.
Final Thoughts
Leveraging automation scripts like this one can significantly enhance the efficiency of IT operations, particularly for MSPs managing multiple client environments. NinjaOne, with its powerful RMM capabilities, provides an excellent platform for integrating such scripts, offering a centralized and streamlined approach to IT management. By utilizing these tools, IT professionals can ensure their infrastructure is always running optimally and be better prepared to handle any storage-related challenges that arise.