kickstart network by script

Latest response

I am running Satellite 5.6. I have been trying many different ways of generating a network statement through the kickstart wizard. I do not want to enter the network definition through the Advanced Options, but instead build a script that will generate the appropriate definition. As a test, I have a system with a bonded network adapter. I can get the appropriate configuration by entering the following either in the Advanced Options (without the network statement header) or by placing the network statement in the Partitioning text box:

network --device=bond0 --hostname=llrhnt00.ccci.org --ip=10.10.11.9 --gateway=10.10.11.254 --netmask=255.255.255.0 --nameserver=10.10.10.36,10.10.10.17,10.10.10.38 --bootproto=static --bondopts=mode=802.3ad,miimon=100,lacp_rate=fast --bondslaves=eth1,eth0

I have attempted to generate the network with a pre-script with an echo in a snippet in a pre-script and by writing the network statement into a /tmp/netinfo file and using %include /tmp/netinfo in the Partitioning box.

Responses

See fourth paragraph in this post...

Even though this bit is from 2006, it seems relevant. LACP 802.3ad, would use both sides of the two interfaces comprising the bonded nic. During kickstart, it would have to use one or the other of the nics and I believe with LACP properly set up on the switch, you'd need both.

If I'm wrong, I hope someone chimes in because this would be nice.

When I create bonded nics, I use a switch line from another port (a kickstart port only activated for a kickstart), build the system and then configure the bonded interface with the slave interface files.

Perhaps try this -> Perhaps use a non-LACP port and line from your switch (a kickstart port) where you can use it for the kickstart exclusively (like eth2, using ordinal numbering, this would be the third available physical interface). Then configure the others to fulfill LACP nic bonding. I place the network directives in the variables portion of the satellite's kickstart then cite the variables in the network line within the satellite's kickstart in the web gui.

Please do let us know how it goes, but I think I'll try this too sometime.

Kind Regards,
Rem

The network statement as shown works properly if encoded either in the Network field of Advanced Options or if placed inline in the Partitions section. If used, this way, 3 interfaces are created, bond0 and it's 2 components eth0 and eth1. The LACP bond apparently works as one would expect.

The problem is if I generate this exact statement via an echo in a snippet, or via writes to a temporary file which is included via a %include in the Partitions section. I've seen both methods described elsewhere but can't make them work. If I use the %include method, the resulting file is automatically copied to root's home directory. It is correct in this file. If I use the echo method, The output of the echo is listed in the log file for the snippet.

Regarding LACP, LACP must be enabled on the switch side as well. I'm not a network engineer, but the way it was described to me by one of network gurus, if it's set up in this fashion, the switch will accept data on either interface, but each interface will present the same MAC address. When the switch sees a packet on one of the interfaces, it associates the MAC address with that interface. Then when traffic flows in the opposite direction, it is sent on the interface that last used the MAC address. If the host then sends packets on the other interface, the switch simply reassociates the MAC addres with this new interface.

I would have expected that you would have to do the install without bonding and then turn on bonding, but this apparently isn't the case. When the kickstart process starts, you get an error that one of the kickstart files can't be loaded. I can't remember which. When this hapens you just click OK. Then you get a screen where you must select the install interface. I always pick the first interface, but I'd guess any one of the interfaces will work. Apparently, it doesn't matter that the bond isn't set up on the host side yet. The packet just gets sent to the switch, the switch associates the MAC address with the interface, and everything proceeds. Then when the bond definition gets applied the LACP protocols take effect.

I think your syntax is just fine, it's just that the expectation of LACP working with Anaconda may be overreaching I believe. In my experience LACP, type 4 bonding must use both ports, never just 1 port. (Did you see that link I posted?) .

I do not believe (from my experience) that LACP will function during the anaconda environment. Type 4 bonding, LACP uses both ports simultaneously and requires both ports (I suspect I'm not telling you anything new with that last bit). Anaconda does not. As soon as I attempted the kickstart with a different port (not configured for bonding), it worked fine. Good luck, I hope it can be done for your sake.

If you can get LACP to work with Anaconda, please do let us know what you did. I doubt type 4 bonding with LACP is possible during kickstart and hope I am incorrect for your sake.

You could probably do this by running your kickstart on ifcfg-eth2 (ordinal numbering) on a non-LACP NIC and then in the post script or /etc/rc3.d/S999configbonding script you write -->run this, then delete the script and disable the non-LACP interface when complete:

(scroll to the right of the block of code below)

/bin/cat << EOF > /etc/sysconfig/network-scripts/ifcfg-bond0
<all your directives>
<go here>
EOF

/bin/cat << EOF > /etc/sysconfig/network-scripts/ifcfg-eth0
<all your directives>
<go here>
EOF
/bin/cat << EOF > /etc/sysconfig/network-scripts/ifcfg-eth1
<all your directives>
<go here>
EOF

See last post, and good luck

From my little experience, apparently everything works with the host side not running LACP and the switch side running LACP. That's not as I expected. I expected Anaconda to fail altogether. I'm just saying that you have to skip through the apparent Anaconda errors and then everything will progress normally.

I realize that I could do everything with a post-script, but the various blogs seem to say that it can do network config with pre-scripts and %includes. I'd like to learn how to crack that nut.

From what I've seen with LACP, it seems both slave interfaces must be up and functioning. However you have me curious and I'll give this a try too - thanks for posting this Tim.

Re: %include /tmp/netinfo

RedHat documents indicate that this method is no longer supported as NetworkManager is used. Therefore, the /etc/sysconfig/network-scripts must be populated directly. I'll have to give this a try.

At the Red Hat Summit a week ago, they mentioned in the RHEL 7 labs that Network Manager's next iteration/replacement will be able to handle bonded nics.

RE: bonding

I did some further testing. Bonding can be configured either by using the network field on Advanced Options or by unchecking the Network box in Advanced Options and placing the entire network command in Partitioning. If you place it in Partitioning, but leave the Network box checked in Advanced Options, the bonding configuration will be ignored.

Nice, Thanks Tim,

I suspected bonding in general was supported. My question would be if Mode 4 – 802.3ad (active) link aggregation would work during anaconda kickstart because the switch would mandate both interfaces to be up and slaved to the ifcfg-bondX file... I suspect you may have touched on this above...

going to build a server today and may try this today...

Well, I'm not sure I'd say bonding works during kickstart. What I'd say is I don't see a network failure if LACP Mode 4 is defined on the switch and you try to use one of the interfaces to do an install. The reason I say it this way is what I see when I try to do the install.

As I said previously, when you do the install, Anaconda gives an error saying that it can't load something from the Satellite server. I don't remember what it is. If you then select OK, Anaconda will continue to a screen where you select which network adapter you wish to install from. The bonded adapter is not listed here, just eth0 and eth1. I select eth0 and the install then completes normally. This leads me to believe that Anaconda is only using one interface even thought the switch is set for LACP.

This kind of makes sense from what the network guru told me. He said that the way LACP Mode 4 works is that the switch just listens on all ports defined for the LACP port group. Whichever one responds, the switch associates that port with the MAC address it receives on that port. When the switch needs to send a packet for that MAC address, it just checks which port last sent data with that MAC address and sends data on that port. If the server sends data on a different port, it just reassociates the MAC address with this new port.

If this descripttion is accurate, it doesn't matter if the host is actually using a bonded interface as long as it only uses 1 interface and only sends 1 MAC address on that 1 interface. There's no confusion on the switch side because it will associate the MAC address to which ever port used to receive the last packet. The server doesn't care because it will only get packets on the one interface and he will get packets with the correct MAC address.

Technically, this kind of configuration may not be supported. It seems to work, though.

Thanks Tim, if you get this to work, please let us know what you did.

I can validate the experience you had - that you can use a single NIC which is unbonded when plumbed to an LACP switch port. I don't know when this changed, but I too recall when back-in-the-day if you had LACP configured on the switch, but not the same on the host, it would simply not work. That no longer seems to be the case. I have not had a chance to validate this during a kickstart, however. When I had researched this before, I could not find any supporting docs that included lacp.

So - I looked at
RHEL 6 - Installation Guide sec 28.1.5.1
which then references
Deployment Guide (Working with Kernel Modules, Chap 28)... and then I'm lost again ;-)

Hello James

That is something I could add to the Deployment Guide in future. I did find this BTW: How to configure LACP 802.3ad with bonded interfaces

Hey Stephen,
The documentation is solid for configuring the bond once the OS is running. We are (were) struggling with kickstarting a host using LACP (see my post at the end). I think the biggest issue is not being able to use
mode=4 versus mode=802.3ad -- that is certainly not intuitive. (I am unable to find supporting doc indicating that you actually cannot use the numeric bond mode - so, it might be a bug?).
thanks

Hello James

I am asking around.

Thanks Tim/James - I've been discussing this with my network guy and we're going to try this sometime soon. Thanks for bringing this up Tim.

I have been successful generating a bonded interface with a python post script. The configuration is read from an included snippet containing a python dictionary which contains various configurations for different hosts. I'd like to extend it so that various levels of pattern matching can be used.

I simply wrote a snippet that updated the files in /etc/sysconfig/network-scripts/ifcfg_*

Thanks Tim It seemed the post script would be the easiest method. Glad to hear you have it.

O.K. Here are some Python snippets. The first is the actual config data. It's just Python dictionaries (or hashes). Should be fairly obvious. This is where you put your network configuration. The second is the actual code snippet. I'm a fairly new Python user (I learned exclusively for Satellite) so I'm sure this could be coded a bit better.

Define your script and put the snippet call in it:
$SNIPPET('spacewalk/1/base-post-hw-network')

Name your script ( call mine the same as the snippet:
base-post-hw-network

Define the scripting language:
/usr/bin/python

Select Script Execution Time:
Post Script

Check "Template"

Click Update

--------------@config-network----------------
DNS=["10.10.10.36", "10.10.10.17", "10.10.10.38"];
DOMAIN="ccci.org"
NAMESERVERS="ccci.org,net.ccci.org"
VLANS={
10:{
'NETMASK':'255.255.255.0',
'GATEWAY':'10.10.10.254'
},
11:{
'NETMASK':'255.255.255.0',
'GATEWAY':'10.10.11.254'
},
13:{
'NETMASK':'255.255.255.0',
'GATEWAY':'10.10.13.254'
},
417:{
'NETMASK':'255.255.255.192',
'GATEWAY':'10.10.210.1'
}
}
IFACES={
"00:18:8B:32:84:67":
{
'HOSTNAME':"llrhnt00",
'IPADDR':"10.10.11.9",
'MASTER':"bond0",
'VLAN':11
},
"00:18:8B:32:84:69":
{
'HOSTNAME':"llrhnt00",
'IPADDR':"10.10.11.9",
'MASTER':"bond0",
'VLAN':11
},
"B8:CA:3A:6E:9D:C0":
{
'HOSTNAME':"plorad60",
'IPADDR':"10.10.210.60",
'MASTER':"bond0",
'VLAN':417
},
"B8:CA:3A:6E:9D:C1":
{
'HOSTNAME':"plorad60",
'IPADDR':"10.10.210.60",
'MASTER':"bond0",
'VLAN':417
},
}

--------------base-post-hw-network----------------
import re
import platform
import subprocess
import glob
import uuid
from subprocess import Popen

$SNIPPET('spacewalk/1/@config-network')

def run(cmd):
popen=Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return popen.stdout

def update_iface(from_iface, to_iface):
for k, v in from_iface.iteritems():
to_iface[k] = v

def update_ifaces(from_ifaces, to_ifaces):
for from_key, from_iface in from_ifaces.iteritems():
if from_key in to_ifaces:
to_iface=to_ifaces[from_key]
update_iface(from_ifaces[from_key], to_iface)
to_ifaces[from_key]=to_iface
else:
to_ifaces[from_key]=from_iface

p1=re.compile('(?P[^=]+)="?(?P[^"]+)"?')
p2=re.compile(r"^(?P\w+) +Link +encap:Ethernet +HWaddr +(?P[0-9[a-fA-F:]+).*")

HOSTNAME=platform.node()
DOMAIN="ccci.org"
SEARCH="ccci.org,net.ccci.org"
DNS=["10.10.10.36", "10.10.10.17", "10.10.10.38"];
DNSNAMES=""
for n in range(0, len(DNS)):
DNSNAMES+="DNS{0}={1}\n".format(n+1, DNS[n])

$SNIPPET('spacewalk/1/@config-network')

uppercase keys in IFACES, user might have forgotten

ifaces={}
for k1, v1 in IFACES.iteritems():
iface={}
for k2, v2 in v1.iteritems():
iface[k2.upper()]=v2
ifaces[k1.upper()]=v1
IFACES=ifaces

apply available device names from ifconfig to IFACES

p=run("ifconfig -a")
for line in p.readlines():
m2=p2.match(line)
if m2:
DEVICE=m2.group('DEVICE')
HWADDR=m2.group('HWADDR')
if HWADDR in IFACES:
iface=IFACES[HWADDR]
iface['DEVICE']=DEVICE
iface['HOSTNAME']=HOSTNAME
iface['HWADDR']=HWADDR
IFACES[HWADDR]=iface

build new ifaces from ifcfg files

ifaces={}
for file in glob.glob('/etc/sysconfig/network-scripts/ifcfg-*'):
if file == '/etc/sysconfig/network-scripts/ifcfg-lo':
continue
iface={}
f=open(file, 'r')
for line in f.readlines():
line=line.strip()
m1=p1.match(line)
if m1:
iface[m1.group('name')]=m1.group('value')
f.close()
if 'HWADDR' in iface:
HWADDR=iface['HWADDR']
ifaces[HWADDR]=iface

merge new ifaces with original IFACES

update_ifaces(ifaces, IFACES)

build list of bonded interfaces from interfaces

and fill in device names from ifconfig

dev=""
bonds={}
nets={}
for HWADDR, IFACE in IFACES.iteritems():
if 'DEVICE' in IFACE:
DEVICE=IFACE['DEVICE']
nets[IFACE['DEVICE']]=IFACE
if 'MASTER' in IFACE:
MASTER=IFACE['MASTER']
IFACE['SLAVE']='yes'
bond={}
if MASTER in bonds:
bond=bonds[MASTER]
if not 'HOSTNAME' in IFACE:
print IFACE
exit(0)
bond[DEVICE]=IFACE
bonds[MASTER]=bond
ifaces[HWADDR]=IFACE
for hwaddr, iface in ifaces.iteritems():
IFACES[HWADDR]=iface

emit any defined bond interfaces

for bondname, bond in bonds.iteritems():
bonddevs=list(bond.keys())
bondkey0=bonddevs[0]
bondx=bond[bondkey0]
bondname=bond[bondkey0]['MASTER']
HOSTNAME=bond[bondkey0]['HOSTNAME']
IPADDR=bond[bondkey0]['IPADDR']
VLAN=VLANS[bond[bondkey0]['VLAN']]
GATEWAY=VLAN['GATEWAY']
NETMASK=VLAN['NETMASK']
UUID=uuid.uuid4()
bondnames=",".join(bonddevs)
t=open('/etc/sysconfig/network-scripts/ifcfg-{0}'.format(bondname), 'w')
t.write("#-----\nDEVICE={0}\nHOSTNAME={1}.{2}\nIPADDR={3}\nGATEWAY={4}\nNETMASK={5}\n{6}UUID={7}\nIPV6INIT=yes\nMTU=1500\nNM_CONTROLLED=no\nONBOOT=yes\nTYPE=bond\nBONDING_OPTS=\"mode=802.3ad miimon=100 lacp_rate=fast\"\nBOOTPROTO=static\n".format(bondname,HOSTNAME,DOMAIN,IPADDR,GATEWAY,NETMASK,DNSNAMES,UUID))
t.close()
t=open('/etc/modprobe.d/{0}.conf'.format(bondname), 'w')
t.write('alias {0} bonding\n'.format(bondname))
t.close()

emit real interfaces

for netname, net in nets.iteritems():
if not 'HOSTNAME' in net:
continue
SLAVE=''
if 'SLAVE' in net:
SLAVE=net['SLAVE'].upper()
del net['VLAN']
if SLAVE == 'YES':
net['ONBOOT']='yes'
net['NM_CONTROLLED']='no'
net['BOOTPROTO']='none'
for k in ['GATEWAY', 'NETMASK', 'IPADDR', 'TYPE', 'HOSTNAME']:
if k in net:
del net[k]
t=open("/etc/sysconfig/network-scripts/ifcfg-{0}".format(netname), 'w')
t.write('#---------\n')
for k, v in net.iteritems():
k=k.upper()
# don't emit stuff on slave interface
t.write('{0}="{1}"\n'.format(k, v))
t.close()

Very nice! Thank you much for posting this!

I'm pretty excited to update this thread... today I was able to kickstart using 802.3ad!

We use a fairly convuluted kickstart method.. but I think the general idea will still come across.

 kickstart6 hostname=myhost.mycompany.com ip=10.5.102.27 netmask=255.255.254.0 gateway=10.5.102.1 bond=bond0:em1,em2:mode=802.3ad,miimon=100,lacp_rate=fast

kickstart6 is an "alias" for:

label kickstart6
  kernel vmlinuz
  append initrd=initrd.img noipv6 lang=en_US ks=http://satellite.mycompany.com/ks/cfg/org/1/label/kickstart6 dns=10.98.230.27,10.98.230.28

IMPORTANT: I think one item regarding the network setup for kickstarts, that is not obvious (nor well documented) is that you cannot use the numeric indicator for the Bond type. (i.e. you need 802.3ad not mode=4) I will try to find some documentation supporting that statement though.

Close

Welcome! Check out the Getting Started with Red Hat page for quick tours and guides for common tasks.