PulegiumPages

Network Bonding Configuration With Foreman

This post is mostly a note to self. The snippets are kindly provided by user schewara who posted them to #theforeman IRC channel. Many thanks for sharing these snippets, they have saved me a lot of time, and hopefully will be usefull for other people as well!

What I was trying to achieve is multi-NIC with bonding configuration in Foreman/Puppet.

One way is to leave this configuration entirelly to Puppet. Another way is to configure the interfaces at the kicstart phase, which is a safer option.

So the snippets are below, verbatim, I will update the post if/when I change them.

Foreman kicstart bit (post install section):

..
..
SYSTEM_INFO=$(dmidecode -s system-manufacturer)
if [[ "$SYSTEM_INFO" == "HP" ]]; then
  # remove ifcfg Files and udev rules if exist
  rm -f /etc/sysconfig/network-scripts/ifcfg-eth*
  rm -f /etc/udev/rules.d/70-persistent-net.rules
  
  wget -P /etc/yum.repos.d -nc -nd -q http://yum.repo.somewhere/repofiles/3rdParty-hp-spp.repo
  yum install -y -e 0 hpsmh hp-smh-templates hp-health hp-snmp-agents hpdiags hponcfg hpacucli cpqacuxe glibc.i686
  
  modprobe hpilo
  
  # Generate ILO 
  cat > /tmp/ilo-parser.py << EOF
<%= snippet("ILO-IFace-Parser") %>
EOF
  
  <%= @osver <= 5 ? "python26" : "python" -%> /tmp/ilo-parser.py
  
  # CLEAR the IML Log
  hpasmcli -s "clear iml"
  
elif [[ "$SYSTEM_INFO" =~ "VMware" ]]; then
..
..

Snippet that handles bonding configuration (“ILO-IFacw-Parser”):

#!/usr/bin/env python

# WARNING: Doesn't work with python3

from xml.etree.ElementTree import parse
import sys
import re
import subprocess
import glob

def parse_ilo_interfaces(filename):
#===============================================================================
# Interfaces XML Snipped
# ...
# <SMBIOS_RECORD B64_DATA="0RQA0QADACJkmKx6AAUAImSYrHgAAA==" TYPE="209">
#  <FIELD NAME="Subject" VALUE="Embedded NIC MAC Assignment" />
#  <FIELD NAME="Port" VALUE="1" />
#  <FIELD NAME="MAC" VALUE="00-22-64-98-AC-7A" />
#  <FIELD NAME="Port" VALUE="2" />
#  <FIELD NAME="MAC" VALUE="00-22-64-98-AC-78" />
#  <FIELD NAME="Port" VALUE="iLO" />
#  <FIELD NAME="MAC" VALUE="00-22-64-97-87-8A" />
# </SMBIOS_RECORD>
# ...
#===============================================================================
    ifaces = {}
    port = ""
    
    tree = parse(filename)
    for arecord in tree.findall("SMBIOS_RECORD"):
        if arecord.attrib["TYPE"] == "209":
            childs = arecord.getchildren()
    
    
    for ch in childs:
        if ch.attrib["NAME"] == "Port":
            port = ch.attrib["VALUE"]
        if ch.attrib["NAME"] == "MAC":
            ifaces[port] = ch.attrib["VALUE"]
    
    # remove iLo entry from the dict
    del ifaces["iLO"]
    
    #print(ifaces)
    
    # fix index number and mac formatting
    macp = re.compile('-')
    for id, mac in ifaces.items():
        ifaces[int(id) - 1] = macp.sub(':', mac).upper()
        del ifaces[id]
        
    #print(ifaces)
    #print
    
    return ifaces

def gen_config_params(type, ifacenr="0", ifacemac="", ip="", nm="", gw=""):
    # TODO REFACTOR
    
    if type == "eth":
        cfg_settings = {
                    "DEVICE": type + ifacenr,
                    "HWADDR": ifacemac,
                    "ONBOOT": "yes",
                    "BOOTPROTO": "none",
                    "USERCTL": "no",
                    "NM_CONTROLLED": "no",
                    "SLAVE": "yes",
                    "MASTER": "bond0"
        }
    elif type == "bond":
        cfg_settings = {
                    "DEVICE": type + ifacenr,
                    "ONBOOT": "yes",
                    "BOOTPROTO": "none",
                    "USERCTL": "no",
                    "NM_CONTROLLED": "no",
                    "BONDING_OPTS": "miimon=100 mode=1",
                    "IPADDR": ip,
                    "NETMASK": nm,
                    "GATEWAY": gw
        }
    return cfg_settings

def write_cfgfile(path, ifacelist):
    # Write the file new
    for ifaces in ifacelist:
        print("writing: ifcfg-" + ifaces["DEVICE"])
        
        with open(path + "ifcfg-" + ifaces["DEVICE"], 'w') as f:
            for key, value in ifaces.items():
                f.write(key + '="' + value + '"\n')

def get_ilo_hostinfo(ilooutfile):
    xmlrequest="""<RIBCL version="2.21">
    <LOGIN USER_LOGIN="adminname" PASSWORD="password">
    <SERVER_INFO MODE="READ" >
    <GET_HOST_DATA />
    </SERVER_INFO>
    </LOGIN>
    </RIBCL>"""
    
    subprocess.Popen(["hponcfg", "-i", "-l", ilooutfile], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate(xmlrequest)

def parse_udev_interfaces():
    sys_files = glob.glob('/sys/class/net/eth*/address')
    
    maclist = []
    
    for infile in sys_files:
        with open(infile, 'r') as f:
            maclist.append(f.read().rstrip().upper())
    
    for mac in sorted(maclist):
        if mac not in ifacedict.values():
            ifacedict[len(ifacedict)] = mac

if __name__ == '__main__':
    # TODO: call hponcfg and generate XML-File or parse XML Output directly
    path = "/etc/sysconfig/network-scripts/"
    ilooutfile = "/tmp/iloout.xml"
    
    ifacedict = {}
    ifacelist = []
    
    # supplied by Foreman
    ipaddr = '<%= @host.ip -%>'
    netmask = '<%= @host.params["netmask"] -%>'
    gateway = '<%= @host.params["gateway"] -%>'
    
    # testing
    #ipaddr = '192.168.0.10'
    #netmask = '255.255.255.0'
    #gateway = '192.168.0.254'

    get_ilo_hostinfo(ilooutfile)
    ifacedict = parse_ilo_interfaces(ilooutfile)
    
    parse_udev_interfaces()
    
    # Bonding Interface
    cfg_params = gen_config_params("bond", "0", ip=ipaddr, nm=netmask, gw=gateway)
    ifacelist.append(cfg_params)
    
    # Ethernet Interfaces
    for nr, mac in ifacedict.items():
        cfg_params = gen_config_params("eth", str(nr), mac)
        if len(ifacedict) == 4 and nr % 2:
            del cfg_params["SLAVE"]
            del cfg_params["MASTER"]
            cfg_params["ONBOOT"] = "no"
        ifacelist.append(cfg_params)
        
    write_cfgfile(path, ifacelist)

As a side note, the HP SPP packages are available on HP site.