I hate having to manually set the hostname in kickstart file, so when I
found a fix, I was very happy. I wish I can take credit, but it was
originally made by somebody who was trying to figure out a way to
automatically set the hostname for VMWare ESX machines. Unfortunately, I
lost that link, so I can’t refer to the other page for credit. So the
best I can do is to explain how it is done and hopefully I find that
link later and update this post, so that the right person is properly
attributed.
To explain how the solution works, its good to understand how Linux
boots a system, which this
article
does a very good job of explaining. However, if you are impatient, this
is short version:
- Computer turns on (DUH!)
- BIOS kick in, which performs POST, local device enumeration and
initialization and then searches for active and bootable devices.
- Stage 1 (MBR) kicks in, looks for boot loader (in our case, GRUB)
- Grub (Stage 2) then loads kernel with an optional ramdisk.
- kernel boots, initializes and then starts init (or some other
process) that then starts up other processes
Now with that mind, let’s take a look at our grub on jenkins:
[root@jenkins chef]# cat /etc/grub.conf
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/mapper/vg_centos6-lv_root
# initrd /initrd-[generic-]version.img
#boot=/dev/vda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-220.2.1.el6.x86_64)
root (hd0,0)
kernel /vmlinuz-2.6.32-220.2.1.el6.x86_64 ro root=/dev/mapper/vg_centos6-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=vg_centos6/lv_swap rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb rd_LVM_LV=vg_centos6/lv_root KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto rhgb quiet rd_NO_DM
initrd /initramfs-2.6.32-220.2.1.el6.x86_64.img
As you can see, it boots the kernel, as well as set parameters such as
root file system, language, keyboard and others things needs for the
system to boot up properly. That information is actually still available
in the running kernel by viewing the following file:
[root@jenkins chef]# cat /proc/cmdline
ro root=/dev/mapper/vg_centos6-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=vg_centos6/lv_swap rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb rd_LVM_LV=vg_centos6/lv_root KEYBOARDTYPE=pc KEYTABLE=us rhgb quiet rd_NO_DM
[root@jenkins chef]
Notice that in this file, you will find the same parameters as you find
in the grub.conf. In some ways, if init (at least on System-V systems)
is the mother of all process, the kernel is the grandmother, quietly
hidden in the background.
What if you were to pass a parameter that it doesn’t recognize? In most
cases, it will probably ignore it, but it will still in the kernel list.
So lets insert:
to the kernel line right between “crashkernel=auto” and “rhgb” (either
in grub or at kernel line at boot loader page during stage 2):
kernel /vmlinuz-2.6.32-220.2.1.el6.x86_64 ro root=/dev/mapper/vg_centos6-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=vg_centos6/lv_swap rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb rd_LVM_LV=vg_centos6/lv_root KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto FOO=BAR rhgb quiet rd_NO_DM
Now lets view /proc/cmdline again:
[root@jenkins ~]# cat /proc/cmdline
ro root=/dev/mapper/vg_centos6-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=vg_centos6/lv_swap rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb rd_LVM_LV=vg_centos6/lv_root KEYBOARDTYPE=pc KEYTABLE=us FOO=BAR rhgb quiet rd_NO_DM
[root@jenkins ~]#
As we can see, FOO=BAR is in there, with no ill effects to the system
boot.
So why would we want to pass a value that the kernel doesn’t use? So
that we can do this:
[rilindo@jenkins ~]$ for x in `cat /proc/cmdline`
> do
> case $x in FOO*)
> eval $x
> echo "${FOO}"
> ;;
> esac
> done
BAR
[rilindo@jenkins ~]$
What this script does is to get the output of /proc/cmdline as a series
of positional elements (think of it like a list or an array) and loop
through it. Then we will test each element through a case statement and
if it matches (in this case, FOO), then it evaluates it to a variable.
We then echo that variable, which will then return a value. In other
words, we look for a section that has “FOO”, and get “BAR” out of it.
That is essentially how we automatically set the hostname in our
installation. Using this technique, we put this script in our %pre
section of our kickstart:
%pre
#!/bin/sh
for x in `cat /proc/cmdline`; do
case $x in SERVERNAME*)
eval $x
echo "network --device eth0 --bootproto dhcp --hostname ${SERVERNAME}.monzell.com" > /tmp/network.ks
;;
esac;
done
%end
Here, we look for a value called SERVERNAME and evaluates that value
into a variable. We will then echo the network setup with the variable
(which we will use as part of the hostname setup) and redirect into the
file under /tmp. Then we will include that file in our installation
section:
At this point, we are essentially done. To use it, we just need to pass
SERVERNAME=X (where X is the name of the hostname you want to set) in
our kickstart setup. In our case, we build virtual machines with KVM via
virt-install, so we pass that in the following line:
virt-install --name jenkins --disk path=/home/vms/jenkins,size=50,bus=virtio --vnc --noautoconsole --vcpus=1 --ram=512 --network bridge=br0,mac=52:54:00:91:95:30 --location=http://192.168.15.100/mirrors/centos/6.2/os/x86_64/ -x "ks=http://192.168.15.100/mirrors/ks/6.2/kvm/x86_64-Ruby-test.cfg SERVERNAME=jenkins"
Here is my entire kickstart file:
install
url --url http://192.168.15.100/mirrors/centos/6.2/os/x86_64/
lang en_US.UTF-8
keyboard us
text
%include /tmp/network.ks
rootpw --iscrypted PUTPASSWORDHERE
firewall --service=ssh
authconfig --enableshadow --passalgo=sha512 --enablefingerprint
selinux --enforcing
timezone --utc America/New_York
bootloader --location=mbr --driveorder=vda --append="crashkernel=auto rhgb quiet"
clearpart --all --drives=vda --initlabel
part /boot --fstype=ext4 --size=500
part pv.EPlgaf-h1b4-YqDI-2wfs-3C7I-SPPt-Agk5O7 --grow --size=1
volgroup vg_centos6 --pesize=4096 pv.EPlgaf-h1b4-YqDI-2wfs-3C7I-SPPt-Agk5O7
logvol / --fstype=ext4 --name=lv_root --vgname=vg_centos6 --grow --size=1024 --maxsize=51200
logvol swap --name=lv_swap --vgname=vg_centos6 --grow --size=1008 --maxsize=2016
repo --name="Local CentOS 6 - x86_64" --baseurl=http://192.168.15.100/mirrors/centos/6.2/os/x86_64
repo --name="Local CentOS 6 - x86_64 - Updates" --baseurl=http://192.168.15.100/mirrors/centos/6.2/updates/x86_64
repo --name="Local Custom Installs" --baseurl=http://192.168.15.100/mirrors/customrepos/centos/x86_64
%packages
@base
@console-internet
@core
@debugging
@directory-client
@hardware-monitoring
@large-systems
@network-file-system-client
@performance
@perl-runtime
@scalable-file-systems
@server-platform
gcc
gcc-c++
pax
oddjob
sgpio
certmonger
pam_krb5
krb5-workstation
nscd
pam_ldap
nss-pam-ldapd
perl-DBD-SQLite
ruby-1.9.3p0
rubygems-1.8.12
%end
%pre
#!/bin/sh
for x in `cat /proc/cmdline`; do
case $x in SERVERNAME*)
eval $x
echo "network --device eth0 --bootproto dhcp --hostname ${SERVERNAME}.example.com" > /tmp/network.ks
;;
esac;
done
%end
%post --log=/root/my-post-log
setsebool -P use_nfs_home_dirs on
mkdir /home/users
mkdir /etc/chef
URLPOSTCONF="http://192.168.15.100/mirrors/ks"
curl ${URLPOSTCONF}/6.2/repos/CentOS-Custom.repo -o /etc/yum.repos.d/CentOS-Custom.repo
curl ${URLPOSTCONF}/6.2/autofs/auto.master -o /etc/auto.master
curl ${URLPOSTCONF}/6.2/autofs/auto.home -o /etc/auto.home
curl ${URLPOSTCONF}/keys/cacert.pem -o /etc/openldap/cacerts/cacert.pem
curl ${URLPOSTCONF}/chef/validation.pem -o /etc/chef/validation.pem
curl ${URLPOSTCONF}/chef/client.rb -o /etc/chef/client.rb
curl ${URLPOSTCONF}/chef/first-run.json -o /etc/chef/first-run.json
rpm --import ${URLPOSTCONF}/keys/legacy.key
rpm --import ${URLPOSTCONF}/keys/custom.key
authconfig --enablesssd --enableldap --enableldaptls --ldapserver=kerberos.monzell.com --ldapbasedn="dc=monzell,dc=com" --enableldapauth --update
echo "nameserver 192.168.15.57" >> /etc/resolv.conf
echo "nameserver 192.168.15.71" >> /etc/resolv.conf
gem install chef
chef-client -j /etc/chef/first-run.json
chkconfig chef-client on
chkconfig rpcbind on
chkconfig sssd on
chkconfig ntpd on
sync
%end
reboot
Let me know if this is useful. And again, I didn’t originally came up
with this, so I plead innocent to charges ofplagiarism. :)