Ironic

Installation
https://docs.openstack.org/bifrost/latest/install/index.html

Documentation
https://docs.openstack.org/bifrost/latest/user/howto.html

Ironic Ansible module
https://docs.openstack.org/ironic/latest/admin/drivers/ansible.html
https://docs.openstack.org/ironic/latest/configuration/config.html#ansible

Prepare: clanup node

# login to node
ssh -i /etc/ironic/id_rsa ${NODE_IP}
 
# cleanup disk
umount /dev/mapper/system-* /dev/disk/by-label/ESP
vgchange -an
mdadm --stop --scan
wipefs -af /dev/nvme*

Ironic manual deployment

ssh ironic.dev.i.example.com
 
HOST_NIC_IP=10.11.12.13
HOST_NAME=ctl1-dev
HOST_NIC_MAC=00:11:22:33:44:55
IMAGE=img-noble-minimal.tgz
 
source /opt/stack/bifrost/bin/activate
export OS_CLOUD=bifrost
 
env ANSIBLE_CONFIG=/opt/openstack-ironic/playbooks/ansible.cfg \
ansible-playbook /opt/openstack-ironic/playbooks/deploy.yaml \
-i /opt/openstack-ironic/playbooks/inventory \
--private-key=/etc/ironic/id_rsa \
-e "{
   'ironic':{
      'nodes':[
         {
            'ip':'${HOST_NIC_IP}',
            'extra':{
               'hostname':'${HOST_NAME}',
               'bootif':'${HOST_NIC_MAC}'
            },
            'user':'root',
            'name':'ironic-manual-prov'
         }
      ],
      'image':{
         'source':'http://ironic.dev.i.example.com:8080/${IMAGE}',
         'url':'http://ironic.dev.i.example.com:8080/${IMAGE}',
         'type':'whole-disk-image',
         'mem_req':512,
         'validate_certs':'yes'
      }
   }
}" \
-e "{'pre_deploy_wait_minutes':0}" \
-vvv

Debug

source /opt/stack/bifrost/env-vars
ironic node-show ${NODE}
 
Asynchronous exception for node 00000000-0000-0000-0000-AC1F6B0EF226:
Node failed to deploy. Exception: Failed to deploy instance: Unexpected error while running command. Command: env
ANSIBLE_CONFIG=/usr/local/lib/python2.7/dist-packages/ironic_staging_drivers/ansible/playbooks/ansible.cfg
ansible-playbook
    /opt/openstack-prepare-baremetal/ironic-staging-driver-ansible/playbooks/deploy_node.yaml
    -i /usr/local/lib/python2.7/dist-packages/ironic_staging_drivers/ansible/playbooks/inventory -e {
"ironic": {"nodes": [{"ip": "10.11.12.13", "extra": {
        "prov_ip": null, "root_disk": "/dev/sda"}, "user": "ansible", "name": "00000000-0000-0000-0000-AC1F6B0EF226"}], "image": {"mem_req": 407, "validate_certs": "yes", "url": "http://10.33.33.40:8080/httpboot/img-bionic-minimal.tgz", "checksum": "md5:a50dc06157e4c7dd773389e4717323d5", "disk_format": "tgz", "source": "http://10.33.33.40:8080/httpboot/img-bionic-minimal.tgz", "type": "whole-disk-image"}, "configdrive": {"type": "url", "location": "http://10.11.12.40:8080/configdrive-00000000-0000-0000-0000-AC1F6B0EF226.iso.gz"}}} --skip-tags=wait
 
# cat /usr/local/lib/python2.7/dist-packages/ironic_staging_drivers/ansible/playbooks/ansible.cfg
[ssh_connection]
retries=1000

Log

/var/log/ansible.log

Delete node

ironic node-set-provision-state ${NODE} deleted
ironic node-set-maintenance ${NODE} on
ironic node-delete ${NODE}

States diagram
https://docs.openstack.org/ironic/latest/_images/states.svg

Rebuild Bifrost PXE boot image

# gzip compressed data
zcat ironic-python-agent.initramfs | cpio -idmv
 
# xz
xzcat ansible_ubuntu.initramfs | cpio -idmv
 
# apply changes
cat <<EOF> ./etc/netplan/dhcp-all-nic.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    all:
      match:
        macaddress: *
      dhcp4: yes
EOF
 
cp /usr/local/sbin/dhcp-all-interfaces.sh{,.org}
cat <<EOF> ./usr/local/sbin/dhcp-all-interfaces.sh
#!/bin/bash
 
BOOTIF=$(cat /proc/cmdline | sed -e 's/^.*BOOTIF=//' -e 's/ .*$//')
sed -i "s/macaddress: \*/macaddress: ${BOOTIF}/g" /etc/netplan/dhcp-all-nic.yaml
netplan apply
 
/bin/ping -W 10 -c 10 8.8.8.8
exit 0
EOF
 
mv /httpboot/ansible_ubuntu.initramfs /httpboot/ansible_ubuntu.initramfs.$(date -I)
#find ./ | cpio -H newc -o | xz -9 --format=lzma >  /httpboot/ansible_ubuntu.initramfs
find ./ | cpio -H newc -o | xz -9 --format=lzma >  /tmp/ironic-python-agent.initramfs
 
# gzip
find ./ | cpio -H newc -o | gzip --best >   /httpboot/ansible_ubuntu.initramfs
 
# parallel compress test (broken)
# apt-get install pxz
# find ./ | cpio -H newc -o | pxz -9 --format=lzma >  /httpboot/ansible_ubuntu.initramfs

Rebuild / Change image

cd /tmp/img-bionic-minimal
tar czf /httpboot/img-bionic-minimal.tgz .

Undeploy node

NODE_UUID=$(openstack baremetal node list -c UUID -f value)
openstack baremetal node undeploy ${NODE_UUID}
openstack baremetal node maintenance set ${NODE_UUID}
openstack baremetal node delete ${NODE_UUID}
 
# list available nodes
openstack baremetal node list

Power on all nodes

openstack baremetal node list -c UUID -f value | xargs -L1 openstack baremetal node power on

Ironic Ansible deployment
https://docs.openstack.org/ironic/queens/admin/drivers/ansible.html
https://docs.openstack.org/ironic/queens/configuration/config.html#ansible
https://docs.openstack.org/ironic/ussuri/admin/interfaces/deploy.html

Ironic Virtual media (ISO) deployment
https://docs.openstack.org/ironic/latest/admin/ramdisk-boot.html

Links
https://www.mirantis.com/blog/automate-bare-metal-server-provisioning-using-ironic-bifrost-and-the-ansible-deploy-driver/
http://dtantsur.github.io/talks/fosdem2016/
https://docs.openstack.org/bifrost/latest/install/index.html
https://specs.openstack.org/openstack/tripleo-specs/specs/pike/tripleo-routed-networks-ironic-inspector.html
https://docs.openstack.org/ironic/latest/user/index.html