The configuration is the collection of information about how exactly
to install a computer. The central configuration space for all install
clients is located on the install server in /srv/fai/config and its
subdirectories. This will be mounted by the install clients to
/var/lib/fai/config. It's also possible to receive all the
configuration data from a cvs(1), subversion (svn(1)) or Git
(git(1)) repository. The following subdirectories are present and
include several files:
debconf(8) data. The format is the same
that is used by debconf-set-selections(8).
fcopy(8) command.
ftar(8) is used to extract the
tar file based on the classes defined. This is done in task
extrbase.
The main installation command fai(8) uses all these subdirectories
in the order listed except for hooks. The FAI package contains
examples for all these configuration scripts and files in
/usr/share/doc/fai-doc/examples. Copy the configuration examples to
the configuration space and start an installation. These files need
not belong to the root account. You can change their ownership and
then edit the configuration with a normal user account.
# cp -a /usr/share/doc/fai-doc/examples/simple/* /srv/fai/config # chown -R fai /srv/fai/config
These files contain simple configuration for some example hosts. Depending on the host name used, your computer will be configured as follows:
Start looking at these examples and study them. Then change or add things to these examples. But don't forget to plan your own installation!
After the kernel has booted, it mounts the root file system via NFS
from the install server and init(8) starts the script
/usr/sbin/fai. This script controls the sequence of the
installation. No other scripts in /etc/init.d/ are used.
The installation script uses many subroutines, which are defined in /usr/lib/fai/subroutines. All important tasks of the installation are called via the subroutine task appended by the name of the task as an option (e.g. task_instsoft). The subroutine task calls hooks with prefix name if available and then calls the default task (defined as task_<name> in subroutines). The default task and its hooks can be skipped on demand by using the subroutine skiptask().
Now follows the description of all default tasks, listed in the order they are executed.
The kernel appended parameters define variables, the syslog and kernel
log daemon are started. The list of network devices is stored in
$netdevices. Then additional parameters are fetched from a DHCP
server and also additional variables are defined. The DNS
resolver configuration file is created.
The location of the configuration space is defined by the variable
$FAI_CONFIG_SRC. You can use NFS, cvs, svn or git to access the
configuration space. See section [isetup] for how to set the
variable.
After that, the file $FAI/hooks/subroutines is sourced if it exists. Using this file, you can define your own subroutines or override the definition of FAI's subroutines.
$FAI_FLAGS are defined and two
additional virtual terminals are opened on demand. A secure shell
daemon is started on demand for remote logins.
fai-class(1) to define classes using scripts and files in
$FAI/class and classes from /tmp/fai/additional-classes and the
variable $ADDCLASSES.
$FAI_ACTION this subroutine decides which
action FAI should perform. The default available actions are:
sysinfo, install and softupdate. If $FAI_ACTION has another
value, a user defined action is called if a file
$FAI/hooks/$FAI_ACTION exists. So you can easily define your own
actions.
partitionname or with
regard to a fstab file found inside a partition. Log files are
stored to the install server.
fai(8) command
line interface, performs a softupdate. See chapter [softupdate] for
details.
setup-storage(8) to partition the hard
disks and to create file systems. The task writes variable definitions
for the root and boot partition and device ($ROOT_PARTITION,
$BOOT_PARTITION, $BOOT_DEVICE) to /tmp/fai/disk_var.sh and creates
an fstab file.
$FAI_ROOT.
ftar -1v -s $FAI/basefiles / is used for unpacking a
different tar file depending on classes defined. This can be used for
installing different Linux distributions than the one used for
creating the nfsroot. The default file base.tgz is a snapshot of a
basic Debian system created by debootstrap(8)
$FAI_DEBMIRROR is
defined), this directory will be mounted to $MNTPOINT.
fai-debconf(8) to set the values for the debconf database.
dpkg-divert(8).
fai-divert.
fai-chboot(8) command is executed
remotely on the install server.
$LOGUSER on
$LOGSERVER (defaults to the install server).
After the subroutine fai_init has done some basic initialization
(create RAM disk, read fai.conf and all subroutines definitions, set
path, print copyright notice), the setup continues by calling the task
confdir and the task setup. The command get-boot-info is called
to get all information from the DHCP server. This command
writes the file /tmp/fai/boot.log, which then is sourced to define
the corresponding global variables. This is an example for this log
file when using a DHCP server.
# cat /tmp/fai/boot.log netdevices_all="eth0" netdevices_up="eth0" netdevices="eth0" BROADCAST='192.168.1.255' DOMAIN='localdomain' DNSSRVS='192.168.1.1' DNSSRVS_1='192.168.1.1' HOSTNAME='demohost' IPADDR='192.168.1.12' NETWORK='192.168.1.0' GATEWAYS='192.168.1.250' GATEWAYS_1='192.168.1.250' SERVER='faiserver' NETMASK='255.255.255.0'
Additional information is passed via the kernel command line or read
from fai.conf. When booting with PXE, command line parameters are
created using fai-chboot(8).
If you do not boot from network card but from CD-ROM or USB stick, you may also give network parameters to the kernel via the kernel command line. Two interesting parameters are
nfsroot=<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
Those parameters are described in the documentation of the Linux kernel sources in /usr/src/linux/Documentation/nfsroot.txt.
The variable $FAI_FLAGS contains a space separated list of
flags. The following flags are known:
setup-storage(8). Partitions marked with preserve_reinstall
are preserved unless this flag ist set. Often, this flag is set in a
file class/*.var by using setting flag_initial=1.
Classes determine which configuration file to choose from a list of available templates. Classes are used in all further tasks of the installation. To determine which config file to use, an install client searches the list of defined classes and uses all configuration files that match a class name. It's also possible to use only the configuration file with the highest priority since the order of classes define the priority from low to high. There are some predefined classes (DEFAULT, LAST and the host name), but classes can also be listed in a file or defined dynamically by scripts. So it's easy to define a class depending on the subnet information or on some hardware that is available on the install client.
The idea of using classes in general and using certain files matching a class name for a configuration is adopted from the installation scripts by Casper Dik for Solaris. This technique proved to be very useful for the SUN workstations, so I also use it for the fully automatic installation of Linux. One simple and very efficient feature of Casper's scripts is to call a command with all files (or on the first one) whose file names are also a class. The following loop implements this function in pseudo shell code:
for class in $all_classes; do if [ -r $config_dir/$class ]; then your_command $config_dir/$class # exit if only the first matching file is needed fi done
Therefore it is possible to add a new file to the configuration
without changing the script. This is because the loop automatically
detects new configuration files that should be used. Unfortunately
cfengine does not support this nice feature, so all classes being used
in cfengine also need to be specified inside the cfengine
scripts. Classes are very important for the fully automatic
installation. If a client belongs to class A, we say the class A
is defined. A class has no value, it is just defined or
undefined. Within scripts, the variable $classes holds a space
separated list with the names of all defined classes. Classes
determine how the installation is performed. For example, an install
client can be configured to become an FTP server by just adding the
class FTP to it.
Mostly a configuration is created by only changing or appending the classes to which a client belongs, making the installation of a new client very easy. Thus no additional information needs to be added to the configuration files if the existing classes suffice for your needs. There are different possibilities to define classes:
The last option is a very nice feature, since these scripts will define classes automatically. For example, several classes are defined only if certain hardware is identified. We use Perl and shell scripts to define classes. All names of classes, except the host name, are written in uppercase. They must not contain a hyphen, a hash or a dot, but may contain underscores. A description of all classes can be found in /usr/share/doc/fai-doc/classes_description.txt.
host names should rarely be used for the configuration files in the configuration space. Instead, a class should be defined and then added for a given host. This is because most of the time the configuration data is not specific for one host, but can be shared among several hosts.
The task defclass calls the script fai-class(1) to define
classes. Therefore, scripts matching [0-9][0-9]* in $FAI/class are
executed. Additionally, a file with the host name may contain a list
of classes. For more information on defining class, read the manual
pages for fai-class(1).
The list of all defined classes is stored in the variable $classes
and saved to /tmp/fai/FAI_CLASSES. The list of all classes is
transferred to cfengine, so it can use them too. The script
10-base-classes (below is a stripped version) is used to define
classes depending on the host name. First, this script defines the
class with the name of the hardware architecture in uppercase letters.
10-base-classes:
# echo architecture and OS name in upper case. Do NOT remove these two lines
uname -s | tr '[:lower:]' '[:upper:]'
dpkg --print-architecture | tr /a-z/ /A-Z/
[ -f /etc/RUNNING_FROM_FAICD ] && echo "FAICD"
# use a list of classes for our demo machine
case $HOSTNAME in
demohost)
echo "FAIBASE GRUB DHCPC DEMO" ;;
gnomehost)
echo "FAIBASE GRUB DHCPC DEMO XORG GNOME";;
*)
echo "FAIBASE GRUB DHCPC" ;;
esacThe script 20-hwdetect.source uses the default Debian commands to detect hardware and to load some kernel modules. If some specific hardware is found, it can also define a new class for it. You can find messages from modprobe in /tmp/fai/kernel.log and on the fourth console terminal by pressing Alt-F4.
The task defvar defines the variables for the install
client. Variables are defined by scripts in class/*.var. All global
variables can be set in DEFAULT.var. For certain groups of hosts use
a class file or for a single host use the file $HOSTNAME.var. Also
here, it's useful to study all the examples.
The following variables are used in the examples and may also be useful for your installation:
fai-chboot(8). If you can't use this command, define it in the
script LAST.var.
consolechars(8).
crypt(3)
or md5 encryption for the password. You can create the encrypted
password using mkpasswd(1). See chapter various hints for
more info.
clock(8) for more information.
Read the manual page of setup-storage(8) for a detailed description
of the new format. This is used by default since FAI 3.2.8.
Before installing packages, fai will add the content of all files named package_config/*.asc to the list of apt keys. If your local repository is signed by your keyid AB12CD34 you can easily add this key, so fai will use it during installation. Use this command for creating the .asc file:
faiserver$ gpg -a --export AB12CD34 > /srv/fai/config/package_config/myrepo.asc
The script install_packages(8) installs the selected software
packages. It uses all configuration files in $FAI/package_config
whose file name matches a defined class. The syntax is very simple.
# an example package class PACKAGES taskinst german PACKAGES aptitude adduser netstd ae less passwd PACKAGES remove gpm xdm PACKAGES aptitude GRUB lilo- grub PACKAGES dselect-upgrade ddd install a2ps install
Comments are starting with a hash (#) and are ending at the end of the line. Every command begins with the word PACKAGES followed by a command name. The command defines which command will be used to install the packages named after this command. The list of all available commands can be listed using install_packages -H. Supported package tools are: aptitude, apt-get, smart, y2pmsh, yast, yum, urpm, rpm
tasksel(1). You can also use aptitude for
installing tasks.
aptitude. This will be the
default in the future and may replace apt-get and taskinst. Aptitude
can also install task packages.
Multiple lines with lists of space separated names of packages follow
the PACKAGES lines. All dependencies are resolved. Packages with
suffix - (eg. lilo-) will be removed instead of installed. The
order of the packages is of no matter. If you like to install
packages from another release than the default, you can append the
release name to the package name like in
openoffice.org/etch-backports. You can also specify a certain
version like apt=0.3.1. More information on these features are
described in aptitude(8).
A line which contains the PRELOADRM commands, downloads a file using
wget(1) into a directory before installing the packages. Using the
file: URL, this file is copied from $FAI_ROOT to the download
directory. For example the package realplayer needs an archive to
install the software, so this archive is downloaded to the directory
/root. After installing the packages this file will be removed. If
the file shouldn't be removed, use the command PRELOAD instead.
It's possible to append a list of class names after the command for apt-get. So this PACKAGE command will only be executed when the corresponding class is defined. So you can combine many small files into the file DEFAULT. WARNING! Use this feature only in the file DEFAULT to keep everything simple. See this file for some examples.
If you want to remove a package name from a certain class was part of this class before, you should not remove the package name from the class file, but instead append a dash (-) to it. This will make sure that the package is remove during a softupdate on hosts which were installed using the old class definition which included this package name.
If you specify a package that does not exist this package will be removed from the installation list when the command install is used.
The default set of scripts in $FAI/scripts is only an example. But they should do a reasonable job for your installation. You can edit them or add new scripts to match your local needs.
The command fai-do-scripts(1) is called to execute all scripts in
this directory. If a directory with a class name exists, all scripts
matching [0-9][0-9]* are executed in alphabetical order. So it's
possible to use scripts of different languages (shell, cfengine,
Perl,..) for one class.
Most scripts are Bourne shell scripts. Shell scripts are useful if the
configuration task only needs to call some shell commands or create a
file from scratch. In order not to write many short scripts, it's
possible to distinguish classes within a script using the command
ifclass. For copying files with classes, use the command
fcopy(8). If you want to extract an archive using classes, use
ftar(8). But now have a look at the scripts and see what they are
doing.
Currently no Perl script are used in the simple examples for modifying the system configuration.
Currently no expect scripts are used in the simple examples for modifying the system configuration.
Cfengine has a rich set of functions to edit existing configuration files, e.g LocateLineMatching, ReplaceAll, InsertLine, AppendIfNoSuchLine, HashCommentLinesContaining. But it can't handle variables which are undefined. If a variable is undefined, the whole cfengine script will abort. Study the examples that are included in the fai package.
More information can be found in the manual page cfengine(8) or at
the cfengine homepage http://www.cfengine.org.
Changing the boot sequence is normally done in the BIOS setup. But you can't change the BIOS from a running Linux system as far as I know. If you know how to perform this, please send me an email. But there's another way of swapping the boot device of a running Linux system.
So, normally the boot sequence of the BIOS will remain unchanged and
your computer should always boot first from its network card and the
second boot device should be the local disk. Then, it will get an
install kernel image from the install server, when an installation
should be performed, or we can tell pxelinux to boot from local
disk. This is done using fai-chboot(8).
Here is how to set up a 3Com network card as first boot device. Enable LAN as first boot device in the BIOS.
Boot From LAN First: Enabled Boot Sequence : C only
Then enter the MBA setup of the 3Com network card and change it as follows:
Default Boot Local Local Boot Enabled Message Timeout 3 Seconds Boot Failure Prompt Wait for timeout Boot Failure Next boot device
This will enable the first IDE hard disk as second boot device after the network card.
Hooks let you specify functions or programs which are run at certain
steps of the installation process. Before a default task is called,
FAI searches for existing hooks for this task and executes them. As
you might expect, classes are also used when calling hooks. Hooks are
executed for every defined class. You only have to create the hook
with the name for the desired class and it will be used. If several
hooks for a task exists, they are called in the order defined by the
classes. If debug is included in $FAI_FLAG the option -d is
passed to all hooks, so you can debug your own hooks. If some default
tasks should be skipped, use the subroutine skiptask and a list of
default tasks as parameters. The example partition.DISKLESS skips
some default tasks.
The directory $FAI/hooks/ contains all hooks. The file name of a hook consists of a task name as a prefix and a class name, separated by a dot. The prefix describes the time when the hook is called, if the class is defined for the install client. For example, the hook partition.DISKLESS is called for every client belonging to the class DISKLESS before the local disks would be partitioned. If it should become a diskless client, this hook can mount remote file systems via NFS and create a /tmp/fai/fstab. After that, the installation process will not try to partition and format a local hard disk, because a file /tmp/fai/fstab already exists.
A hook of the form hookprefix.classname can't define variables for the installation script, because it's a subprocess. But you can use any binary executable or any script you wrote. Hooks that have the suffix .source (e.g. partition.DEFAULT.source) must be Bourne shell scripts and are sourced. So it's possible to redefine variables for the installation scripts.
In the first part of FAI, all hooks with prefix confdir are called.
Since the configuration directory $FAI is mounted in the default
task confdir, the hooks for this task are the only hooks located in
$nfsroot/$FAI/hooks on the install server. All other hooks are
found in $FAI_CONFIGDIR/hooks on the install server.
All hooks that are called before classes are defined can only use the
following classes: DEFAULT $HOSTNAME LAST. If a hook for class
DEFAULT should only be called if no hook for class $HOSTNAME is
available, insert these lines to the default hook:
hookexample.DEFAULT: #! /bin/sh # skip DEFAULT hook if a hook for $HOSTNAME exists scriptname=$(basename $0 .DEFAULT) [-f $FAI/hooks/$scriptname.$HOSTNAME ] && exit # here follows the actions for class DEFAULT . .
Some examples for what hooks could be used:
ssh in the very beginning to verify that you mounted the
configuration from the correct server and not a possible spoofing
host.
$FAI_LOCATION.
If the client can't successfully boot from the network card, use
tcpdump(8) to look for Ethernet packets between the install server
and the client. Search also for entries in several log files made by
tftpd(8) and dhcpd3(8) :
faiserver$ egrep "tftpd|dhcpd" /var/log/*
Sometimes the installation seems to stop, but often there's only a
postinstall script of a software package that requires manual input
from the console. Change to another virtual terminal and look which
process is running with tools like top(1) and pstree(1). You can
add debug to FAI_FLAGS to make the installation process show all
output from the postinst scripts on the console and get its input also
from the console. Don't hesitate to send an email to the mailing list
or to fai@fai-project.org if you have any
questions. Sample log files from successfully installed computers are
available on the FAI homepage.
FAI is creating several log files. During installation they are stored in /tmp/fai on the install client itself. At the end of the installation they will be copied to the install server (see [isavelog]). After the install client rebooted into his newly installed system, you can find the FAI logs in /var/log/fai. Log files are also created when doing the softupdate or dirinstall action.
These a some log files which are created by FAI.
dmesg command. Contains useful messages of the kernel
ring buffer.
setup-storage(8).
If the installation process finishes, the hook savelog.LAST.source searches all log files for common errors and writes them to the file error.log. So, you should first look into this file for errors. Also the file status.log give you the exit code of the last command executed in a script. To be sure, you should look for more details in all log files.