service-capacity-modeling 0.3.102__py3-none-any.whl → 0.3.104__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -113,6 +113,10 @@ def _estimate_java_app_region( # pylint: disable=too-many-positional-arguments
113
113
  cluster.cluster_type = "nflx-java-app"
114
114
  cluster.attached_drives = attached_drives
115
115
 
116
+ # Add drive cost (root volume is EBS and costs money)
117
+ drive_cost = sum(d.annual_cost for d in attached_drives) * cluster.count
118
+ cluster.annual_cost = cluster.annual_cost + drive_cost
119
+
116
120
  # Generally don't want giant clusters
117
121
  # Especially not above 1000 because some load balancers struggle
118
122
  # with such large clusters
@@ -19,6 +19,7 @@ from service_capacity_modeling.interface import (
19
19
  CapacityDesires,
20
20
  certain_float,
21
21
  certain_int,
22
+ ClusterCapacity,
22
23
  Consistency,
23
24
  DataShape,
24
25
  GlobalConsistency,
@@ -27,6 +28,27 @@ from service_capacity_modeling.interface import (
27
28
  )
28
29
 
29
30
 
31
+ def _format_cluster(cluster: ClusterCapacity, deployment: str) -> dict[str, Any]:
32
+ """Format a single cluster's details."""
33
+ info: dict[str, Any] = {
34
+ "cluster_type": cluster.cluster_type,
35
+ "deployment": deployment,
36
+ "instance": cluster.instance.name,
37
+ "count": cluster.count,
38
+ "annual_cost": float(cluster.annual_cost),
39
+ }
40
+
41
+ # Add attached drives if present
42
+ if cluster.attached_drives:
43
+ drives = []
44
+ for drive in cluster.attached_drives:
45
+ size_gib = int(drive.size_gib) if drive.size_gib else 0
46
+ drives.append(f"{drive.name} : {size_gib}GB")
47
+ info["attached_drives"] = sorted(drives)
48
+
49
+ return info
50
+
51
+
30
52
  def capture_costs(
31
53
  model_name: str,
32
54
  region: str,
@@ -48,31 +70,27 @@ def capture_costs(
48
70
  return {"error": "No capacity plans generated", "scenario": scenario_name}
49
71
 
50
72
  cap_plan = cap_plans[0]
51
- clusters = cap_plan.candidate_clusters
73
+ candidate = cap_plan.candidate_clusters
74
+
75
+ # Build cluster details for each cluster
76
+ cluster_details = []
77
+ for zonal_cluster in candidate.zonal:
78
+ cluster_details.append(_format_cluster(zonal_cluster, "zonal"))
79
+ for regional_cluster in candidate.regional:
80
+ cluster_details.append(_format_cluster(regional_cluster, "regional"))
52
81
 
53
82
  result = {
54
83
  "scenario": scenario_name,
55
84
  "model": model_name,
56
85
  "region": region,
57
86
  "service_tier": desires.service_tier,
87
+ "total_annual_cost": float(candidate.total_annual_cost),
88
+ "clusters": cluster_details,
58
89
  "annual_costs": dict(
59
- sorted((k, float(v)) for k, v in clusters.annual_costs.items())
90
+ sorted((k, float(v)) for k, v in candidate.annual_costs.items())
60
91
  ),
61
- "total_annual_cost": float(clusters.total_annual_cost),
62
- "cluster_count": len(clusters.zonal) + len(clusters.regional),
63
- "service_count": len(clusters.services),
64
92
  }
65
93
 
66
- # Add instance info
67
- if clusters.zonal:
68
- result["instance_name"] = clusters.zonal[0].instance.name
69
- result["instance_count"] = clusters.zonal[0].count
70
- result["deployment"] = "zonal"
71
- elif clusters.regional:
72
- result["instance_name"] = clusters.regional[0].instance.name
73
- result["instance_count"] = clusters.regional[0].count
74
- result["deployment"] = "regional"
75
-
76
94
  return result
77
95
  except (ValueError, KeyError, AttributeError) as e:
78
96
  return {"error": str(e), "scenario": scenario_name}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: service-capacity-modeling
3
- Version: 0.3.102
3
+ Version: 0.3.104
4
4
  Summary: Contains utilities for modeling capacity for pluggable workloads
5
5
  Author: Joseph Lynch
6
6
  Author-email: josephl@netflix.com
@@ -72,21 +72,21 @@ service_capacity_modeling/models/org/netflix/kafka.py,sha256=1GA18idG9kk7yfoFbzR
72
72
  service_capacity_modeling/models/org/netflix/key_value.py,sha256=WH8NblHqHwnAAumB2Zz1Qd4NBFWDQEQ1rpBcP3fVVQk,9409
73
73
  service_capacity_modeling/models/org/netflix/postgres.py,sha256=LBxDqkc-lYxDBu2VwNLuf2Q4o4hU3jPwu4YSt33Oe-8,4128
74
74
  service_capacity_modeling/models/org/netflix/rds.py,sha256=8GVmpMhTisZPdT-mP1Sx5U7VAF32lnTI27iYPfGg9CY,10930
75
- service_capacity_modeling/models/org/netflix/stateless_java.py,sha256=1oL74Qd6wSsDIWG_3Qt4x0qwgMQie-mJ6HKB0obgD5g,11379
75
+ service_capacity_modeling/models/org/netflix/stateless_java.py,sha256=-Se9VdLpKP8HdvvwGcbO87qsOfs9HiwUucV_uch0PxM,11574
76
76
  service_capacity_modeling/models/org/netflix/time_series.py,sha256=NjZTr0NC6c0tduY4O1Z6Nfm0S8Mt0pKxPgphywIulvQ,8240
77
77
  service_capacity_modeling/models/org/netflix/time_series_config.py,sha256=usV7y9NVb8hfGrB6POg63lzSfxUafyBMu0zP-HvPOMo,7326
78
78
  service_capacity_modeling/models/org/netflix/wal.py,sha256=QtRlqP_AIVpTg-XEINAfvf7J7J9EzXMY5PrxE3DIOU0,4482
79
79
  service_capacity_modeling/models/org/netflix/zookeeper.py,sha256=T_CkmRqoEVqpERCFPU8xihyaxlNfUHDJXz7dMHM8GD0,7679
80
80
  service_capacity_modeling/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  service_capacity_modeling/tools/auto_shape.py,sha256=K248-DayPrcZwLw1dYr47lpeQQwL0ylh1WAoVSdLNxw,23621
82
- service_capacity_modeling/tools/capture_baseline_costs.py,sha256=VXa5AEGpK4fGFhsDgKv-jKSkgLL8arAkuogv8Vm3Gq4,10729
82
+ service_capacity_modeling/tools/capture_baseline_costs.py,sha256=X4uVRHidQOVgtBTTCGfwHkp_OPPce3UZLKzyHimzsUY,11250
83
83
  service_capacity_modeling/tools/fetch_pricing.py,sha256=fO84h77cqiiIHF4hZt490RwbZ6JqjB45UsnPpV2AXD4,6122
84
84
  service_capacity_modeling/tools/generate_missing.py,sha256=F7YqvMJAV4nZc20GNrlIsnQSF8_77sLgwYZqc5k4LDg,3099
85
85
  service_capacity_modeling/tools/instance_families.py,sha256=e5RuYkCLUITvsAazDH12B6KjX_PaBsv6Ne3mj0HK_sQ,9223
86
86
  service_capacity_modeling/tools/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- service_capacity_modeling-0.3.102.dist-info/licenses/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
88
- service_capacity_modeling-0.3.102.dist-info/METADATA,sha256=_l-grWwJT6CWBSAX1VJEaGR8oSgrdBG9t1Z36PC-XAc,10367
89
- service_capacity_modeling-0.3.102.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
90
- service_capacity_modeling-0.3.102.dist-info/entry_points.txt,sha256=ZsjzpG5SomWpT1zCE19n1uSXKH2gTI_yc33sdl0vmJg,146
91
- service_capacity_modeling-0.3.102.dist-info/top_level.txt,sha256=H8XjTCLgR3enHq5t3bIbxt9SeUkUT8HT_SDv2dgIT_A,26
92
- service_capacity_modeling-0.3.102.dist-info/RECORD,,
87
+ service_capacity_modeling-0.3.104.dist-info/licenses/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
88
+ service_capacity_modeling-0.3.104.dist-info/METADATA,sha256=ysDkTgjsf5Z0SPMBaFMV1QQHw4LD_l4BZjVizDxU3eU,10367
89
+ service_capacity_modeling-0.3.104.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
90
+ service_capacity_modeling-0.3.104.dist-info/entry_points.txt,sha256=ZsjzpG5SomWpT1zCE19n1uSXKH2gTI_yc33sdl0vmJg,146
91
+ service_capacity_modeling-0.3.104.dist-info/top_level.txt,sha256=H8XjTCLgR3enHq5t3bIbxt9SeUkUT8HT_SDv2dgIT_A,26
92
+ service_capacity_modeling-0.3.104.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5