From 4342500f516e4400ad1f8b4d8046d8a4392398a2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Oct 2025 21:57:31 -0400 Subject: [PATCH 1/4] Teach scripts/detect_indentation_issues.py about Kconfig The script was detecting problems in Kconfig help text, where the lines begin with . Fix the script to cut down on false positives. Generated-by: Claude AI Signed-off-by: Chuck Lever --- scripts/detect_indentation_issues.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/detect_indentation_issues.py b/scripts/detect_indentation_issues.py index 98783b4cc..e41b76fe1 100755 --- a/scripts/detect_indentation_issues.py +++ b/scripts/detect_indentation_issues.py @@ -34,9 +34,11 @@ def check_file_indentation(file_path): # Special rules for certain file types file_ext = Path(file_path).suffix.lower() + file_name = Path(file_path).name is_yaml = file_ext in [".yml", ".yaml"] - is_makefile = "Makefile" in Path(file_path).name or file_ext == ".mk" + is_makefile = "Makefile" in file_name or file_ext == ".mk" is_python = file_ext == ".py" + is_kconfig = file_name.startswith("Kconfig") # Check each line for issues for line_num, line in enumerate(lines, 1): @@ -69,6 +71,11 @@ def check_file_indentation(file_path): issues.append( f"Line {line_num}: Tab character in Python file (PEP 8 recommends spaces)" ) + elif is_kconfig: + # Kconfig files use tabs for indentation and tab+spaces for help text + # The pattern "\t " (tab followed by 2 spaces) is valid for help text + # So we don't flag this as mixed indentation + pass else: # For other files, check for mixed indentation if leading_ws: From 418ddf1e9433fc0135a808910869a7ffe7c4e4be Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 21 Oct 2025 14:56:55 -0400 Subject: [PATCH 2/4] scripts: Add --provider argument to generate_cloud_configs.py This adds command-line argument support to allow generating Kconfig menus for specific cloud providers instead of always processing all providers. The script now accepts an optional --provider argument that can be set to lambdalabs, aws, azure, gce, or oci to generate configuration for only that provider. When no --provider argument is specified, the script maintains backward compatibility by generating configs for all providers. This improves the user experience when working with a specific cloud provider and reduces unnecessary API calls and processing time. Generated-by: Claude AI Signed-off-by: Chuck Lever --- scripts/generate_cloud_configs.py | 82 ++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/scripts/generate_cloud_configs.py b/scripts/generate_cloud_configs.py index 4f833c406..c47c1ff01 100755 --- a/scripts/generate_cloud_configs.py +++ b/scripts/generate_cloud_configs.py @@ -10,6 +10,7 @@ import sys import subprocess import json +import argparse def generate_lambdalabs_kconfig() -> bool: @@ -146,16 +147,12 @@ def generate_aws_kconfig() -> bool: return all_success -def main(): - """Main function to generate cloud configurations.""" - print("Cloud Provider Configuration Summary") - print("=" * 60) - print() - - # Lambda Labs - Generate Kconfig files first +def process_lambdalabs(): + """Process Lambda Labs configuration.""" + # Generate Kconfig files first kconfig_generated = generate_lambdalabs_kconfig() - # Lambda Labs - Get summary + # Get summary success, summary = get_lambdalabs_summary() if success: print(f"✓ {summary}") @@ -167,7 +164,9 @@ def main(): print(f"⚠ {summary}") print() - # AWS - Generate Kconfig files + +def process_aws(): + """Process AWS configuration.""" kconfig_generated = generate_aws_kconfig() if kconfig_generated: print("✓ AWS: Kconfig files generated successfully") @@ -175,15 +174,74 @@ def main(): print("⚠ AWS: Failed to generate Kconfig files - using defaults") print() - # Azure (placeholder - not implemented) + +def process_azure(): + """Process Azure configuration (placeholder).""" print("⚠ Azure: Dynamic configuration not yet implemented") - # GCE (placeholder - not implemented) + +def process_gce(): + """Process GCE configuration (placeholder).""" print("⚠ GCE: Dynamic configuration not yet implemented") - # OCI (placeholder - not implemented) + +def process_oci(): + """Process OCI configuration (placeholder).""" print("⚠ OCI: Dynamic configuration not yet implemented") + +def main(): + """Main function to generate cloud configurations.""" + parser = argparse.ArgumentParser( + description="Generate dynamic cloud configurations for supported providers", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s # Generate configs for all providers + %(prog)s --provider lambdalabs # Generate configs for Lambda Labs only + %(prog)s --provider aws # Generate configs for AWS only + %(prog)s --provider azure # Generate configs for Azure only + +Supported providers: lambdalabs, aws, azure, gce, oci + """, + ) + + parser.add_argument( + "--provider", + choices=["lambdalabs", "aws", "azure", "gce", "oci"], + help="Generate configuration for a specific cloud provider only", + ) + + args = parser.parse_args() + + # Provider dispatch table + providers = { + "lambdalabs": process_lambdalabs, + "aws": process_aws, + "azure": process_azure, + "gce": process_gce, + "oci": process_oci, + } + + print("Cloud Provider Configuration Summary") + print("=" * 60) + print() + + # If a specific provider is requested, only process that one + if args.provider: + if args.provider in providers: + providers[args.provider]() + else: + print(f"Error: Unknown provider '{args.provider}'", file=sys.stderr) + sys.exit(1) + else: + # Process all providers + process_lambdalabs() + process_aws() + process_azure() + process_gce() + process_oci() + print() print("Note: Dynamic configurations query real-time availability") print("Run 'make menuconfig' to configure your cloud provider") From 6898a157fc7986708013236c3fda34791b0379b5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 21 Oct 2025 15:32:14 -0400 Subject: [PATCH 3/4] dynamic-cloud-kconfig.Makefile: Add individual provider make targets Utilize the new command-line argument to generate_cloud_configs.py to enable users and scripts to generate only one provider's Kconfig menus. This appears to have been the intent for Lambda Labs, but was never implemented fully. Generated-by: Claude AI Signed-off-by: Chuck Lever --- scripts/dynamic-cloud-kconfig.Makefile | 37 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/scripts/dynamic-cloud-kconfig.Makefile b/scripts/dynamic-cloud-kconfig.Makefile index e15651abd..909661572 100644 --- a/scripts/dynamic-cloud-kconfig.Makefile +++ b/scripts/dynamic-cloud-kconfig.Makefile @@ -12,30 +12,51 @@ LAMBDALABS_KCONFIG_IMAGES := $(LAMBDALABS_KCONFIG_DIR)/Kconfig.images.generated LAMBDALABS_KCONFIGS := $(LAMBDALABS_KCONFIG_COMPUTE) $(LAMBDALABS_KCONFIG_LOCATION) $(LAMBDALABS_KCONFIG_IMAGES) -# Add Lambda Labs generated files to mrproper clean list -KDEVOPS_MRPROPER += $(LAMBDALABS_KCONFIGS) +# AWS dynamic configuration +AWS_KCONFIG_DIR := terraform/aws/kconfigs +AWS_KCONFIG_AMI := $(AWS_KCONFIG_DIR)/Kconfig.ami +AWS_KCONFIG_INSTANCE := $(AWS_KCONFIG_DIR)/Kconfig.instance +AWS_KCONFIG_LOCATION := $(AWS_KCONFIG_DIR)/Kconfig.location + +AWS_KCONFIGS := $(AWS_KCONFIG_AMI) $(AWS_KCONFIG_INSTANCE) $(AWS_KCONFIG_LOCATION) + +# Add generated files to mrproper clean list +KDEVOPS_MRPROPER += $(LAMBDALABS_KCONFIGS) $(AWS_KCONFIGS) # Touch Lambda Labs generated files so Kconfig can source them # This ensures the files exist (even if empty) before Kconfig runs dynamic_lambdalabs_kconfig_touch: $(Q)touch $(LAMBDALABS_KCONFIGS) -DYNAMIC_KCONFIG += dynamic_lambdalabs_kconfig_touch +# Touch AWS generated files so Kconfig can source them +dynamic_aws_kconfig_touch: + $(Q)touch $(AWS_KCONFIGS) + +DYNAMIC_KCONFIG += dynamic_lambdalabs_kconfig_touch dynamic_aws_kconfig_touch -# Individual Lambda Labs targets are now handled by generate_cloud_configs.py +# Lambda Labs targets use --provider argument for efficiency cloud-config-lambdalabs: - $(Q)python3 scripts/generate_cloud_configs.py + $(Q)python3 scripts/generate_cloud_configs.py --provider lambdalabs + +# AWS targets use --provider argument for efficiency +cloud-config-aws: + $(Q)python3 scripts/generate_cloud_configs.py --provider aws # Clean Lambda Labs generated files clean-cloud-config-lambdalabs: $(Q)rm -f $(LAMBDALABS_KCONFIGS) -DYNAMIC_CLOUD_KCONFIG += cloud-config-lambdalabs +# Clean AWS generated files +clean-cloud-config-aws: + $(Q)rm -f $(AWS_KCONFIGS) + +DYNAMIC_CLOUD_KCONFIG += cloud-config-lambdalabs cloud-config-aws 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 "cloud-config-aws - generates AWS dynamic kconfig content" @echo "clean-cloud-config - removes all generated cloud kconfig files" @echo "cloud-list-all - list all cloud instances for configured provider" @@ -44,11 +65,11 @@ HELP_TARGETS += cloud-config-help cloud-config: $(Q)python3 scripts/generate_cloud_configs.py -clean-cloud-config: clean-cloud-config-lambdalabs +clean-cloud-config: clean-cloud-config-lambdalabs clean-cloud-config-aws $(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 +PHONY += cloud-config cloud-config-lambdalabs cloud-config-aws clean-cloud-config clean-cloud-config-lambdalabs clean-cloud-config-aws cloud-config-help cloud-list-all From 8f85fa6d5f9b732f102671ef0bac8bc470f025a9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 22 Oct 2025 11:27:38 -0400 Subject: [PATCH 4/4] volume_groups: Fix tasks/terraform/aws.yml Commit 9d6332ca3121 ("volume_group: Use cloud provider's default data disk") was incomplete. Take full advantage of the new mechanism to determine which EBS devices are available for use by kdevops. Fixes: 9d6332ca3121 ("volume_group: Use cloud provider's default data disk") Assisted-by: Claude AI Signed-off-by: Chuck Lever --- .../volume_group/tasks/terraform/aws.yml | 44 +++++-------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/playbooks/roles/volume_group/tasks/terraform/aws.yml b/playbooks/roles/volume_group/tasks/terraform/aws.yml index e26776407..24d6f01e3 100644 --- a/playbooks/roles/volume_group/tasks/terraform/aws.yml +++ b/playbooks/roles/volume_group/tasks/terraform/aws.yml @@ -10,39 +10,19 @@ # are not fixed and cannot be depended upon to locate specific # devices or their content. # -# The block device name attached to each volume is fixed by -# terraform but not exposed to Ansible or the guest. It has to be -# extracted from terraform state and matched to a volume ID, which -# appears as the device serial number. +# Instead, rely on a directory created by a udev rule to find the +# EBS block devices attached on behalf of kdevops. The rule +# guarantees the symlinks names always point to the same block +# device. # -# The root EBS device is not included in terraform's block device -# map, so is easily avoided. -# - -- name: Retrieve the block_device_map from terraform - delegate_to: localhost - run_once: true - ansible.builtin.command: - chdir: "{{ topdir_path }}/terraform/aws/" - cmd: "terraform output -json block_device_map" - register: terraform_output - changed_when: false -- name: Exclude the device that will house the /data file system - vars: - block_device_dict: "{{ terraform_output.stdout | from_json }}" - local_map: "{{ block_device_dict[inventory_hostname] }}" - ansible.builtin.set_fact: - ebs_volume_ids: "{{ ebs_volume_ids + ['nvme-Amazon_Elastic_Block_Store_' + item.value | regex_replace('-', '')] }}" - when: - item.key != data_device - with_dict: "{{ local_map }}" +- name: Retrieve list of symlinks residing under /dev/disk/kdevops + ansible.builtin.find: + file_type: link + paths: "/dev/disk/kdevops" + excludes: "{{ data_device | basename }}" + register: find_output -- name: Add unused EBS volumes to the volume list +- name: Extract device paths from find output ansible.builtin.set_fact: - physical_volumes: "{{ physical_volumes + ['/dev/' + item.key] }}" - when: - - item.value.links.ids | intersect(ebs_volume_ids) | list | count > 0 - loop_control: - label: "Adding block device: /dev/{{ item.key }}" - with_dict: "{{ ansible_devices }}" + physical_volumes: "{{ find_output.files | map(attribute='path') | list }}"