Apple Script : Monitoring and Recording on macOS caching server

The first thing you need for this to work is a macOS device which set up as a caching server, the purpose of this service is to save bandwidth when in corporate dense environment with lots of macOS devices trying to download system updates and applications.

MacOS Caching Theory

The theory is very simple, if the macOS device is placed on your corporate networkthen the high level overview of the process is, when iOS, upgrades and application updates are downloaded the macOS device will natively look for this caching server (when is he correctly registered) 

When a valid registered server is found for the first device to content downloads to that device and to the caching server, when this download has successfully been cached and the second device tries to download this update it comes from the local caching server and not from the Internet.

One v Many Devices

Generally, you will find the one device downloading 50MB of application updates is not a problem however, if you multiply that by over 2000 devices doing the same thing - you end up with horrible network saturation, depending on your Internet capacity and availability.

Amazing in "dense" environments

This is where the macOS device comes in and over six month period in corporate environments, especially large HQ offices I have seen this device save while over 600GB from coming through the Internet - as macOS device has been serving them the cached copy.

Simple Setup but you need monitoring

Obviously, these devices are pretty much set and forget devices, But what if they stop working or corrupt their configuration?

This is where I have created an bash script that will monitor the statistics, availability,  discoverability and efficiency of these servers that can save your network from saturation.

The script is below, but remember before you can run it first you need to make it executable:

chmod +x generate_cache_report.sh

Then you need to execute with the correct command, which is:

sudo ./generate_cache_report.sh

When run this will give you a report that resembles this:

Apple Script :generate_cache_report.sh

#!/bin/bash
# Get timestamp for report
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
REPORT_FILE="cache_health_report.html"

# Function to get locator info
get_locator_info() {
    AssetCacheLocatorUtil locator 2>&1
}
# Function to check reachability
get_reachability() {
    AssetCacheLocatorUtil reachability 2>&1
}

# Get status information
STATUS_OUTPUT=$(AssetCacheManagerUtil status)
LOCATOR_OUTPUT=$(get_locator_info)
REACHABILITY_OUTPUT=$(get_reachability)
# Function to extract value from status output
get_value() {
    echo "$STATUS_OUTPUT" | grep "$1:" | cut -d: -f2- | xargs
}
# Function to determine status class
get_status_class() {
    if [ "$1" = "true" ] || [ "$1" = "OK" ] || [ "$1" = "1" ]; then
        echo "healthy"
    else
        echo "unhealthy"
    fi
}

# Get metrics before HTML generation
CACHE_STATUS=$(get_value "CacheStatus")
STARTUP_STATUS=$(get_value "StartupStatus")
IS_ACTIVATED=$(get_value "Activated")
REGISTRATION_STATUS=$(get_value "RegistrationStatus")
CACHE_USED=$(get_value "CacheUsed")
CACHE_LIMIT=$(get_value "CacheLimit")
CACHE_USED_NUM=$(echo "$CACHE_USED" | cut -d' ' -f1)
CACHE_LIMIT_NUM=$(echo "$CACHE_LIMIT" | cut -d' ' -f1)
DISK_USAGE=$(echo "scale=1; ($CACHE_USED_NUM / $CACHE_LIMIT_NUM) * 100" | bc)
BYTES_TO_CLIENTS=$(get_value "TotalBytesReturnedToClients")
BYTES_FROM_ORIGIN=$(get_value "TotalBytesStoredFromOrigin")
BYTES_DROPPED=$(get_value "TotalBytesDropped")
PUBLIC_ADDRESS=$(get_value "PublicAddress")
PORT=$(get_value "Port")
SERVER_GUID=$(get_value "ServerGUID")
RESTRICTED_MEDIA=$(get_value "RestrictedMedia")

# Create HTML report
cat > "$REPORT_FILE" << EOF
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cache Server Health Status</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
            line-height: 1.6;
            max-width: 1200px;
            margin: 40px auto;
            padding: 20px;
            background: #f5f5f7;
        }
        .card {
            background: white;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .header {
            color: #1d1d1f;
            border-bottom: 1px solid #d2d2d7;
            padding-bottom: 10px;
            margin-bottom: 20px;
        }
        .status-item {
            display: flex;
            align-items: center;
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 10px;
            background: #f8f8f8;
        }
        .status-indicator {
            display: inline-flex;
            align-items: center;
            padding: 5px 15px;
            border-radius: 5px;
            font-weight: 500;
            margin-left: auto;
        }
        .status-icon {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            margin-right: 6px;
        }
        .healthy { 
            background: #e4f8e8; 
            color: #0a7567;
        }
        .unhealthy { 
            background: #fde8e8; 
            color: #c81e1e;
        }
        .warning { 
            background: #fff3e0; 
            color: #ad5700;
        }
        .metric-value {
            font-weight: 500;
            margin-right: 10px;
            color: #666;
        }
        .section-title {
            margin: 0 0 15px 0;
            color: #1d1d1f;
            font-size: 1.2em;
        }
        .grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
            gap: 15px;
        }
        .subsection {
            margin-top: 20px;
        }
        .performance-metric {
            font-size: 0.9em;
            color: #666;
        }
        .performance-value {
            font-size: 1.1em;
            font-weight: 500;
            color: #333;
        }
        .small-metric {
            font-size: 0.85em;
        }
    </style>
</head>
<body>
    <div class="card">
        <div class="header">
            <h1>Cache Server Health Status</h1>
            <p>Generated: $TIMESTAMP</p>
        </div>
    </div>
    <div class="card">
        <h2 class="section-title">Core Health Status</h2>
        <div class="status-item">
            <strong>Overall Cache Status</strong>
            <span class="status-indicator $(get_status_class "$CACHE_STATUS")">
                <span class="status-icon" style="background: $([ "$CACHE_STATUS" = "OK" ] && echo "#0a7567" || echo "#c81e1e")"></span>
                $([ "$CACHE_STATUS" = "OK" ] && echo "HEALTHY" || echo "UNHEALTHY")
            </span>
        </div>       
        <div class="status-item">
            <strong>Service Activation</strong>
            <span class="status-indicator $(get_status_class "$IS_ACTIVATED")">
                <span class="status-icon" style="background: $([ "$IS_ACTIVATED" = "true" ] && echo "#0a7567" || echo "#c81e1e")"></span>
                $([ "$IS_ACTIVATED" = "true" ] && echo "ACTIVE" || echo "INACTIVE")
            </span>
        </div>
        <div class="status-item">
            <strong>Registration Status</strong>
            <span class="status-indicator $(get_status_class "$REGISTRATION_STATUS")">
                <span class="status-icon" style="background: $([ "$REGISTRATION_STATUS" = "1" ] && echo "#0a7567" || echo "#c81e1e")"></span>
                $([ "$REGISTRATION_STATUS" = "1" ] && echo "REGISTERED" || echo "UNREGISTERED")
            </span>
        </div>
    </div>
    <div class="card">
        <h2 class="section-title">Resource Health</h2>
        <div class="status-item">
            <strong>Storage Usage</strong>
            <span class="metric-value">$CACHE_USED of $CACHE_LIMIT ($DISK_USAGE%)</span>
            <span class="status-indicator $([ "$DISK_USAGE" -lt 80 ] && echo "healthy" || ([ "$DISK_USAGE" -lt 90 ] && echo "warning" || echo "unhealthy"))">
                <span class="status-icon" style="background: $([ "$DISK_USAGE" -lt 80 ] && echo "#0a7567" || ([ "$DISK_USAGE" -lt 90 ] && echo "#ad5700" || echo "#c81e1e"))"></span>
                $([ "$DISK_USAGE" -lt 80 ] && echo "HEALTHY" || ([ "$DISK_USAGE" -lt 90 ] && echo "WARNING" || echo "CRITICAL"))
            </span>
        </div>
    </div>
    <div class="card">
        <h2 class="section-title">Network Health</h2>
        <div class="grid">
            <div class="status-item">
                <strong>Server Port</strong>
                <span class="metric-value">$PORT</span>
                <span class="status-indicator healthy">
                    <span class="status-icon" style="background: #0a7567"></span>
                    LISTENING
                </span>
            </div>
            <div class="status-item">
                <strong>Public Address</strong>
                <span class="metric-value">$PUBLIC_ADDRESS</span>
                <span class="status-indicator healthy">
                    <span class="status-icon" style="background: #0a7567"></span>
                    CONFIGURED
                </span>
            </div>
        </div>
        <div class="subsection">
            <h3 class="section-title">Traffic Metrics</h3>
            <div class="grid">
                <div class="status-item">
                    <div>
                        <div class="performance-metric">Data Served to Clients</div>
                        <div class="performance-value">$BYTES_TO_CLIENTS</div>
                    </div>
                </div>
                <div class="status-item">
                    <div>
                        <div class="performance-metric">Data Retrieved from Origin</div>
                        <div class="performance-value">$BYTES_FROM_ORIGIN</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="card">
        <h2 class="section-title">Server Configuration</h2>
        <div class="status-item small-metric">
            <strong>Server GUID</strong>
            <span class="metric-value">$SERVER_GUID</span>
        </div>
        <div class="status-item">
            <strong>Cache Reachability</strong>
            <span class="status-indicator healthy">
                <span class="status-icon" style="background: #0a7567"></span>
                REACHABLE
            </span>
        </div>
    </div>
</body>
</html>
EOF
echo "Report generated: $REPORT_FILE"

Automate the execution of the Script

You obviously need this to be run automatically then you need to follow a few steps these are outlined below, if you do not complete this you will need to manually run this script.

Move the script to a permanent location

sudo mkdir -p /usr/local/scripts
sudo cp cache_report_email.sh /usr/local/scripts/
sudo chmod +x /usr/local/scripts/cache_report_email.sh

Open root's crontab (as sudo is required)

sudo crontab -e

Add crontab tasks

Run at 8am Monday-Friday
0 8 * * 1-5 /usr/local/scripts/cache_report_email.sh

 Run at 6pm Monday-Friday
0 18 * * 1-5 /usr/local/scripts/cache_report_email.sh

Previous Post Next Post

نموذج الاتصال