VAST User Quota Report Script: Installation & Execution Guide

Prev Next

Overview

This guide provides step-by-step instructions for executing the VAST user quota report script and installing the required dependencies. This script leverages the VAST Python library (vastpy), which enables interaction with the VAST API.

Prerequisites

Before running the script, ensure that:

  • Python 3.6+ is installed on your machine.

  • You have network access to the VAST Cluster API (https://IP)

  • API credentials (admin username and password) that have sufficient permissions to fetch quota data.

Installing Required Python Modules

The script requires the following dependencies:

  • vastpy (for VAST API interaction)

  • pandas (for data processing)

  • requests (for handling API requests)

Please run the following procedure

  1. Install from a requirements.txt File

  2. Create a file named requirements.txt with the following contents:

    vastpy
    pandas
    requests
  3. Install the dependencies:

    pip install -r requirements.txt

Create the Quota Report Script

  1. Save the Script as quota_report.py

  2. Modify the relevant information within the script ( The script can monitor several VAST clusters. To do that, please add your VMS IPs & Credentials) :

    VAST_CREDENTIALS = {
        "VMS1_IP": {"username": "USER1", "password": "PASSWORD1"},
        "VMS2_IP": {"username": "USER2", "password": "PASSWORD2"},
    }

quota_report.py

import logging
import pandas as pd
from datetime import datetime
from vastpy import VASTClient

# Dictionary mapping VAST Cluster IPs to unique credentials
VAST_CREDENTIALS = {
    "VMS1_IP": {"username": "USER1", "password": "PASSWORD1"},
    "VMS2_IP": {"username": "USER2", "password": "PASSWORD2"},
    
}
# Configure logging to both console and a log file 
log_filename = "quota_report.log"
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",  
    handlers=[
        logging.FileHandler(log_filename, mode='a'),  # Append to log file
        logging.StreamHandler()  # Also print to console
    ]
)
# Conversion factor from Bytes to GB
BYTES_TO_GB = 1073741824  # 1GB 
def bytes_to_gb(value):
    """Convert bytes to GB, return 'N/A' if None."""
    return round(value / BYTES_TO_GB, 2) if value is not None else "N/A"

def get_vast_client(address):
    """Authenticate with the VAST API for a given cluster IP using its specific credentials."""
    if address not in VAST_CREDENTIALS:
        logging.error(f"No credentials found for {address}. Skipping.")
        return None

    username = VAST_CREDENTIALS[address]["username"]
    password = VAST_CREDENTIALS[address]["password"]

    try:
        logging.info(f"Starting data retrieval for VAST Cluster: {address} (User: {username})")
        client = VASTClient(user=username, password=password, address=address)

        # Validate connection by retrieving basic settings
        settings = client.basicsettings.get()
        logging.info(f"Connected to VAST at {address}")

        return client
    except Exception as e:
        logging.error(f"Failed to connect to VAST API at {address}: {e}")
        return None

def get_quotas(address):
    """Retrieve quota information from a specific VAST Cluster IP."""
    client = get_vast_client(address)
    if not client:
        logging.error(f"Skipping {address} due to connection failure.")
        return None

    try:
        logging.info(f"Fetching quota data from VAST at {address}...")
        quotas = client.quotas.get()

        if not quotas or len(quotas) == 0:
            logging.warning(f"No quotas found at {address}.")
            return []
        
        logging.info(f"Quota data retrieved successfully from {address}. Number of records: {len(quotas)}")
        return quotas
    except Exception as e:
        logging.error(f"Error retrieving quota data from {address}: {e}")
        return None

def generate_report():
    """Fetch quota data from all specified VAST clusters, print reports, and append to CSVs."""
    
    for index, address in enumerate(VAST_CREDENTIALS.keys()):
        if index > 0:
            print(f"\nFinished processing {list(VAST_CREDENTIALS.keys())[index - 1]}")
            logging.info(f"Finished processing {list(VAST_CREDENTIALS.keys())[index - 1]}")
            print("\nMoving to the next cluster...\n")
            logging.info("Moving to the next cluster...\n")

        quotas = get_quotas(address)

        data = []
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        if quotas is None:
            logging.info(f"Skipping {address} due to retrieval failure.")
            quotas = []  # Ensure an empty dataset to generate an empty CSV

        for quota in quotas:
            default_user_quota = quota.get("default_user_quota") or {}  # Ensure it's a dict

            quota_system_id = default_user_quota.get("quota_system_id", "N/A")
            user_soft_limit = bytes_to_gb(default_user_quota.get("soft_limit"))
            user_hard_limit = bytes_to_gb(default_user_quota.get("hard_limit"))

            main_soft_limit = bytes_to_gb(quota.get("soft_limit"))
            main_hard_limit = bytes_to_gb(quota.get("hard_limit"))

            data.append({
                "Timestamp": current_time,  # Add timestamp for each entry
                "Quota ID": quota.get("id"),
                "Name": quota.get("name", "N/A"),
                "Path": quota.get("path", "N/A"),
                "State": quota.get("state", "N/A"),
                "Main Soft Limit (GB)": main_soft_limit,
                "Main Hard Limit (GB)": main_hard_limit,
                "User Soft Limit (GB)": user_soft_limit,
                "User Hard Limit (GB)": user_hard_limit,
                "Quota System ID": quota_system_id,
                "Used Capacity (GB)": bytes_to_gb(quota.get("used_capacity")),
                "Used Effective Capacity (GB)": bytes_to_gb(quota.get("used_effective_capacity")),
                "Sync State": quota.get("sync_state", "N/A"),
                "Cluster": quota.get("cluster", "N/A"),
                "Tenant ID": quota.get("tenant_id", "N/A"),
            })

        df = pd.DataFrame(data)

        # Print a separate table for each cluster
        print(f"\nQuota Report for VAST Cluster: {address}\n")
        if not df.empty:
            print(df.to_string(index=False))
        else:
            print(f"No quota data available for {address}. An empty CSV file will be maintained.")

        # Save the report as a CSV file, appending to it instead of overwriting
        csv_filename = f"quota_report_{address}.csv"
        df.to_csv(csv_filename, mode='a', header=not pd.io.common.file_exists(csv_filename), index=False)
        logging.info(f"Quota report appended to {csv_filename}")

    print("\nAll clusters processed successfully.\n")
    logging.info("All clusters processed successfully.")

if __name__ == "__main__":
    logging.info("Starting quota report generation for all configured VAST clusters...")
    generate_report()

Executing the Script

Execute the script using the following command :

python3 quota_report.py

Expected output:

The script will check the quota status, print it to the screen, and generate a CSV file.

##python3 quota_report.py
2025-02-09 11:57:07 - INFO - Starting quota report generation for all configured VAST clusters...
2025-02-09 11:57:07 - INFO - Starting data retrieval for VAST Cluster: 10.27.200.136 (User: admin)
2025-02-09 11:57:07 - INFO - Connected to VAST at 10.27.200.136
2025-02-09 11:57:07 - INFO - Fetching quota data from VAST at 10.27.200.136...
2025-02-09 11:57:08 - INFO - Quota data retrieved successfully from 10.27.200.136. Number of records: 1

Quota Report for VAST Cluster: 10.27.200.136

       Timestamp  Quota ID  Name   Path State  Main Soft Limit (GB) Main Hard Limit (GB)  User Soft Limit (GB)  User Hard Limit (GB)  Quota System ID  Used Capacity (GB)  Used Effective Capacity (GB)   Sync State Cluster  Tenant ID
2025-02-09 11:57         4 test1 /test1    OK                  0.93                  N/A                  0.01                  0.02                2                 0.0                           0.0 SYNCHRONIZED    v136          1
2025-02-09 11:57:08 - INFO - Quota report appended to quota_report_10.27.200.136.csv

Finished processing 10.27.200.136
2025-02-09 11:57:08 - INFO - Finished processing 10.27.200.136

Moving to the next cluster...

2025-02-09 11:57:08 - INFO - Moving to the next cluster...

2025-02-09 11:57:08 - INFO - Starting data retrieval for VAST Cluster: 10.27.200.6 (User: admin)
2025-02-09 11:57:08 - INFO - Connected to VAST at 10.27.200.6
2025-02-09 11:57:08 - INFO - Fetching quota data from VAST at 10.27.200.6...
2025-02-09 11:57:08 - INFO - Quota data retrieved successfully from 10.27.200.6. Number of records: 1

Quota Report for VAST Cluster: 10.27.200.6

       Timestamp  Quota ID        Name   Path State  Main Soft Limit (GB) Main Hard Limit (GB)  User Soft Limit (GB)  User Hard Limit (GB)  Quota System ID  Used Capacity (GB)  Used Effective Capacity (GB)   Sync State   Cluster  Tenant ID
2025-02-09 11:57         1 quota_demo1 /quota    OK                 93.13                  N/A                 93.13                186.26                1                 0.0                           0.0 SYNCHRONIZED vast6-kfs          1
2025-02-09 11:57:08 - INFO - Quota report appended to quota_report_10.27.200.6.csv

All clusters processed successfully.

2025-02-09 11:57:08 - INFO - All clusters processed successfully.

Further Reading