Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@
"not a directory.")
PXE_MAPPING_FILE_EXT_FAIL_MSG = ("File path is invalid. Please ensure that the file ends with "
".csv extension")
PXE_MAPPING_AARCH64_LOCAL_PATH_MSG = ("aarch64 nodes are present in pxe_mapping_file.csv but "
"local share path selected for omnia core container deployment. "
"aarch64 nodes require NFS share path. "
"Please redeploy omnia core container with NFS share path option or remove aarch64 nodes "
"from pxe_mapping_file.csv.")
CLUSTER_OS_FAIL_MSG = "Cluster OS must be 'rhel' for RHEL Omnia Infrastructure Manager"

# local_repo.yml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,63 @@ def validate_admin_ips_against_network_spec(pxe_mapping_file_path, network_spec_

return errors

def validate_aarch64_local_path_compatibility(pxe_mapping_file_path, input_file_path, omnia_base_dir, project_name):
"""
Validates that aarch64 nodes are not present when using local share path.

Args:
pxe_mapping_file_path (str): Path to the PXE mapping file.
input_file_path (str): Path to the input file.
omnia_base_dir (str): Base directory of Omnia.
project_name (str): Name of the project.

Raises:
ValueError: If aarch64 nodes are found with local share path configuration.
"""
# Check metadata file for omnia_Share_option
metadata_path = "/opt/omnia/.data/oim_metadata.yml"

# Default to Local if metadata doesn't exist or omnia_Share_option is not set
share_option = "Local"

if os.path.isfile(metadata_path):
try:
with open(metadata_path, "r", encoding="utf-8") as f:
metadata = yaml.safe_load(f) or {}

# Check omnia_share_option in metadata
share_option = metadata.get("omnia_share_option", "Local")
except Exception:
# If there's an error reading metadata, assume Local
pass

# If share option is NFS, no need to check further
if share_option.lower() == "nfs":
return

# Check for aarch64 nodes in PXE mapping file
with open(pxe_mapping_file_path, "r", encoding="utf-8") as fh:
raw_lines = fh.readlines()

non_comment_lines = [ln for ln in raw_lines if ln.strip()]
reader = csv.DictReader(non_comment_lines)

fieldname_map = {fn.strip().upper(): fn for fn in reader.fieldnames}
fg_col = fieldname_map.get("FUNCTIONAL_GROUP_NAME")

if not fg_col:
return

aarch64_found = False
for row in reader:
fg_name = row.get(fg_col, "").strip() if row.get(fg_col) else ""
if fg_name and "aarch64" in fg_name.lower():
aarch64_found = True
break

if aarch64_found:
raise ValueError(en_us_validation_msg.PXE_MAPPING_AARCH64_LOCAL_PATH_MSG)

def validate_provision_config(
input_file_path, data, logger, module, omnia_base_dir, module_utils_base, project_name
):
Expand Down Expand Up @@ -811,6 +868,7 @@ def validate_provision_config(
validate_functional_groups_separation(pxe_mapping_file_path)
validate_parent_service_tag_hierarchy(pxe_mapping_file_path)
validate_slurm_login_compiler_prefix(pxe_mapping_file_path)
validate_aarch64_local_path_compatibility(pxe_mapping_file_path, input_file_path, omnia_base_dir, project_name)

# Validate ADMIN_IPs against network_spec.yml ranges
network_spec_path = create_file_path(input_file_path, file_names["network_spec"])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2026 Dell Inc. or its subsidiaries. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
---

- name: Create provision directory if it doesn't exist
ansible.builtin.file:
path: "{{ provision_dir_path }}"
state: directory
mode: "{{ provision_dir_mode }}"

- name: Read PXE mapping file
ansible.builtin.read_csv:
path: "{{ pxe_mapping_file_path }}"
register: pxe_mapping_data

- name: Extract BMC IPs from PXE mapping file
ansible.builtin.set_fact:
bmc_ip_list: "{{ pxe_mapping_data.list | map(attribute='BMC_IP') | list }}"

- name: Generate BMC inventory file
ansible.builtin.template:
src: bmc_inventory.j2
dest: "{{ bmc_inventory_path }}"
mode: "{{ bmc_inventory_file_mode }}"
when: bmc_ip_list | length > 0

- name: Display BMC inventory generation status
ansible.builtin.debug:
msg: "{{ bmc_inventory_generated_msg }}"
when: bmc_ip_list | length > 0
4 changes: 4 additions & 0 deletions discovery/roles/discovery_validations/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@
when:
- idrac_telemetry_support | lower == 'true' | default('false') or
ldms_support | default('false')

- name: Generate BMC inventory from PXE mapping file
ansible.builtin.include_tasks: generate_bmc_inventory.yml
when: mapping_file_status
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# BMC Inventory File
# Auto-generated from PXE mapping file
# Path: {{ pxe_mapping_file_path }}
# Generated: {{ ansible_date_time.iso8601 }}

[bmc]
{% for bmc_ip in bmc_ip_list %}
{{ bmc_ip }}
{% endfor %}
8 changes: 8 additions & 0 deletions discovery/roles/discovery_validations/vars/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,11 @@ openldap_container_missing_msg: "{{ '\x1b[31m' }}\
For more information, refer to the Omnia documentation on OpenLDAP configuration.\n\
==================================================================================\
{{ '\x1b[0m' }}"

# Usage: generate_bmc_inventory.yml
pxe_mapping_file_path: "{{ hostvars['localhost']['input_project_dir'] }}/pxe_mapping_file.csv"
bmc_inventory_path: "/opt/omnia/provision/bmc_inventory"
provision_dir_path: "/opt/omnia/provision"
provision_dir_mode: "0755"
bmc_inventory_file_mode: "0644"
bmc_inventory_generated_msg: "BMC inventory generated at {{ bmc_inventory_path }} with {{ bmc_ip_list | length }} BMC IP(s)"
43 changes: 43 additions & 0 deletions utils/roles/idrac_pxe_boot/tasks/pre_checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright 2026 Dell Inc. or its subsidiaries. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
---

- name: Check if custom inventory was provided
ansible.builtin.set_fact:
custom_inventory_provided: "{{ groups['bmc'] is defined and groups['bmc'] | length | int > 0 }}"

- name: Use auto-generated BMC inventory if no custom inventory provided
when: not custom_inventory_provided
block:
- name: Check if auto-generated BMC inventory exists
ansible.builtin.stat:
path: "{{ bmc_inventory_path }}"
register: bmc_inventory_stat

- name: Fail if no BMC inventory available
ansible.builtin.fail:
msg: "{{ bmc_inventory_not_found_msg }}"
when: not bmc_inventory_stat.stat.exists

- name: Add auto-generated BMC inventory to in-memory inventory
ansible.builtin.add_host:
name: "{{ item }}"
groups: bmc
loop: "{{ lookup('file', bmc_inventory_path).split('\n') | select('match', '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$') | list }}"
when: bmc_inventory_stat.stat.exists

- name: Validate BMC group has hosts
ansible.builtin.fail:
msg: "{{ bmc_no_hosts_msg }}"
when: groups['bmc'] is not defined or groups['bmc'] | length | int == 0
13 changes: 13 additions & 0 deletions utils/roles/idrac_pxe_boot/vars/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,16 @@ provision_os_msg: "OS provisioning is initiated. Wait for installation to comple
pxe_provisioning_fail_msg: "OS booting using PXE failed. This could be due to outdated NIC firmware. Re-run set_pxe_boot.yml after fixing the issue"
bmc_validation_fail_msg: "Failed. bmc group in inventory must have atleast one bmc ip."
unreachable_idrac_msg: "iDRAC is unreachable. pxe boot might be set. Please check the host reboot status manually"

# Usage: pre_checks.yml
bmc_inventory_path: "/opt/omnia/provision/bmc_inventory"
bmc_inventory_not_found_msg: |
Failed. No BMC inventory found.

Please either:
1. Provide a custom inventory file with 'bmc' group:
ansible-playbook set_pxe_boot.yml -i <your_inventory_file>

2. Or run discovery.yml first to auto-generate BMC inventory from PXE mapping file at:
{{ bmc_inventory_path }}
bmc_no_hosts_msg: "Failed. 'bmc' group has no hosts. Please check your inventory file or PXE mapping file."
12 changes: 6 additions & 6 deletions utils/set_pxe_boot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@
# boot image must be reachable by the target nodes once the iDRAC
# is set to PXE mode.
# -------------------------------------------------------------------------
- name: Validate BMC group exists in inventory

- name: Validate and setup BMC inventory
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Check if bmc group exists in inventory
ansible.builtin.fail:
msg: "Failed. 'bmc' group not found in inventory or has no hosts.
Please ensure the inventory file contains a 'bmc' group with at least one BMC IP address."
when: groups['bmc'] is not defined or groups['bmc'] | length | int == 0
- name: Include pre-checks for BMC inventory validation
ansible.builtin.include_role:
name: idrac_pxe_boot
tasks_from: pre_checks

- name: Set_fact for fetch omnia config credentials
hosts: localhost
Expand Down
Loading