From 9a1d52230d0874490dd20b8f5412b5381f60c108 Mon Sep 17 00:00:00 2001 From: Jino Jose Date: Wed, 8 Jul 2026 15:21:05 +0530 Subject: [PATCH] Harden workstation tier handling --- ansible/roles/cezen-backend/files/main.py | 20 +++++++++++++++----- autoinstall/firstboot-setup.sh | 8 ++++---- install.sh | 17 ++++++++++++++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/ansible/roles/cezen-backend/files/main.py b/ansible/roles/cezen-backend/files/main.py index ffda339..b864365 100644 --- a/ansible/roles/cezen-backend/files/main.py +++ b/ansible/roles/cezen-backend/files/main.py @@ -1950,9 +1950,15 @@ async def readiness_report(admin: dict = Depends(admin_only)): entitlement = _entitlement_summary(feasibility) readiness = _readiness_score(feasibility, license_payload) recommended_tier = (feasibility.get("recommendation") or {}).get("recommended_tier", "starter") - current_rank = list(TIER_MATRIX.keys()).index(entitlement.get("licensed_tier") or license_payload["tier"]) - recommended_rank = list(TIER_MATRIX.keys()).index(_normalize_tier(recommended_tier)) - commercial_fit = "matched" if current_rank >= recommended_rank else "license_upgrade_recommended" + current_tier = entitlement.get("licensed_tier") or license_payload["tier"] + if license_payload.get("category") == CATEGORY_WORKSTATION or tier_category(current_tier) == CATEGORY_WORKSTATION: + commercial_fit = "workstation_category" + commercial_fit_note = "Workstation is a standalone product category, so Server S/M/L/Max upgrade guidance does not apply directly." + else: + current_rank = list(TIER_MATRIX.keys()).index(current_tier) + recommended_rank = list(TIER_MATRIX.keys()).index(_normalize_tier(recommended_tier)) + commercial_fit = "matched" if current_rank >= recommended_rank else "license_upgrade_recommended" + commercial_fit_note = "Current license covers the recommended deployment." if commercial_fit == "matched" else "Quote a higher tier or reduce enabled features for this hardware." return { "schema": "cezen.readiness_report.v1", "generated_at": utcnow(), @@ -1963,10 +1969,10 @@ async def readiness_report(admin: dict = Depends(admin_only)): "readiness": readiness, "commercial_fit": { "status": commercial_fit, - "current_tier": entitlement.get("licensed_tier") or license_payload["tier"], + "current_tier": current_tier, "provisioned_tier": entitlement.get("provisioned_tier"), "recommended_tier": _normalize_tier(recommended_tier), - "note": "Current license covers the recommended deployment." if commercial_fit == "matched" else "Quote a higher tier or reduce enabled features for this hardware.", + "note": commercial_fit_note, }, "proposal_summary": [ f"Recommended Cezen profile: {(feasibility.get('recommendation') or {}).get('recommended_profile', 'core')}", @@ -2070,6 +2076,10 @@ async def get_branding(): result["license_support_until"] = license_record.get("support_until", "") result["license_status"] = entitlement.get("license_status", "missing") result["provisioned_tier"] = entitlement.get("provisioned_tier", "") + tier = result.get("tier_slug") + if tier in TIER_MATRIX: + result["tier_commercial_label"] = TIER_MATRIX[tier].get("commercial_label", TIER_MATRIX[tier]["label"]) + result["tier_category"] = TIER_MATRIX[tier].get("category", CATEGORY_SERVER) return result @app.put("/api/settings/branding") diff --git a/autoinstall/firstboot-setup.sh b/autoinstall/firstboot-setup.sh index 5cb0464..cbc6fa7 100644 --- a/autoinstall/firstboot-setup.sh +++ b/autoinstall/firstboot-setup.sh @@ -399,10 +399,10 @@ python3 - <<'PY' import json, os, time # NOTE: install.sh (Phase 2) rewrites /opt/cezen/install-record.json after -# this, using its own $TIER routing value ("starter" for Workstation) — this -# is expected: provisioned_tier there reflects the Ansible profile actually -# installed. The persistent Workstation marker is /opt/cezen/tier, not this -# file, and install.sh never touches that marker. +# this. For Workstation, it preserves category="workstation" and selected_tier +# = "workstation", while provisioned_tier becomes "starter" to reflect the +# Ansible profile actually installed. The persistent Workstation marker is +# still /opt/cezen/tier, and install.sh never touches that marker. payload = { "schema": "cezen.install_record.v1", "customer_name": os.environ.get("CUSTOMER_NAME", "").strip(), diff --git a/install.sh b/install.sh index 5d5274d..d672384 100644 --- a/install.sh +++ b/install.sh @@ -22,6 +22,8 @@ elif [ -f /opt/aipackage/autoinstall/.tier ]; then else TIER="basic" # default if no marker found fi +DISPLAY_TIER="$TIER" +PRODUCT_CATEGORY="server" PHASE="1" SKIP_ROLES="" SOFTWARE_ONLY=false @@ -55,11 +57,19 @@ done normalize_tier() { case "$TIER" in + workstation) + DISPLAY_TIER="workstation" + PRODUCT_CATEGORY="workstation" + TIER="starter" + ;; entry|basic) TIER="basic" ;; mid|pro) TIER="pro" ;; advanced|max) TIER="max" ;; starter) TIER="starter" ;; esac + if [ "$PRODUCT_CATEGORY" = "server" ]; then + DISPLAY_TIER="$TIER" + fi } normalize_tier @@ -191,7 +201,7 @@ PY } write_install_record() { - python3 - "$INSTALL_RECORD_JSON" "$TIER" "$PROFILE" "$SKIP_ROLES" "$GPU_AVAILABLE" "$FEASIBILITY_JSON" "$LICENSE_EVAL_JSON" <<'PY' + python3 - "$INSTALL_RECORD_JSON" "$TIER" "$PROFILE" "$SKIP_ROLES" "$GPU_AVAILABLE" "$FEASIBILITY_JSON" "$LICENSE_EVAL_JSON" "$DISPLAY_TIER" "$PRODUCT_CATEGORY" <<'PY' import json, sys from datetime import datetime, timezone from pathlib import Path @@ -226,8 +236,9 @@ components = { payload = { "schema": "cezen.install_record.v1", "generated_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"), - "selected_tier": tier, + "selected_tier": sys.argv[8], "provisioned_tier": tier, + "category": sys.argv[9], "provisioned_profile": profile, "skip_roles": skip_roles, "components": components, @@ -413,7 +424,7 @@ run_phase2() { echo "" echo "╔══════════════════════════════════════════╗" echo "║ Nexus One AI installation complete! ║" - echo "║ Tier: $(printf '%-33s' "$TIER")║" + echo "║ Tier: $(printf '%-33s' "$DISPLAY_TIER")║" echo "║ ║" echo "║ Portal → http://localhost ║" echo "║ Ollama API → http://localhost:11434 ║"