Skip to content

Commit 1d5ef6b

Browse files
committed
Add script to analyze hyperscaler account distribution per global account and provider
1 parent 9d2a8fe commit 1d5ef6b

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to analyze hyperscaler account distribution per global account and per provider.
4+
"""
5+
6+
import subprocess
7+
import json
8+
import sys
9+
from collections import defaultdict
10+
from typing import Dict, List
11+
import argparse
12+
13+
14+
class HyperscalerAccountAnalyzer:
15+
"""Analyzes hyperscaler account distribution per global account and per provider."""
16+
17+
PROVIDERS = ['aws', 'gcp', 'azure', 'alicloud', 'openstack', 'other']
18+
19+
def __init__(self):
20+
self.hyperscaler_account_count = defaultdict(int)
21+
self.global_account_data = defaultdict(lambda: defaultdict(list))
22+
23+
def fetch_runtime_data(self) -> List[Dict]:
24+
try:
25+
result = subprocess.run(
26+
['kcp', 'rt', '-ojson'],
27+
capture_output=True,
28+
text=True,
29+
check=True
30+
)
31+
data = json.loads(result.stdout)
32+
return data.get('data', [])
33+
except subprocess.CalledProcessError as e:
34+
print(f"Error executing kcp command: {e}", file=sys.stderr)
35+
sys.exit(1)
36+
except json.JSONDecodeError as e:
37+
print(f"Error parsing JSON: {e}", file=sys.stderr)
38+
sys.exit(1)
39+
except FileNotFoundError:
40+
print("Error: 'kcp' command not found. Please ensure it's installed and in your PATH.", file=sys.stderr)
41+
sys.exit(1)
42+
43+
def determine_provider(self, secret_name: str) -> str:
44+
"""Determine cloud provider from hyperscaler account secret name."""
45+
if not secret_name:
46+
return None
47+
48+
secret_lower = secret_name.lower()
49+
50+
if 'aws' in secret_lower:
51+
return 'aws'
52+
elif 'gcp' in secret_lower:
53+
return 'gcp'
54+
elif 'azure' in secret_lower or 'sap-skr' in secret_lower:
55+
return 'azure'
56+
elif 'ali' in secret_lower:
57+
return 'alicloud'
58+
elif 'openstack' in secret_lower:
59+
return 'openstack'
60+
else:
61+
return 'other'
62+
63+
def analyze(self, data: List[Dict]) -> None:
64+
"""Analyze runtime data and count unique hyperscaler accounts per global account per provider."""
65+
# Track unique hyperscaler accounts per global account to avoid double counting
66+
seen_hyperscaler_accounts = defaultdict(set)
67+
68+
for runtime in data:
69+
global_account_id = runtime.get('globalAccountID')
70+
secret_name = runtime.get('subscriptionSecretName')
71+
72+
# Skip if no secret name (hyperscaler account identifier)
73+
if not secret_name:
74+
continue
75+
76+
provider = self.determine_provider(secret_name)
77+
if not provider:
78+
continue
79+
80+
# Use "unknown" if no global account ID
81+
if not global_account_id:
82+
global_account_id = "unknown"
83+
84+
# Create unique key for this hyperscaler account in this global account
85+
account_key = f"{global_account_id}|{secret_name}"
86+
87+
# Skip if we've already counted this hyperscaler account for this global account
88+
if account_key in seen_hyperscaler_accounts[provider]:
89+
continue
90+
91+
# Mark this hyperscaler account as seen
92+
seen_hyperscaler_accounts[provider].add(account_key)
93+
94+
# Collect statistics
95+
self.hyperscaler_account_count[provider] += 1
96+
self.global_account_data[global_account_id][provider].append(secret_name)
97+
98+
def get_statistics(self) -> Dict:
99+
"""Calculate hyperscaler account statistics per provider."""
100+
total = sum(self.hyperscaler_account_count.values())
101+
102+
stats = {
103+
'total': total,
104+
'providers': dict(self.hyperscaler_account_count),
105+
'percentages': {},
106+
'global_account_count': len(self.global_account_data)
107+
}
108+
109+
if total > 0:
110+
for provider in self.PROVIDERS:
111+
count = self.hyperscaler_account_count.get(provider, 0)
112+
stats['percentages'][provider] = round((count / total) * 100, 2)
113+
114+
return stats
115+
116+
def get_global_account_breakdown(self) -> Dict:
117+
"""Get breakdown of hyperscaler accounts per global account per provider."""
118+
breakdown = {}
119+
120+
for ga_id, providers in self.global_account_data.items():
121+
breakdown[ga_id] = {
122+
'providers': {},
123+
'hyperscaler_accounts': {}
124+
}
125+
126+
for provider in self.PROVIDERS:
127+
accounts = providers.get(provider, [])
128+
breakdown[ga_id]['providers'][provider] = len(accounts)
129+
breakdown[ga_id]['hyperscaler_accounts'][provider] = accounts
130+
131+
return breakdown
132+
133+
def print_table_output(self) -> None:
134+
"""Print hyperscaler account analysis in table format."""
135+
stats = self.get_statistics()
136+
breakdown = self.get_global_account_breakdown()
137+
138+
print("\n" + "=" * 70)
139+
print("Hyperscaler Account Distribution Analysis")
140+
print("=" * 70)
141+
print("\n--- Overall Statistics ---\n")
142+
print(f"{'Provider':<15} {'Accounts':<10} {'Percentage':<12}")
143+
print("-" * 40)
144+
145+
for provider in self.PROVIDERS:
146+
count = stats['providers'].get(provider, 0)
147+
percentage = stats['percentages'].get(provider, 0)
148+
if count > 0:
149+
print(f"{provider.upper():<15} {count:<10} {percentage:<12.2f}%")
150+
151+
print("-" * 40)
152+
print(f"{'TOTAL':<15} {stats['total']:<10}")
153+
print(f"\nTotal Global Accounts: {stats['global_account_count']}")
154+
155+
print("\n--- Distribution by Global Account ---\n")
156+
print(f"{'Global Account ID':<38} | {'AWS':<4} | {'GCP':<4} | {'Azure':<5} | {'AliCloud':<8} | {'OpenStack':<10} | {'Other':<5} | {'Total':<5}")
157+
print("-" * 130)
158+
159+
for ga_id in sorted(breakdown.keys()):
160+
data = breakdown[ga_id]
161+
aws_count = data['providers'].get('aws', 0)
162+
gcp_count = data['providers'].get('gcp', 0)
163+
azure_count = data['providers'].get('azure', 0)
164+
alicloud_count = data['providers'].get('alicloud', 0)
165+
openstack_count = data['providers'].get('openstack', 0)
166+
other_count = data['providers'].get('other', 0)
167+
total_count = sum(data['providers'].values())
168+
169+
print(f"{ga_id:<38} | {aws_count:<4} | {gcp_count:<4} | {azure_count:<5} | {alicloud_count:<8} | {openstack_count:<10} | {other_count:<5} | {total_count:<5}")
170+
171+
# Detect global accounts with more than 1 hyperscaler account per provider
172+
multi_account_gas = []
173+
for ga_id in sorted(breakdown.keys()):
174+
data = breakdown[ga_id]
175+
aws_count = data['providers'].get('aws', 0)
176+
gcp_count = data['providers'].get('gcp', 0)
177+
azure_count = data['providers'].get('azure', 0)
178+
alicloud_count = data['providers'].get('alicloud', 0)
179+
openstack_count = data['providers'].get('openstack', 0)
180+
181+
if aws_count > 1 or gcp_count > 1 or azure_count > 1 or alicloud_count > 1 or openstack_count > 1:
182+
multi_account_gas.append({
183+
'ga_id': ga_id,
184+
'aws': aws_count,
185+
'gcp': gcp_count,
186+
'azure': azure_count,
187+
'alicloud': alicloud_count,
188+
'openstack': openstack_count
189+
})
190+
191+
if multi_account_gas:
192+
print("\n--- Global Accounts with Multiple Hyperscaler Accounts per Provider ---\n")
193+
print(f"{'Global Account ID':<38} | {'AWS':<4} | {'GCP':<4} | {'Azure':<5} | {'AliCloud':<8} | {'OpenStack':<10}")
194+
print("-" * 110)
195+
for account in multi_account_gas:
196+
print(f"{account['ga_id']:<38} | {account['aws']:<4} | {account['gcp']:<4} | {account['azure']:<5} | {account['alicloud']:<8} | {account['openstack']:<10}")
197+
print(f"\nTotal Global Accounts using multiple hyperscaler accounts per provider: {len(multi_account_gas)}")
198+
199+
print("\n" + "=" * 70)
200+
201+
def print_json_output(self) -> None:
202+
stats = self.get_statistics()
203+
breakdown = self.get_global_account_breakdown()
204+
205+
output = {
206+
'statistics': stats,
207+
'global_accounts': breakdown
208+
}
209+
210+
print(json.dumps(output, indent=2))
211+
212+
213+
def main():
214+
parser = argparse.ArgumentParser(
215+
description='Analyze hyperscaler account distribution per global account and per provider'
216+
)
217+
parser.add_argument(
218+
'--output', '-o',
219+
choices=['table', 'json'],
220+
default='table',
221+
help='Output format (default: table)'
222+
)
223+
parser.add_argument(
224+
'--verbose', '-v',
225+
action='store_true',
226+
help='Show detailed hyperscaler account names'
227+
)
228+
parser.add_argument(
229+
'--show-other',
230+
action='store_true',
231+
help='Show detailed breakdown of "other" category hyperscaler accounts'
232+
)
233+
234+
args = parser.parse_args()
235+
236+
analyzer = HyperscalerAccountAnalyzer()
237+
238+
print("Fetching runtime data...", file=sys.stderr)
239+
data = analyzer.fetch_runtime_data()
240+
241+
print("Analyzing data...", file=sys.stderr)
242+
analyzer.analyze(data)
243+
244+
if args.output == 'json':
245+
analyzer.print_json_output()
246+
else:
247+
analyzer.print_table_output()
248+
249+
if args.show_other:
250+
print("\n--- Detailed 'Other' Category Breakdown ---\n")
251+
breakdown = analyzer.get_global_account_breakdown()
252+
other_accounts = []
253+
for ga_id, data in breakdown.items():
254+
for account in data['hyperscaler_accounts'].get('other', []):
255+
other_accounts.append((ga_id, account))
256+
257+
if other_accounts:
258+
print(f"Total 'Other' hyperscaler accounts: {len(other_accounts)}\n")
259+
print(f"{'Global Account ID':<38} | Hyperscaler Account Name")
260+
print("-" * 120)
261+
for ga_id, account in sorted(other_accounts, key=lambda x: x[1]):
262+
print(f"{ga_id:<38} | {account}")
263+
else:
264+
print("No hyperscaler accounts in 'Other' category")
265+
266+
if args.verbose:
267+
print("\n--- Detailed Hyperscaler Account Names by Global Account ---\n")
268+
breakdown = analyzer.get_global_account_breakdown()
269+
for ga_id in sorted(breakdown.keys()):
270+
print(f"\nGlobal Account: {ga_id}")
271+
data = breakdown[ga_id]
272+
for provider in analyzer.PROVIDERS:
273+
accounts = data['hyperscaler_accounts'].get(provider, [])
274+
if accounts:
275+
print(f" {provider.upper()}:")
276+
for account in accounts:
277+
print(f" - {account}")
278+
279+
280+
if __name__ == '__main__':
281+
main()

0 commit comments

Comments
 (0)