Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"openmpi": "5.0.8",
"csi_driver_powerscale": "v2.15.0",
"rocm": "6.3.1",
"service_k8s": "1.34.1"
"service_k8s": "1.35.1"
}

# All of the passwords fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,12 @@ def powerscale_csm_values_parse_error_msg(error):
"URL must start with 'http://' or 'https://'."
)
def powerscale_image_version_mismatch_msg(image_name, values_image, service_k8s_image):
"""Returns error message when CSM values.yaml image version doesn't match service_k8s.json."""
"""Returns error message when CSM values.yaml image version doesn't match service_k8s (versioned)."""
return (
f"Image version mismatch for '{image_name}': "
f"CSM Observability values.yaml has '{values_image}' but "
f"service_k8s.json has '{service_k8s_image}'. "
f"Please update service_k8s.json to match the values.yaml version "
f"service_k8s (versioned) has '{service_k8s_image}'. "
f"Please update service_k8s (versioned) to match the values.yaml version "
f"and re-run local_repo.yml to mirror the correct image to Pulp."
)
# pylint: enable=invalid-name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,23 @@ def validate_software_config(
for software_pkg in data['softwares']:
software = software_pkg['name']
arch_list = software_pkg.get('arch')
# Get software version for versioned JSON files (e.g., service_k8s_v1.35.1.json)
software_version = software_pkg.get('version')
for arch in arch_list:
json_path = get_json_file_path(
software, cluster_os_type, cluster_os_version, input_file_path, arch)
software, cluster_os_type, cluster_os_version, input_file_path, arch,
software_version=software_version)
# Check if json_path is None or if the JSON syntax is invalid
if not json_path:
# Construct expected filename for error message
if software == "service_k8s" and software_version:
expected_file = f"{software}_v{software_version}.json"
else:
expected_file = f"{software}.json"
errors.append(
create_error_msg(
"Validation Error: ", software,
f"is present in software_config.json. JSON file not found: {software}.json"
f"is present in software_config.json. JSON file not found: {expected_file}"
)
)
else:
Expand Down Expand Up @@ -1809,24 +1817,34 @@ def get_config_file_paths(input_dir, data, software_config_file_path):

Returns:
dict: Dictionary containing resolved file paths:
- service_k8s_json_path: Path to service_k8s.json
- service_k8s_json_path: Path to service_k8s (versioned)
- csi_driver_powerscale_json_path: Path to csi_driver_powerscale.json
"""
# Try reading cluster_os_type/version from data first, then from software_config.json
cluster_os_type = data.get("cluster_os_type", "rhel")
cluster_os_version = data.get("cluster_os_version", "10.0")
service_k8s_version = None

if os.path.exists(software_config_file_path):
try:
with open(software_config_file_path, 'r', encoding='utf-8') as scf:
sc_data = json.load(scf)
cluster_os_type = sc_data.get("cluster_os_type", cluster_os_type)
cluster_os_version = sc_data.get("cluster_os_version", cluster_os_version)
# Extract service_k8s version from software_config.json
for sw in sc_data.get("softwares", []):
if sw.get("name") == "service_k8s" and sw.get("version"):
service_k8s_version = sw["version"]
break
except (json.JSONDecodeError, IOError):
pass

config_base_path = os.path.join(input_dir, "config", "x86_64", cluster_os_type, cluster_os_version)
service_k8s_json_path = os.path.join(config_base_path, "service_k8s.json")
# Use versioned service_k8s file - version is required
if not service_k8s_version:
raise ValueError("service_k8s version not found in software_config.json")
service_k8s_json = f"service_k8s_v{service_k8s_version}.json"
service_k8s_json_path = os.path.join(config_base_path, service_k8s_json)
csi_driver_powerscale_json_path = os.path.join(config_base_path, "csi_driver_powerscale.json")

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from ansible.module_utils.input_validation.common_utils import validation_utils
from ansible.module_utils.input_validation.common_utils import config
from ansible.module_utils.input_validation.common_utils import en_us_validation_msg
from ansible.module_utils.local_repo.software_utils import load_yaml, load_json
from ansible.module_utils.local_repo.software_utils import load_yaml, load_json, get_json_file_path

file_names = config.files
create_error_msg = validation_utils.create_error_msg
Expand Down Expand Up @@ -239,13 +239,22 @@ def validate_local_repo_config(input_file_path, data,
for software in software_config_json["softwares"]:
sw = software["name"]
arch_list = software.get("arch")
# Get software version for versioned JSON files (e.g., service_k8s_v1.35.1.json)
software_version = software.get("version")
for arch in arch_list:
json_path = create_file_path(
input_file_path,
f"config/{arch}{os_ver_path}" + sw +".json")
if not os.path.exists(json_path):
# Use get_json_file_path for proper versioned JSON file resolution
json_path = get_json_file_path(
sw, cluster_os_type, cluster_os_version,
software_config_file_path, arch,
software_version=software_version)
if not json_path or not os.path.exists(json_path):
# Construct expected filename for error message
if sw == "service_k8s" and software_version:
expected_file = f"{sw}_v{software_version}.json"
else:
expected_file = f"{sw}.json"
errors.append(
create_error_msg(sw + '/' + arch, f"{sw} JSON file not found for architecture {arch}.", json_path))
create_error_msg(sw + '/' + arch, f"{sw} JSON file not found for architecture {arch}.", expected_file))
else:
curr_json = load_json(json_path)
pkg_list = curr_json[sw]['cluster']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def validate_powerscale_telemetry_config(
))

# Cross-validate image versions
# between values.yaml and service_k8s.json
# between values.yaml and service_k8s (versioned)
service_k8s_json_path = config_paths.get(
"service_k8s_json_path", ""
)
Expand All @@ -204,7 +204,7 @@ def validate_powerscale_telemetry_config(
with open(service_k8s_json_path, 'r', encoding='utf-8') as sk8s_f:
service_k8s_data = json.load(sk8s_f)

# Build lookup: package -> tag from service_k8s.json
# Build lookup: package -> tag from service_k8s (versioned)
sk8s_images = {}
for entry in service_k8s_data.get(
"service_k8s", {}
Expand Down Expand Up @@ -237,7 +237,7 @@ def validate_powerscale_telemetry_config(
sidecar_proxy = karavi_auth.get("sidecarProxy", {})
if sidecar_proxy and sidecar_proxy.get("image"):
# csm-authorization-sidecar is in
# csi_driver_powerscale.json, not service_k8s.json
# csi_driver_powerscale.json, not service_k8s (versioned)
if (csi_driver_powerscale_json_path and
os.path.exists(csi_driver_powerscale_json_path)):
try:
Expand Down Expand Up @@ -305,18 +305,18 @@ def validate_powerscale_telemetry_config(
)
else:
logger.warning(
f"Image {sk8s_key} not found in service_k8s.json, "
f"Image {sk8s_key} not found in service_k8s file, "
f"skipping version check"
)

except (json.JSONDecodeError, IOError) as sk8s_err:
logger.warning(
f"Could not read service_k8s.json for "
f"Could not read service_k8s file for "
f"image version validation: {sk8s_err}"
)
else:
logger.warning(
f"service_k8s.json not found at {service_k8s_json_path}, "
f"service_k8s file not found at {service_k8s_json_path}, "
f"skipping image version validation"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,34 @@ def get_config_file_paths(input_dir, data, software_config_file_path):

Returns:
dict: Dictionary containing resolved file paths:
- service_k8s_json_path: Path to service_k8s.json
- service_k8s_json_path: Path to service_k8s (versioned)
- csi_driver_powerscale_json_path: Path to csi_driver_powerscale.json
"""
# Try reading cluster_os_type/version from data first, then from software_config.json
cluster_os_type = data.get("cluster_os_type", "rhel")
cluster_os_version = data.get("cluster_os_version", "10.0")
service_k8s_version = None

if os.path.exists(software_config_file_path):
try:
with open(software_config_file_path, 'r', encoding='utf-8') as scf:
sc_data = json.load(scf)
cluster_os_type = sc_data.get("cluster_os_type", cluster_os_type)
cluster_os_version = sc_data.get("cluster_os_version", cluster_os_version)
# Extract service_k8s version from software_config.json
for sw in sc_data.get("softwares", []):
if sw.get("name") == "service_k8s" and sw.get("version"):
service_k8s_version = sw["version"]
break
except (json.JSONDecodeError, IOError):
pass

config_base_path = os.path.join(input_dir, "config", "x86_64", cluster_os_type, cluster_os_version)
service_k8s_json_path = os.path.join(config_base_path, "service_k8s.json")
# Use versioned service_k8s file - version is required
if not service_k8s_version:
raise ValueError("service_k8s version not found in software_config.json")
service_k8s_json = f"service_k8s_v{service_k8s_version}.json"
service_k8s_json_path = os.path.join(config_base_path, service_k8s_json)
csi_driver_powerscale_json_path = os.path.join(config_base_path, "csi_driver_powerscale.json")

return {
Expand Down
14 changes: 12 additions & 2 deletions common/library/module_utils/local_repo/software_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def load_yaml(file_path):
return yaml.safe_load(file)

def get_json_file_path(software_name, cluster_os_type,
cluster_os_version, user_json_path, arch):
cluster_os_version, user_json_path, arch, software_version=None):
"""
Generate the file path for a JSON file based on the provided software name,
cluster OS type, cluster OS version, and user JSON path.
Expand All @@ -129,13 +129,23 @@ def get_json_file_path(software_name, cluster_os_type,
cluster_os_version (str): The version of the cluster operating system.
user_json_path (str): The path to the user JSON file.
arch: Architecture for a particular software
software_version (str, optional): Version of the software for versioned JSON files.
Used for software like service_k8s that have versioned JSON files
(e.g., service_k8s_v1.35.1.json).

Returns:
str or None: The file path for the JSON file if it exists, otherwise None.
"""
base_path = os.path.dirname(os.path.abspath(user_json_path))

# Handle versioned JSON files (e.g., service_k8s_v1.35.1.json)
if software_name == "service_k8s" and software_version:
json_filename = f"{software_name}_v{software_version}.json"
else:
json_filename = f"{software_name}.json"

json_path = os.path.join(base_path,
f'{SOFTWARE_CONFIG_SUBDIR}/{arch}/{cluster_os_type}/{cluster_os_version}/{software_name}.json'
f'{SOFTWARE_CONFIG_SUBDIR}/{arch}/{cluster_os_type}/{cluster_os_version}/{json_filename}'
)
return json_path

Expand Down
28 changes: 24 additions & 4 deletions common/library/modules/image_package_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,12 @@ def process_functional_group(fg_name, arch, os_version, input_project_dir,
packages = []

for json_file in json_files:
# Extract software name from json file
# Handle versioned files like service_k8s_v1.35.1.json -> service_k8s
sw_name = json_file.replace(".json", "")
# Remove version suffix for versioned files (e.g., service_k8s_v1.35.1 -> service_k8s)
if sw_name.startswith("service_k8s_v"):
sw_name = "service_k8s"
if sw_name not in allowed_softwares:
continue

Expand All @@ -170,7 +175,8 @@ def process_functional_group(fg_name, arch, os_version, input_project_dir,
sw_data, fg_name=fg_name, slurm_defined=True
)
)
elif json_file == "service_k8s.json":
elif json_file.startswith("service_k8s_v"):
# Handle versioned service_k8s_v<version>.json files
packages.extend(
collect_packages_from_json(
sw_data, fg_name=fg_name, service_k8s_defined=True
Expand All @@ -194,6 +200,7 @@ def run_module():
software_config_file=dict(type="str", required=True),
input_project_dir=dict(type="str", required=True),
additional_json_path=dict(type="str", required=False, default=""),
service_k8s_version=dict(type="str", required=False, default=""),
)

result = dict(
Expand All @@ -212,6 +219,7 @@ def run_module():
software_config_file = module.params["software_config_file"]
input_project_dir = module.params["input_project_dir"]
additional_json_path = module.params["additional_json_path"]
service_k8s_version = module.params["service_k8s_version"]

software_config = load_json_file(software_config_file, module)
if not software_config:
Expand All @@ -221,6 +229,13 @@ def run_module():
if not os_version:
module.fail_json(msg="cluster_os_version not found in software_config.json")

# Extract service_k8s version from software_config if not provided
if not service_k8s_version:
for sw in software_config.get("softwares", []):
if sw.get("name") == "service_k8s" and sw.get("version"):
service_k8s_version = sw["version"]
break

allowed_softwares = {
sw["name"] for sw in software_config.get("softwares", [])
}
Expand All @@ -229,14 +244,19 @@ def run_module():
additional_enabled = is_additional_packages_enabled(software_config)
allowed_additional_subgroups = get_allowed_additional_subgroups(software_config) if additional_enabled else []

# Versioned JSON file for service_k8s: service_k8s_v<version>.json
if not service_k8s_version:
module.fail_json(msg="service_k8s version not found in software_config.json")
service_k8s_json = f"service_k8s_v{service_k8s_version}.json"

# pylint: disable=line-too-long
# Functional group → json files mapping
software_map = {
"os_x86_64": ["default_packages.json", "ldms.json"],
"os_aarch64": ["default_packages.json", "ldms.json"],
"service_kube_node_x86_64": ["service_k8s.json"],
"service_kube_control_plane_first_x86_64": ["service_k8s.json"],
"service_kube_control_plane_x86_64": ["service_k8s.json"],
"service_kube_node_x86_64": [service_k8s_json],
"service_kube_control_plane_first_x86_64": [service_k8s_json],
"service_kube_control_plane_x86_64": [service_k8s_json],
"slurm_control_node_x86_64": ["slurm_custom.json", "openldap.json", "ldms.json"],
"slurm_node_x86_64": ["slurm_custom.json", "openldap.json", "ldms.json"],
"login_node_x86_64": ["slurm_custom.json", "openldap.json", "ldms.json"],
Expand Down
9 changes: 8 additions & 1 deletion common/library/modules/prepare_tasklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,15 @@ def main():
logger.info("Preparing package lists...")
for software in software_list[arch]:
logger.info(f"Processing software: {software}")
# Get software version for versioned JSON files (e.g., service_k8s_v1.35.1.json)
software_version = None
for sw in user_data.get("softwares", []):
if sw.get("name") == software and sw.get("version"):
software_version = sw["version"]
break
json_path[arch] = get_json_file_path(software, cluster_os_type,
cluster_os_version, user_json_file, arch)
cluster_os_version, user_json_file, arch,
software_version=software_version)
status_csv_path[arch] = get_csv_file_path(software, log_dir, arch)
logger.info(f"json_path: {json_path}")
logger.info(f"status_csv_path: {status_csv_path}")
Expand Down
2 changes: 1 addition & 1 deletion examples/rhel_software_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{"name": "default_packages", "arch": ["x86_64","aarch64"]},
{"name": "admin_debug_packages", "arch": ["x86_64","aarch64"]},
{"name": "openldap", "arch": ["x86_64","aarch64"]},
{"name": "service_k8s","version": "1.34.1", "arch": ["x86_64"]},
{"name": "service_k8s","version": "1.35.1", "arch": ["x86_64"]},
{"name": "slurm_custom", "arch": ["x86_64","aarch64"]},
{"name": "ucx", "version": "1.19.0", "arch": ["x86_64","aarch64"]},
{"name": "openmpi", "version": "5.0.8", "arch": ["x86_64","aarch64"]},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{"name": "admin_debug_packages", "arch": ["x86_64","aarch64"]},
{"name": "openldap", "arch": ["x86_64","aarch64"]},
{"name": "slurm_custom", "arch": ["x86_64","aarch64"]},
{"name": "service_k8s", "version": "1.34.1", "arch": ["x86_64"]},
{"name": "service_k8s", "version": "1.35.1", "arch": ["x86_64"]},
{"name": "ucx", "version": "1.19.0", "arch": ["x86_64","aarch64"]},
{"name": "openmpi", "version": "5.0.8", "arch": ["x86_64","aarch64"]},
{"name": "csi_driver_powerscale", "version":"v2.15.0", "arch": ["x86_64"]},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{"name": "admin_debug_packages", "arch": ["x86_64"]},
{"name": "openldap", "arch": ["x86_64"]},
{"name": "slurm_custom", "arch": ["x86_64"]},
{"name": "service_k8s", "version": "1.34.1", "arch": ["x86_64"]},
{"name": "service_k8s", "version": "1.35.1", "arch": ["x86_64"]},
{"name": "ucx", "version": "1.19.0", "arch": ["x86_64"]},
{"name": "openmpi", "version": "5.0.8", "arch": ["x86_64"]},
{"name": "csi_driver_powerscale", "version":"v2.15.0", "arch": ["x86_64"]},
Expand Down
Loading
Loading