diff --git a/Dockerfile.loader b/Dockerfile.loader index a616643..1e163bf 100644 --- a/Dockerfile.loader +++ b/Dockerfile.loader @@ -1,23 +1,26 @@ # syntax=docker/dockerfile:1.4 FROM cgr.dev/chainguard/python:latest-dev as builder +user root + WORKDIR /app COPY requirements.txt . -RUN pip install -r requirements.txt --user +RUN pip install -r requirements.txt FROM cgr.dev/chainguard/python:latest +user root + WORKDIR /app # Make sure you update Python version in path -COPY --from=builder /home/nonroot/.local/lib/python3.12/site-packages /home/nonroot/.local/lib/python3.12/site-packages - -COPY dnsmasq_updater.py . +COPY --from=builder /usr/lib/python3.12/site-packages /usr/lib/python3.12/site-packages # Copy the rest of the application code COPY dnsmasq_updater.py . + # Set the command to run your Python application -ENTRYPOINT ["python", "/app/dnsmasq_updater.py"] \ No newline at end of file +ENTRYPOINT ["python", "/app/dnsmasq_updater.py"] diff --git a/Dockerfile.updater b/Dockerfile.updater deleted file mode 100644 index a616643..0000000 --- a/Dockerfile.updater +++ /dev/null @@ -1,23 +0,0 @@ -# syntax=docker/dockerfile:1.4 -FROM cgr.dev/chainguard/python:latest-dev as builder - -WORKDIR /app - -COPY requirements.txt . - -RUN pip install -r requirements.txt --user - -FROM cgr.dev/chainguard/python:latest - -WORKDIR /app - -# Make sure you update Python version in path -COPY --from=builder /home/nonroot/.local/lib/python3.12/site-packages /home/nonroot/.local/lib/python3.12/site-packages - -COPY dnsmasq_updater.py . - -# Copy the rest of the application code -COPY dnsmasq_updater.py . - -# Set the command to run your Python application -ENTRYPOINT ["python", "/app/dnsmasq_updater.py"] \ No newline at end of file diff --git a/dnsmasq_updater.py b/dnsmasq_updater.py index a55181e..021595b 100644 --- a/dnsmasq_updater.py +++ b/dnsmasq_updater.py @@ -10,6 +10,7 @@ import sys import argparse import logging +from pathlib import Path # This script needs an auth token and must be able to renew that token on use. @@ -41,40 +42,42 @@ def getNID(c_data, xname): else: return None -def template_file(base_url, access_token, hostsfilename, optsfilename): +def template_file(base_url, bootscript_base_url, access_token, hostsfilename, optsfilename): ei_data = getSMD(f'{base_url}/hsm/v2/Inventory/EthernetInterfaces',access_token) component_data = getSMD(f"{base_url}/hsm/v2/State/Components", access_token)['Components'] logging.warning(f"Retrieved {len(ei_data)} EthernetInterfaces and {len(component_data)} Components from {base_url}") - hostsfile = open(hostsfilename, "w") - #this for loop writes host entries - for i in ei_data: - if i['Type'] != 'NodeBMC': - nidname=getNID(component_data, i['ComponentID']) - if nidname: - print(f"{i['MACAddress']},set:{nidname},{i['IPAddresses'][0]['IPAddress']},{nidname}", file=hostsfile) + hostsfilepath = Path(hostsfilename) + hostsfilepath.parent.mkdir(parents=True, exist_ok=True) + with hostsfilepath.open("w") as hostsfile: + #this for loop writes host entries + for i in ei_data: + if i['Type'] != 'NodeBMC': + nidname=getNID(component_data, i['ComponentID']) + if nidname: + print(f"{i['MACAddress']},set:{nidname},{i['IPAddresses'][0]['IPAddress']},{nidname}", file=hostsfile) + else: + print(f"{i['MACAddress']},set:{i['ComponentID']},{i['IPAddresses'][0]['IPAddress']},{i['ComponentID']}", file=hostsfile) else: - print(f"{i['MACAddress']},set:{i['ComponentID']},{i['IPAddresses'][0]['IPAddress']},{i['ComponentID']}", file=hostsfile) - else: - print(f"{i['MACAddress']},{i['IPAddresses'][0]['IPAddress']},{i['ComponentID']}", file=hostsfile) - hostsfile.close() - logging.warning(f"Generated {hostsfilename}") + print(f"{i['MACAddress']},{i['IPAddresses'][0]['IPAddress']},{i['ComponentID']}", file=hostsfile) + logging.warning(f"Generated {hostsfilename}") #TODO actually map all the BMCs straight from redfish, instead of creating dummy endpoints for them. #rf_data = getSMD(f'http://{smd_endpoint}:27779/hsm/v2/Inventory/RedfishEndpoints') #for r in rf_data['RedfishEndpoints']: # print(r['ID'] + ' ' + r['IPAddress']) #optsfile = tempfile.TemporaryFile(mode = "r+") - optsfile = open(optsfilename, "w") - #this for loop writes option entries, we wouldn't need it if the BSS wasn't MAC specific - for i in ei_data: - if 'bmc' not in i['Description']: - nidname=getNID(component_data, i['ComponentID']) - if nidname: - print(f"tag:{nidname},tag:IPXEBOOT,option:bootfile-name,\"{base_url}/boot/v1/bootscript?mac={i['MACAddress']}\"", file=optsfile) - else: - print(f"tag:{i['ComponentID']},tag:IPXEBOOT,option:bootfile-name,\"{base_url}/boot/v1/bootscript?mac={i['MACAddress']}\"", file=optsfile) - optsfile.close() - logging.warning(f"Generated {optsfilename}") + optsfilepath = Path(optsfilename) + optsfilepath.parent.mkdir(parents=True, exist_ok=True) + with optsfilepath.open("w") as optsfile: + #this for loop writes option entries, we wouldn't need it if the BSS wasn't MAC specific + for i in ei_data: + if 'bmc' not in i['Description']: + nidname=getNID(component_data, i['ComponentID']) + if nidname: + print(f"tag:{nidname},tag:IPXEBOOT,option:bootfile-name,\"{bootscript_base_url}/boot/v1/bootscript?mac={i['MACAddress']}\"", file=optsfile) + else: + print(f"tag:{i['ComponentID']},tag:IPXEBOOT,option:bootfile-name,\"{bootscript_base_url}/boot/v1/bootscript?mac={i['MACAddress']}\"", file=optsfile) + logging.warning(f"Generated {optsfilename}") @@ -92,8 +95,10 @@ def main(): parser.add_argument('--base-url', help='Base URL for OpenCHAMI endpoint. This flag will override the OCHAMI_BASEURL environment variable.') parser.add_argument('--access-token', help='Access token for OpenCHAMI endpoint This flag will override the OCHAMI_ACCESS_TOKEN environment variable.') parser.add_argument('--hosts-file', help='Path to the hosts file', default='/configs/site/hosts/hostsfile') - parser.add_argument('--opts-file', help='Path to the options file', default='/configs/site/hosts/optsfile') - + parser.add_argument('--opts-file', help='Path to the options file', default='/configs/site/opts/optsfile') + parser.add_argument('--bootscript-base-url', help='Hostname (or ip address) to be used when requesting the bootscript') + + args = parser.parse_args() if args.base_url: @@ -109,6 +114,16 @@ def main(): else: logging.warning(f'Configuring base_url based on default value of "http://localhost"') base_url = 'http://localhost' + if os.environ.get('OCHAMI_BOOTSCRIPT_BASEURL') is not None: + logging.warning(f'Configuring bootscript_base_url based on OCHAMI_BOOTSCRIPT_BASEURL which is "{os.environ["OCHAMI_BOOTSCRIPT_BASEURL"]}"') + bootscript_base_url = os.environ['OCHAMI_BOOTSCRIPT_BASEURL'] + else: + if args.bootscript_base_url: + logging.warning(f'Configuring bootscript_base_url based on flag value of "{args.bootscript_base_url}"') + bootscript_base_url = args.bootscript_base_url + else: + logging.warning(f'Configuring bootscript_base_url based on default value of "{base_url}"') + bootscript_base_url = base_url if args.access_token: if os.environ.get('OCHAMI_ACCESS_TOKEN') is not None: @@ -128,7 +143,7 @@ def main(): # Main loop to run template_file() every minute while True: try: - template_file(base_url, access_token, args.hosts_file, args.opts_file) # Execute the subroutine + template_file(base_url, bootscript_base_url, access_token, args.hosts_file, args.opts_file) # Execute the subroutine time.sleep(60) # Wait for 60 seconds (1 minute) except KeyboardInterrupt: print("KeyboardInterrupt received, exiting...")