tools: add Dell iDRAC XML generator

This commit is contained in:
Greg Sutcliffe 2025-05-21 16:05:55 +01:00 committed by kevin
parent 3553166073
commit e74727c5dd
5 changed files with 4144 additions and 0 deletions

View file

@ -0,0 +1,48 @@
This tool generates XML files to be uploaded to Dell iDRAC consoles
# Requirements
- `pandas` for the DHCP parser
- `racadm` to talk to the Dells
# Usage
1. Put a list of IPs in `hostnames.csv` - 2 IPs are given as an example
2. Run `dhcp_parser.py`
1. This will reference the IPs and lookup the hostnames that match
2. The output is written back to hostnames.csv
3. (optional) Export a known-good XML from a DRAC host, and place it in drac-prod.xml.base
1. **This is optional, the repo contains our current XML with the edits already done**
2. e.g. `sudo racadm -r <ip> -u admin -p "supersecret" get -f ./drac-prod.xml.base -t xml --clone`
3. Edit NIC.1#DNSRacName and replace the hostname with PLACEHOLDER, like this:
- `<Attribute Name="NIC.1#DNSRacName">PLACEHOLDER</Attribute>`
4. Edit the Users and comment out the passwords for the relevant users
- `<!-- <Attribute Name="Users.2#Password"></Attribute> -->`
4. Edit `xml_writer` to set the passwords used for the prod and stg environments (lines 47, 50, 56 & 59)
4. Run `python xml_writer.py`
# Output
This will generate 2 directories and 4 scripts.
The directories are called `prod` and `stg` and contain the customised XML for
each target host in the IP list.
The files are `prod.sh`, `stg.sh`, `prod_test.sh` and `stg_test.sh`. Run the
test scripts first - these simply use `racadm` to test the user/password works
for each host. `racadm` can be noisy, so run it like this:
```
bash ./stg_test.sh 2>&1 |grep -E "(10.16|ERROR)"
```
Obviously replace the 10.16 with the real IPs - you want to match all the
hosts but not the random text `racadm` spews out
If any fail, check the access manually, and re-run until then work.
Then run the main script, eg `bash stg.sh` which will apply the custom XML from
the directory to the each host in series.
You can run the test script another time after to check access still works

View file

@ -0,0 +1,52 @@
#!/usr/bin/env python
import re
import pandas as pd
# File paths
ips_csv = './hostnames.csv'
dhcp_data = '/srv/web/infra/ansible/roles/dhcp_server/files/dhcpd.conf.noc01.iad2.fedoraproject.org'
# Read the CSV
ips = pd.read_csv(ips_csv)
# Read the DHCP config file into a list of lines
with open(dhcp_data, 'r', encoding='utf-8') as f:
dhcp_lines = f.readlines()
def get_name(ip):
# Search for the line containing the IP address
for idx, line in enumerate(dhcp_lines):
if ip in line:
# Get the next line (like grep -A1)
if idx + 1 < len(dhcp_lines):
next_line = dhcp_lines[idx + 1]
# Extract the hostname using the regex
match = re.search(r'"(.*)\.mgmt', next_line)
if match:
return match.group(1)
break
return None # Return None if not found
def get_stg(ip):
# Search for the line containing the IP address
for idx, line in enumerate(dhcp_lines):
if ip in line:
# Get the next line (like grep -A1)
if idx + 1 < len(dhcp_lines):
next_line = dhcp_lines[idx + 1]
# Extract the hostname using the regex
match = re.search(r'stg', next_line)
if match:
return True
else:
return False
break
return None # Return None if not found
# Apply the function to each IP
ips['name'] = ips['ip'].apply(get_name)
ips['stg'] = ips['ip'].apply(get_stg)
# Write the updated DataFrame back to CSV
ips.to_csv('./hostnames.csv', index=False)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
ip,name,stg
10.16.160.14
10.16.160.33
1 ip,name,stg
2 10.16.160.14
3 10.16.160.33

View file

@ -0,0 +1,61 @@
#!/usr/bin/env python
import os
import csv
# Read the CSV file
with open('./hostnames.csv', newline='') as csvfile:
reader = csv.DictReader(csvfile)
servers = list(reader)
stg_script = "stg.sh"
if os.path.exists(stg_script):
os.remove(stg_script)
stg_test_script = "stg_test.sh"
if os.path.exists(stg_test_script):
os.remove(stg_test_script)
prod_script = "prod.sh"
if os.path.exists(prod_script):
os.remove(prod_script)
prod_test_script = "prod_test.sh"
if os.path.exists(prod_test_script):
os.remove(prod_test_script)
user = 'root'
# Read the template XML as a string
with open('./drac-prod.xml.base', 'r', encoding='utf-8') as f:
template = f.read()
for server in servers:
ip = server['ip']
name = server['name']
stg = server['stg']
# Replace the placeholder with the actual name
xml_content = template.replace(
'<Attribute Name="NIC.1#DNSRacName">PLACEHOLDER</Attribute>',
f'<Attribute Name="NIC.1#DNSRacName">{name}</Attribute>'
)
# Write to a new XML file named after the IP
if stg == "True":
filename = f'stg/{ip}.xml'
with open(stg_script, 'a', encoding='utf-8') as f:
f.write(f"sudo racadm -r {ip} -u {user} -p 'stg-password' set -f ./stg/{ip}.xml -t xml -s Off\n")
with open(stg_test_script, 'a', encoding='utf-8') as f:
f.write(f"echo {ip}\n")
f.write(f"sudo racadm -r {ip} -u {user} -p 'stg-password' getsvctag\n")
with open(filename, 'w', encoding='utf-8') as f:
f.write(xml_content)
else:
filename = f'prod/{ip}.xml'
with open(prod_script, 'a', encoding='utf-8') as f:
f.write(f"sudo racadm -r {ip} -u {user} -p 'prod-password' set -f ./prod/{ip}.xml -t xml -s Off\n")
with open(prod_test_script, 'a', encoding='utf-8') as f:
f.write(f"echo {ip}\n")
f.write(f"sudo racadm -r {ip} -u {user} -p 'prod-password' getsvctag\n")
with open(filename, 'w', encoding='utf-8') as f:
f.write(xml_content)