Edit dhcpd.leases through a foreman-hook script

Foreman does not support ESXi natively. While the legacy BIOS mode installation is simple enough using “PXELinux BIOS” as the bootloader, installation in EFI mode is much trickier. Problem is exacerbated by GRUB2 not being able to chainload the ESXi EFI bootloader mboot.efi (a renamed bootx64.efi).

For EFI mode installation, you have to pass the filename = mboot.efi directive from the dhcp server to the client. So you dit dhcpd.conf, change the loader to mboot.efi and change the PXELoader of the host to “None”. Now you have managed to install ESXi successfully using foreman. However when the client reboots, it tries to PXE boot again. Once it loads mboot.efi, it will try to reinstall the OS. There are ways to stop the actual installation from happening but you cannot perform a local boot and the host will never boot inside ESXi. This is in stark contrast to legacy BIOS mode where after installation completes and client does a PXE boot, it is presented with the default PXELinux template and the first entry is LOCALBOOT.


  1. Don’t have NIC as the first boot entry and use host specific scripts to set one time PXE boot.
  2. Through Foreman:
    So it’s evident that you cannot have dhcpd.conf hand out mboot.efi. It needs to be controlled on a per host basis through dhcpd.leases file. To have a completely automated workflow, I thought about this:

ESXi installation in EFI mode:

  • Set host’s PXELoader to None
  • Create after_build foreman-hook script which checks OS Name, PXELoader and host mac.
    • If OS_Name starts with ‘ESXi’ and PXELoader = ‘None’
    • Edit the latest entry of the host in dhcpd.leases and set filename = mboot.efi
    • Reload dhcp server
  • Next create a before_provision script with same checks as above
    • Delete the filename directive of the host from lease file.
    • Reload dhcp server
    • Turn off build mode for the host as the wget foreman-built call will fail.

The after_build script is not able to edit the dhcp lease file. Here’s the snippet:

active_lease_mac_line_no=$(grep -n $system_mac $dhcpleasefile |tail -1 |awk -F: '{print $1}')
echo "Last entry of $system_mac found at line number $active_lease_mac_line_no in dhcpd.leases"
pxe_loader_line=$(tail -n+$((active_lease_mac_line_no+2)) $dhcpleasefile | head -1)
if [[ $pxe_loader_line != *"server.filename"* ]]; then
    echo "Line $((active_lease_mac_line_no+2)) = ${pxe_loader_line}. No PXE Loader"
    sed -i $((active_lease_mac_line_no+2))i'\        supersede server.filename = "mboot.efi";' $dhcpleasefile
    echo "PXE-Loader exists"

Error message:
sed: couldn't open temporary file /var/lib/dhcp/sed6gplUv: Permission denied

Is there a better solution? If not, is there a workaround to edit the lease file or am I just sick outta luck?

Isn’t much better to add new entry to PXELoaders? It’s simply a Ruby hash in our codebase, grep “grubx64.efi” and you will find the place, add new entry “VMWare EXSi” => “whatever.efi” and restart httpd, select this option and work done.

If this works, please file PR and we will include this entry in the next Foreman release!

1 Like

HOOOLY **** ! This was exactly the kind of solution I was looking for!
Here’s what I did:

  • Edit /usr/share/foreman/app/models/concerns/pxe_loader_support.rb, added the ESXi efi bootloader to the function all_loaders_map

def all_loaders_map(precision = ‘x64’)
“None” => “”,
“PXELinux BIOS” => “pxelinux.0”,
“PXELinux UEFI” => “pxelinux.efi”,
"ESXi UEFI" => “mboot.efi”,


and restarted httpd.

  • Through the web UI, change the PXE Loader of the host to ESXi UEFI and set build mode on.
  • Create a before-provision foreman-hook script that changes the host’s PXE Loader to None (of course after a bunch of checks).

Now you have a fully working ESXi UEFI installation workflow through Foreman.

I have no words to thank you Lukas! I have been looking for a solution to this for a long time! You have saved me from a lot of manual work!

I will submit a PR for this. Also we would need this in hammer-cli as well and update the apidoc. I have tested using api v2 and it works too.


Glad you solved it! Honestly my plan was pulling out those “lists” into yaml or config so users can edit this easily, but this is not that bad. I was also thinking adding all possible loaders into the list, but this could create more confusion for users. Thus it is what it is.

Blog post incoming? :smile: https://github.com/theforeman/theforeman.org/tree/gh-pages/_posts

Oh yes. I would love to!


Just updating the thread with the relevant blog post by me.: