Files
2026-08-21 01:55:21 +00:00

110 lines
3.5 KiB
Python

"""ConnectVM rate card (INR) and pricing engine.
Model: flavor-based monthly pricing, prorated by billable hours in the
billing month. A VM present for the whole month pays its full flavor price;
created mid-month it prorates. Custom flavors (not on the card) are priced
from add-on unit rates. Volumes and floating IPs use add-on rates.
"""
# ---- Rate card: flavor monthly prices (INR) -------------------------------
# key = normalized flavor name ("B1 - Lite" / "B1 · Lite" -> "b1 lite")
FLAVOR_MONTHLY = {
"m2 tiny": 1200.0,
"m1 standard": 2303.0,
"a1 nano": 2303.0,
"b1 lite": 4607.0,
"c1 core": 9215.0,
"r1 medium": 27647.0,
"s1 large": 34559.0,
"t3 max": 43199.0,
"d2 edge": 46079.0,
"t2 xlarge": 62207.0,
"w2 prime": 73727.0,
"x1 elite": 92159.0,
}
# Card specs (vcpus, ram_mb, disk_gb) to detect spec drift on named flavors
FLAVOR_SPECS = {
"m2 tiny": (1, 2048, 10),
"m1 standard": (1, 4096, 20),
"a1 nano": (2, 4096, 20),
"b1 lite": (4, 8192, 40),
"c1 core": (8, 16384, 80),
"r1 medium": (16, 65536, 200),
"s1 large": (24, 65536, 300),
"t3 max": (32, 131072, 480),
"d2 edge": (12, 32768, 100),
"t2 xlarge": (32, 98304, 480),
"w2 prime": (42, 131072, 500),
"x1 elite": (64, 131072, 800),
}
# ---- Add-on rates (INR/month) ---------------------------------------------
ADDON_VCPU = 480.0 # per vCPU
ADDON_RAM_PER_GB = 192.0 # per GB RAM
ADDON_DISK_PER_GB = 9.6 # ₹96 per 10 GB SSD
FIP_MONTHLY = 300.0 # additional IPv4
SNAPSHOT_PER_10GB = 48.0 # not metered yet
LB_MONTHLY = 1440.0 # not metered yet (Octavia)
BACKUP_PER_10GB = 72.0 # not metered yet
BANDWIDTH_PER_GB = 1.80 # not metered yet
RATE_CARD_INFO = {
"currency": "INR",
"model": "flavor monthly price prorated by billable hours; "
"custom flavors computed from add-on rates",
"addon_rates_per_month": {
"vcpu": ADDON_VCPU,
"ram_gb": ADDON_RAM_PER_GB,
"ssd_gb": ADDON_DISK_PER_GB,
"floating_ip": FIP_MONTHLY,
},
"not_yet_metered": {
"snapshot_per_10gb": SNAPSHOT_PER_10GB,
"load_balancer": LB_MONTHLY,
"backup_per_10gb": BACKUP_PER_10GB,
"bandwidth_per_gb": BANDWIDTH_PER_GB,
},
}
def normalize_flavor(name: str) -> str:
for ch in ("·", "-", "_", "."):
name = name.replace(ch, " ")
return " ".join(name.lower().split())
def flavor_monthly(name: str, vcpus: int, ram_mb: int, disk_gb: int):
"""Return (monthly_price_inr, source, spec_mismatch)."""
key = normalize_flavor(name or "")
if key in FLAVOR_MONTHLY:
spec = FLAVOR_SPECS[key]
mismatch = spec != (vcpus, ram_mb, disk_gb)
return FLAVOR_MONTHLY[key], "rate-card", mismatch
price = (
vcpus * ADDON_VCPU
+ (ram_mb / 1024) * ADDON_RAM_PER_GB
+ disk_gb * ADDON_DISK_PER_GB
)
return price, "computed", False
def period_hours(begin, end) -> float:
return (end - begin).total_seconds() / 3600
def inr(amount: float) -> str:
"""Format with Indian digit grouping: 92,159.00 / 1,23,456.00"""
neg = amount < 0
whole, _, frac = f"{abs(amount):.2f}".partition(".")
if len(whole) > 3:
head, tail = whole[:-3], whole[-3:]
groups = []
while len(head) > 2:
groups.insert(0, head[-2:])
head = head[:-2]
if head:
groups.insert(0, head)
whole = ",".join(groups + [tail])
return f"₹{'-' if neg else ''}{whole}.{frac}"