Skip to content

Instantly share code, notes, and snippets.

@rooftopcellist
Created November 22, 2023 22:18
Show Gist options
  • Save rooftopcellist/9a5ab0a4f10c22e7b5472318ddfad5b0 to your computer and use it in GitHub Desktop.
Save rooftopcellist/9a5ab0a4f10c22e7b5472318ddfad5b0 to your computer and use it in GitHub Desktop.
Create a report of resource usage in a given namespace
'''
Purpose:
* Generate a report of all pod cpu and memory usage in a namespace
Usage:
$ python3 resource-utilization-report.py namespace
'''
import subprocess
import argparse
import json
from tabulate import tabulate
def convert_memory_to_megabytes(memory_str):
"""
Convert memory value to Megabytes.
If the value is in Gi, convert it to Mi; otherwise, return the value as is.
"""
if memory_str.endswith('Gi'):
return str(int(float(memory_str.replace('Gi', '')) * 1024)) + 'Mi'
elif memory_str.endswith('Mi'):
return memory_str
else:
return 'N/A' # Handle cases where unit is not recognized
def get_resource_usage(namespace):
try:
# Initialize variables to store total metrics for CPU and Memory
total_cpu_usage = 0.0
total_cpu_requests = 0.0
total_cpu_limits = 0.0
total_memory_usage = 0.0
total_memory_requests = 0
total_memory_limits = 0
# Get CPU and memory usage for all pods in the namespace
top_output = subprocess.check_output(['kubectl', 'top', 'pod', '-n', namespace, '--containers', '--no-headers']).decode('utf-8')
# Get resource requests and limits for all pods in the namespace
describe_output = subprocess.check_output(['kubectl', 'get', 'pod', '-n', namespace, '-o', 'json']).decode('utf-8')
# Parse the describe output as JSON
pod_info = json.loads(describe_output)
pod_count = len(pod_info['items'])
# Initialize dictionaries to store resource data
resource_data = {}
# Split and process the top output
top_lines = top_output.strip().split('\n')
for line in top_lines:
parts = line.split()
pod_name = parts[0]
container_name = parts[1]
cpu_usage = parts[2]
memory_usage = parts[3]
# Add CPU usage to total and convert memory usage to Mi if necessary
total_cpu_usage += float(cpu_usage.replace('m', ''))
total_memory_usage += int(convert_memory_to_megabytes(memory_usage).replace('Mi', ''))
# Skip the "POD" container
if container_name == "POD":
continue
# Store resource data
if pod_name not in resource_data:
resource_data[pod_name] = {
'Pod Name': pod_name,
'Containers': []
}
resource_data[pod_name]['Containers'].append({
'Container Name': container_name,
'CPU Usage': cpu_usage,
'Memory Usage': memory_usage
})
# Iterate over pods to gather requests and limits
for pod in pod_info['items']:
pod_name = pod['metadata']['name']
# Initialize dictionaries to store requests and limits for containers
requests = {}
limits = {}
for container in pod['spec']['containers']:
container_name = container['name']
# Get container-level resource requests and limits
container_resources_data = container.get('resources', {})
cpu_requests = container_resources_data.get('requests', {}).get('cpu', 'N/A')
memory_requests = convert_memory_to_megabytes(container_resources_data.get('requests', {}).get('memory', 'N/A'))
cpu_limits = container_resources_data.get('limits', {}).get('cpu', 'N/A')
memory_limits = convert_memory_to_megabytes(container_resources_data.get('limits', {}).get('memory', 'N/A'))
# Add CPU and Memory requests and limits to total
if cpu_requests != 'N/A':
total_cpu_requests += float(cpu_requests.replace('m', ''))
if cpu_limits != 'N/A':
total_cpu_limits += float(cpu_limits.replace('m', ''))
if memory_requests != 'N/A':
total_memory_requests += int(memory_requests.replace('Mi', ''))
if memory_limits != 'N/A':
total_memory_limits += int(memory_limits.replace('Mi', ''))
requests[container_name] = f"CPU: {cpu_requests}, Memory: {memory_requests}"
limits[container_name] = f"CPU: {cpu_limits}, Memory: {memory_limits}"
container_resources = {}
container_resources[pod_name] = {
'Requests': requests,
'Limits': limits
}
# Return the total metrics along with the existing data
return resource_data, container_resources, total_cpu_usage, total_cpu_requests, total_cpu_limits, total_memory_usage, total_memory_requests, total_memory_limits, pod_count
except subprocess.CalledProcessError as e:
return str(e), None, None, None, None, None, None, None, None
def main():
parser = argparse.ArgumentParser(description='Generate a performance report for a Kubernetes namespace.')
parser.add_argument('namespace', metavar='NAMESPACE', type=str, help='The Kubernetes namespace to monitor')
args = parser.parse_args()
namespace = args.namespace
resource_data, container_resources, total_cpu_usage, total_cpu_requests, total_cpu_limits, total_memory_usage, total_memory_requests, total_memory_limits, pod_count = get_resource_usage(namespace)
if isinstance(resource_data, dict):
data = []
headers = ['Pod Name', 'Container Name', 'CPU Usage', 'Memory Usage', 'Requests', 'Limits']
for pod_name, pod_info in resource_data.items():
for container_info in pod_info['Containers']:
container_name = container_info['Container Name']
cpu_usage = container_info['CPU Usage']
memory_usage = container_info['Memory Usage']
requests = container_resources.get(pod_name, {}).get('Requests', {}).get(container_name, 'N/A')
limits = container_resources.get(pod_name, {}).get('Limits', {}).get(container_name, 'N/A')
data.append([pod_name, container_name, cpu_usage, memory_usage, requests, limits])
print(tabulate(data, headers=headers, tablefmt='grid'))
# Print the totals
print(f"\nTotal CPU Usage for Namespace: {total_cpu_usage}m")
print(f"Total CPU Requests for Namespace: {total_cpu_requests}m")
print(f"Total CPU Limits for Namespace: {total_cpu_limits}m")
print(f"Total Memory Usage for Namespace: {total_memory_usage}Mi")
print(f"Total Memory Requests for Namespace: {total_memory_requests}Mi")
print(f"Total Memory Limits for Namespace: {total_memory_limits}Mi")
print(f"Total Pods for Namespace: {pod_count}")
else:
print(f"Error: {resource_data}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment