All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luis Chamberlain <mcgrof@kernel.org>
To: Chuck Lever <cel@kernel.org>, Daniel Gomez <da.gomez@kruces.com>,
	kdevops@lists.linux.dev
Cc: Luis Chamberlain <mcgrof@kernel.org>
Subject: [PATCH v2 05/10] kconfig: add dynamic cloud provider configuration infrastructure
Date: Wed, 27 Aug 2025 14:28:56 -0700	[thread overview]
Message-ID: <20250827212902.4021990-6-mcgrof@kernel.org> (raw)
In-Reply-To: <20250827212902.4021990-1-mcgrof@kernel.org>

Introduce a new dynamic configuration system that queries cloud provider
APIs to generate real-time Kconfig options. This allows configuration
menus to show current availability and capacity directly from the cloud.

The system adds:
- Python script to query APIs and generate Kconfig files
- New 'make cloud-config' target for cloud-specific updates
- Integration with existing 'make dynconfig' infrastructure
- CLOUD_INITIALIZED marker to detect cloud setup state
- Automatic fallback to static defaults when API unavailable

This sets up the framework for API-driven configuration but doesn't
enable any specific providers yet. The architecture supports future
extension to AWS, Azure, GCE, and other cloud providers.

Generated-by: Claude AI
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
 kconfigs/Kconfig.bringup               |   5 +
 scripts/dynamic-cloud-kconfig.Makefile |  44 +++++
 scripts/dynamic-kconfig.Makefile       |   2 +
 scripts/generate_cloud_configs.py      | 223 +++++++++++++++++++++++++
 scripts/lambdalabs_ssh_keys.py         |   3 +-
 5 files changed, 276 insertions(+), 1 deletion(-)
 create mode 100644 scripts/dynamic-cloud-kconfig.Makefile
 create mode 100755 scripts/generate_cloud_configs.py

diff --git a/kconfigs/Kconfig.bringup b/kconfigs/Kconfig.bringup
index 8caf07b..b64ba50 100644
--- a/kconfigs/Kconfig.bringup
+++ b/kconfigs/Kconfig.bringup
@@ -9,8 +9,13 @@ config KDEVOPS_ENABLE_NIXOS
 	bool
 	output yaml
 
+config CLOUD_INITIALIZED
+	bool
+	default $(shell, test -f .cloud.initialized && echo y || echo n) = "y"
+
 choice
 	prompt "Node bring up method"
+	default TERRAFORM if CLOUD_INITIALIZED
 	default GUESTFS
 
 config GUESTFS
diff --git a/scripts/dynamic-cloud-kconfig.Makefile b/scripts/dynamic-cloud-kconfig.Makefile
new file mode 100644
index 0000000..cc0a6b8
--- /dev/null
+++ b/scripts/dynamic-cloud-kconfig.Makefile
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: copyleft-next-0.3.1
+# Dynamic cloud provider Kconfig generation
+
+DYNAMIC_CLOUD_KCONFIG :=
+DYNAMIC_CLOUD_KCONFIG_ARGS :=
+
+# Lambda Labs dynamic configuration
+LAMBDALABS_KCONFIG_DIR := terraform/lambdalabs/kconfigs
+LAMBDALABS_KCONFIG_COMPUTE := $(LAMBDALABS_KCONFIG_DIR)/Kconfig.compute.generated
+LAMBDALABS_KCONFIG_LOCATION := $(LAMBDALABS_KCONFIG_DIR)/Kconfig.location.generated
+LAMBDALABS_KCONFIG_IMAGES := $(LAMBDALABS_KCONFIG_DIR)/Kconfig.images.generated
+
+LAMBDALABS_KCONFIGS := $(LAMBDALABS_KCONFIG_COMPUTE) $(LAMBDALABS_KCONFIG_LOCATION) $(LAMBDALABS_KCONFIG_IMAGES)
+
+# Individual Lambda Labs targets are now handled by generate_cloud_configs.py
+cloud-config-lambdalabs:
+	$(Q)python3 scripts/generate_cloud_configs.py
+
+# Clean Lambda Labs generated files
+clean-cloud-config-lambdalabs:
+	$(Q)rm -f $(LAMBDALABS_KCONFIGS)
+
+DYNAMIC_CLOUD_KCONFIG += cloud-config-lambdalabs
+
+cloud-config-help:
+	@echo "Cloud-specific dynamic kconfig targets:"
+	@echo "cloud-config            - generates all cloud provider dynamic kconfig content"
+	@echo "cloud-config-lambdalabs - generates Lambda Labs dynamic kconfig content"
+	@echo "clean-cloud-config      - removes all generated cloud kconfig files"
+	@echo "cloud-list-all          - list all cloud instances for configured provider"
+
+HELP_TARGETS += cloud-config-help
+
+cloud-config:
+	$(Q)python3 scripts/generate_cloud_configs.py
+
+clean-cloud-config: clean-cloud-config-lambdalabs
+	$(Q)echo "Cleaned all cloud provider dynamic Kconfig files."
+
+cloud-list-all:
+	$(Q)chmod +x scripts/cloud_list_all.sh
+	$(Q)scripts/cloud_list_all.sh
+
+PHONY += cloud-config cloud-config-lambdalabs clean-cloud-config clean-cloud-config-lambdalabs cloud-config-help cloud-list-all
diff --git a/scripts/dynamic-kconfig.Makefile b/scripts/dynamic-kconfig.Makefile
index b6c0e43..bab83e3 100644
--- a/scripts/dynamic-kconfig.Makefile
+++ b/scripts/dynamic-kconfig.Makefile
@@ -6,6 +6,7 @@ DYNAMIC_KCONFIG_PCIE_ARGS :=
 HELP_TARGETS += dynamic-kconfig-help
 
 include $(TOPDIR)/scripts/dynamic-pci-kconfig.Makefile
+include $(TOPDIR)/scripts/dynamic-cloud-kconfig.Makefile
 
 ANSIBLE_EXTRA_ARGS += $(DYNAMIC_KCONFIG_PCIE_ARGS)
 
@@ -19,5 +20,6 @@ PHONY += dynamic-kconfig-help
 
 dynconfig:
 	$(Q)$(MAKE) dynconfig-pci
+	$(Q)$(MAKE) cloud-config
 
 PHONY += dynconfig
diff --git a/scripts/generate_cloud_configs.py b/scripts/generate_cloud_configs.py
new file mode 100755
index 0000000..294a1d9
--- /dev/null
+++ b/scripts/generate_cloud_configs.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: copyleft-next-0.3.1
+
+"""
+Generate dynamic cloud configurations for all supported providers.
+Provides a summary of available options and pricing.
+"""
+
+import os
+import sys
+import subprocess
+import json
+import urllib.request
+import urllib.error
+from typing import Dict, List, Optional, Tuple
+
+# Import our credentials module
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from lambdalabs_credentials import get_api_key as get_api_key_from_credentials
+
+
+def get_lambdalabs_summary() -> Tuple[bool, str]:
+    """
+    Get a summary of Lambda Labs configurations.
+    Returns (success, summary_string)
+    """
+    api_key = get_api_key_from_credentials()
+    if not api_key:
+        return False, "Lambda Labs: API key not set - using defaults"
+
+    try:
+        # Get instance types with capacity
+        headers = {"Authorization": f"Bearer {api_key}", "User-Agent": "kdevops/1.0"}
+        req = urllib.request.Request(
+            "https://cloud.lambdalabs.com/api/v1/instance-types", headers=headers
+        )
+
+        with urllib.request.urlopen(req) as response:
+            data = json.loads(response.read().decode())
+
+            if "data" not in data:
+                return False, "Lambda Labs: Invalid API response"
+
+            # Get pricing data
+            pricing = {
+                "gpu_1x_gh200": 1.49,
+                "gpu_1x_h100_sxm": 3.29,
+                "gpu_1x_h100_pcie": 2.49,
+                "gpu_1x_a100": 1.29,
+                "gpu_1x_a100_sxm": 1.29,
+                "gpu_1x_a100_pcie": 1.29,
+                "gpu_1x_a10": 0.75,
+                "gpu_1x_a6000": 0.80,
+                "gpu_1x_rtx6000": 0.50,
+                "gpu_1x_quadro_rtx_6000": 0.50,
+                "gpu_2x_h100_sxm": 6.38,
+                "gpu_2x_a100": 2.58,
+                "gpu_2x_a100_pcie": 2.58,
+                "gpu_2x_a6000": 1.60,
+                "gpu_4x_h100_sxm": 12.36,
+                "gpu_4x_a100": 5.16,
+                "gpu_4x_a100_pcie": 5.16,
+                "gpu_4x_a6000": 3.20,
+                "gpu_8x_b200_sxm": 39.92,
+                "gpu_8x_h100_sxm": 23.92,
+                "gpu_8x_a100_80gb": 14.32,
+                "gpu_8x_a100_80gb_sxm": 14.32,
+                "gpu_8x_a100": 10.32,
+                "gpu_8x_a100_40gb": 10.32,
+                "gpu_8x_v100": 4.40,
+            }
+
+            # Count available instances and get price range
+            available_count = 0
+            total_count = len(data["data"])
+            available_prices = []
+            all_regions = set()
+
+            for instance_type, info in data["data"].items():
+                regions = info.get("regions_with_capacity_available", [])
+                if regions:
+                    available_count += 1
+                    if instance_type in pricing:
+                        available_prices.append(pricing[instance_type])
+                    for r in regions:
+                        all_regions.add(r["name"])
+
+            # Format summary
+            if available_prices:
+                min_price = min(available_prices)
+                max_price = max(available_prices)
+                price_range = f"${min_price:.2f}-${max_price:.2f}/hr"
+            else:
+                price_range = "pricing varies"
+
+            region_count = len(all_regions)
+
+            return (
+                True,
+                f"Lambda Labs: {available_count}/{total_count} GPU types available, {region_count} regions, {price_range}",
+            )
+
+    except urllib.error.HTTPError as e:
+        if e.code == 403:
+            return False, "Lambda Labs: API key invalid - using defaults"
+        else:
+            return False, f"Lambda Labs: API error {e.code}"
+    except Exception as e:
+        return False, f"Lambda Labs: Error - {str(e)}"
+
+
+def generate_lambdalabs_configs(output_dir: str) -> bool:
+    """Generate Lambda Labs Kconfig files."""
+    try:
+        # Run the lambdalabs_api.py script
+        result = subprocess.run(
+            [sys.executable, "scripts/lambdalabs_api.py", "all", output_dir],
+            capture_output=True,
+            text=True,
+        )
+
+        if result.returncode != 0:
+            print(
+                f"  ⚠ Error generating Lambda Labs configs: {result.stderr}",
+                file=sys.stderr,
+            )
+            return False
+
+        return True
+    except Exception as e:
+        print(f"  ⚠ Error: {e}", file=sys.stderr)
+        return False
+
+
+def generate_aws_configs(output_dir: str) -> bool:
+    """
+    Generate AWS Kconfig files (placeholder for future implementation).
+    """
+    # For now, just return True as AWS uses static configs
+    return True
+
+
+def generate_azure_configs(output_dir: str) -> bool:
+    """
+    Generate Azure Kconfig files (placeholder for future implementation).
+    """
+    # For now, just return True as Azure uses static configs
+    return True
+
+
+def generate_gce_configs(output_dir: str) -> bool:
+    """
+    Generate GCE Kconfig files (placeholder for future implementation).
+    """
+    # For now, just return True as GCE uses static configs
+    return True
+
+
+def main():
+    """Main function to generate all cloud configurations."""
+    print("Generating dynamic cloud configurations based on latest data...")
+    print()
+
+    # Create .cloud.initialized marker file to signal cloud support is configured
+    # This will be used by Kconfig to set intelligent defaults
+    try:
+        with open(".cloud.initialized", "w") as f:
+            f.write("# This file indicates cloud support has been initialized\n")
+            f.write("# Created by 'make cloud-config'\n")
+            f.write("# Kconfig will use this to set cloud-related defaults\n")
+    except Exception as e:
+        print(f"  ⚠ Warning: Could not create .cloud.initialized: {e}", file=sys.stderr)
+
+    # Get summaries for each provider
+    providers = []
+
+    # Lambda Labs
+    success, summary = get_lambdalabs_summary()
+    providers.append(("Lambda Labs", success, summary))
+
+    # Future providers (placeholders for when we add dynamic support)
+    # When these providers get dynamic config support, they would show:
+    # providers.append(("AWS", True, "AWS: 100+ instance types, 26 regions, $0.01-$40.00/hr"))
+    # providers.append(("Azure", True, "Azure: 200+ VM sizes, 60+ regions, $0.01-$50.00/hr"))
+    # providers.append(("GCE", True, "GCE: 50+ machine types, 35 regions, $0.01-$30.00/hr"))
+
+    # Print summaries
+    for provider, success, summary in providers:
+        if success:
+            print(f"  ✓ {summary}")
+        else:
+            print(f"  ⚠ {summary}")
+
+    print()
+
+    # Generate configurations for each provider
+    configs_generated = []
+
+    # Lambda Labs
+    print("  • Generating Lambda Labs configurations...")
+    if generate_lambdalabs_configs("terraform/lambdalabs/kconfigs"):
+        configs_generated.append("Lambda Labs")
+        print("    ✓ Instance types, regions, and capacity information updated")
+    else:
+        print("    ⚠ Using default configurations")
+
+    # Future providers would go here
+    # print("  • AWS configurations (static)...")
+    # configs_generated.append("AWS")
+
+    print()
+
+    if configs_generated:
+        print(f"✓ Cloud configurations ready for: {', '.join(configs_generated)}")
+        print("  Run 'make menuconfig' to select your cloud provider and options")
+    else:
+        print("⚠ No dynamic configurations were generated, using defaults")
+
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/scripts/lambdalabs_ssh_keys.py b/scripts/lambdalabs_ssh_keys.py
index d4caede..2fa9880 100755
--- a/scripts/lambdalabs_ssh_keys.py
+++ b/scripts/lambdalabs_ssh_keys.py
@@ -270,7 +270,8 @@ def main():
         print("Error: Lambda Labs API key not found", file=sys.stderr)
         print("Please configure your API key:", file=sys.stderr)
         print(
-            "  python3 scripts/lambdalabs_credentials.py set 'your-api-key'", file=sys.stderr
+            "  python3 scripts/lambdalabs_credentials.py set 'your-api-key'",
+            file=sys.stderr,
         )
         sys.exit(1)
 
-- 
2.50.1


  parent reply	other threads:[~2025-08-27 21:29 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-27 21:28 [PATCH v2 00/10] terraform: add Lambda Labs cloud provider support with dynamic API-driven configuration Luis Chamberlain
2025-08-27 21:28 ` [PATCH v2 01/10] gitignore: add entries for Lambda Labs dynamic configuration Luis Chamberlain
2025-08-27 21:28 ` [PATCH v2 02/10] scripts: add Lambda Labs Python API library Luis Chamberlain
2025-08-28 18:59   ` Chuck Lever
2025-08-28 19:33     ` Luis Chamberlain
2025-08-28 20:00       ` Chuck Lever
2025-08-28 20:03         ` Luis Chamberlain
2025-08-28 20:13           ` Chuck Lever
2025-08-28 20:16             ` Luis Chamberlain
2025-08-29 11:24               ` Luis Chamberlain
2025-08-29 13:48                 ` Chuck Lever
2025-08-27 21:28 ` [PATCH v2 03/10] scripts: add Lambda Labs credentials management Luis Chamberlain
2025-08-27 21:28 ` [PATCH v2 04/10] scripts: add Lambda Labs SSH key management utilities Luis Chamberlain
2025-08-27 21:28 ` Luis Chamberlain [this message]
2025-08-27 21:28 ` [PATCH v2 06/10] terraform/lambdalabs: add Kconfig structure for Lambda Labs Luis Chamberlain
2025-08-27 21:28 ` [PATCH v2 07/10] terraform/lambdalabs: add terraform provider implementation Luis Chamberlain
2025-08-27 21:28 ` [PATCH v2 08/10] ansible/terraform: integrate Lambda Labs into build system Luis Chamberlain
2025-08-27 21:29 ` [PATCH v2 09/10] scripts: add Lambda Labs testing and debugging utilities Luis Chamberlain
2025-08-27 21:29 ` [PATCH v2 10/10] terraform: enable Lambda Labs cloud provider in menus Luis Chamberlain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250827212902.4021990-6-mcgrof@kernel.org \
    --to=mcgrof@kernel.org \
    --cc=cel@kernel.org \
    --cc=da.gomez@kruces.com \
    --cc=kdevops@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.