Showing posts with label dhcp. Show all posts
Showing posts with label dhcp. Show all posts

Tuesday, 1 April 2008

One rule to bind them - part III

Apologies for the length of time between this article and the last in the series. Home and work life has been hectic and hasn't left much time for writing.

In the previous article in the series I wrote a how-to on DHCP booting a Solaris installation and passing custom DHCP options. In this article I'll conclude the series and explain what you can do with this information to make your installation hands-free.

What does a begin script do?

A begin script allows you to execute a series of commands before the installation commences, such as backing up files before an upgrade or creating a derived jumpstart profile. I'll be discussing the latter.

In my jumpstart environment a build list is generated which provides the minimum information about each Solaris build I maintain. It looks like this:
DNS:5.10:RNET:Domain Name Server
FIREWALL:5.9:CORE:Firewall-1
CORE:5.10:CORE:Hardened CORE
CORE:5.9:CORE:Hardened CORE
DEVELOPER:5.10:DEVELOPER:Hardened DEVELOPER
DEVELOPER:5.9:DEVELOPER:Hardened DEVELOPER
ENTIRE:5.10:ENTIRE:Hardened ENTIRE
ENTIRE:5.9:ENTIRE:Hardened ENTIRE
MINIMIZED:5.10:CORE:Hardened Minimized
MINIMIZED:5.9:CORE:Hardened Minimized
The first column corresponds to the JSBuild DHCP option that is sent to the client. The second column is the Solaris version. The third column is the Solaris metacluster: Core, User, Developer, Entire, Entire OEM. The fourth column is a short description of the build.

As the name of the article implies there is only one rule therefore only one begin script. This script does all the heavy lifting required to generate the derived jumpstart profile on a per build basis.

Determining what to build

The first thing the script does is extract the JSBuild DHCP option which tells it which build it is installing. If the JSBuild DHCP option empty (due to incorrect DHCP server configuration or use of old style bootparams installation) then the begin script falls back to the build list above and generates an interactive menu for the user to choose a build from.

Deriving the profile

Now the system knows what it is, it needs to know how to install it. This is what the profile does. The begin script tests whether it has to perform an initial installation or flash archive installation by NFS mounting a directory from the jumpstart server and looking for a flash archive. I have an automated process that builds and patches flash archives for each Solaris metacluster (more about this in a future article). If no flash archive is present then the script falls back to an initial install.

The initial install logic looks for a metacluster include file which defines all of the packages to install. The include file looks like:
#!/bin/sh
#
# Minimal Solaris 10 build
# Start with the minimal required number of packages
echo "cluster SUNWCreq" >> ${SI_PROFILE}

# For performance monitoring
echo "cluster SUNWCacc add" >> ${SI_PROFILE}

# To support Compression (gzip)
echo "cluster SUNWCfwcmp add" >> ${SI_PROFILE}

# To support the Network Time Protocol
echo "cluster SUNWCntp add" >> ${SI_PROFILE}
...
For Solaris 10 initial installations the script also outputs a profile patch keyword like:
patch patch_order nfs://jumpstart/path/to/10_Recommended
Which applies the recommended patch bundle during jumpstart. For Solaris releases prior to 10 this is done using the JASS install-recommended-patches.fin finish script.

Disk layout and mirroring

The next step is to see if the build has a custom partition configuration. Here is an example partition configuration file:
s0:6144:/:logging:
s1:1024:swap::
s3:4096:/var:logging,nosuid:
s6:free:/srv:zpool:srv
zfs1:free:/opt::srv/opt
zfs2:free:/var/opt::srv/var-opt
This file defines the disk slice, size, file system mount, and options. You'll notice that it caters for creating ZFS file systems as well even though the current 8/07 release of Solaris 10 doesn't. If the custom configuration doesn't exist then it uses a default one.

The begin script (well actually another nawk script) parses the partition configuration and outputs filesys profile keywords for each slice. It also outputs SVM mirroring information for Solaris 10 if a second disk is present with the same geometry as the root disk, again, for older Solaris releases, this is done by a script on first boot.

Preserving information between begin and finish

Once the disk layout has been derived then the begin script has done its job and can exit and hand the rest of the installation over to the installer and finish script(s).

What if you want to use some of the information you just derived in your finish scripts?

You can't just export an environment variable and have it persist from the begin script to the finish script. You have to store the information somewhere that the finish script can read it from. The simplest way to do this is write them out in VAR=value format to a file under /tmp and then source this file at the beginning of your finish script. The information I keep between stages is the build, metacluster, root disk device, mirror disk device, patch cluster release date and whether the system was built from a flash archive or not.

Are we there yet?

Although I have managed to modularize most aspects of a build you can still see the complexity for a build developer in maintaining all of these files. In a future article I'll discuss a new layer on top of JASS that I have developed that consolidates all of this information into a single, easy to maintain configuration file.

Monday, 28 May 2007

One rule to bind them - part II

In part I of this article I tried reducing the number of steps a user was required to perform to get a system installed via jumpstart and addressed a couple of the goals I mentioned in Building a manageable jumpstart infrastructure using the Solaris Security Toolkit.

My ultimate goal was to reduce steps 1 through 5 and selecting from the build menu to a single interaction such as entering the jumpstart client details into a web page or a configuration file. To eliminate the build menu I had to find a way to inform the client which build it was to install onto itself. The /etc/bootparams file doesn't allow custom parameters to be passed to the client. So after some research and experimentation I switched to DHCP with my own custom client options.

I started by working out the minimum amount of information a client required. I settled on the following DHCP option definitions in dhcp_inittab(4) format:
JSBuild         SITE,           128,    ASCII,     1,      0,      sdmi
JSRevision SITE, 130, ASCII, 1, 0, sdmi
JSOSVersion SITE, 131, ASCII, 1, 0, sdmi
JSBuild
A single word such as FIREWALL, DNS, WEBSERVER etc.

JSRevision
The version of the jumpstart environment. I have all of my finish scripts and configurations under source control using subversion. This option is so that I can build hosts from a specific version of my jumpstart environment.

JSOSVersion
The OS version e.g. Solaris_8, Solaris_9, Solaris_10.
Both JSRevision and JSOSVersion are used on the DHCP server side to craft the paths of a number of jumpstart specific DHCP client options.

These new DHCP options were added to the dhcp inittab in the OS install miniroot:
/srv/install/OS/Solaris_10/Solaris_10/Tools/Boot/etc/dhcp/inittab
This was necessary to allow me to run '/sbin/dhcpinfo JSBuild' or '/sbin/dhcpinfo 128' in my begin script.

I chose the ISC DHCPD for my DHCP server as it is extremely easy to configure and very flexible. The dhcpd.conf is:
#
# DHCP Configuration
#

pid-file-name "/var/run/dhcpd.pid";
lease-file-name "/srv/dhcp/dhcpd.leases";

ping-check true;
ddns-update-style none;
authoritative;

default-lease-time 86400;
max-lease-time 86400;

# MY Custom Options
option space MY;
option MY.jumpstart-build code 128 = text;
option MY.jumpstart-revision code 130 = text;
option MY.jumpstart-osversion code 131 = text;

# SUN's Jumpstart DHCP Vendor options
option space SUNW;
option SUNW.root-mount-options code 1 = text;
option SUNW.root-server-address code 2 = ip-address;
option SUNW.root-server-hostname code 3 = text;
option SUNW.root-path-name code 4 = text;
option SUNW.swap-server-address code 5 = ip-address;
option SUNW.swap-file-path code 6 = text;
option SUNW.boot-file-path code 7 = text;
option SUNW.posix-timezone-string code 8 = text;
option SUNW.boot-read-size code 9 = unsigned integer 16;
option SUNW.install-server-address code 10 = ip-address;
option SUNW.install-server-hostname code 11 = text;
option SUNW.install-path code 12 = text;
option SUNW.sysidcfg-path code 13 = text;
option SUNW.jumpstart-cfg-path code 14 = text;
option SUNW.terminal-type code 15 = text;
option SUNW.boot-uri code 16 = text;
option SUNW.http-proxy code 17 = text;

subnet 192.168.1.0 netmask 255.255.255.0 {
authoritative;
deny unknown-clients;
next-server jumpserver;
server-name "jumpserver";
option tftp-server-name "jumpserver";
option domain-name "example.com";
option routers 192.168.1.254;
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.1.255;
option domain-name-servers 192.168.1.1, 192.168.1.2;
option ntp-servers 192.168.1.1, 192.168.1.2;

option SUNW.root-server-address jumpserver;
option SUNW.root-server-hostname "jumpserver";
option SUNW.posix-timezone-string "Australia/NSW";

# default OS is Solaris 10
option MY.jumpstart-osversion "Solaris_10";
}
#
# Solaris
#
class "SUNW" {
match if substring(option vendor-class-identifier, 0, 4) = "SUNW";

#
# Serve the correct inetboot file to sun4v hardware platforms.
#
# Note: T2000 is actually SUNW.Sun-Fire-T200
#
if option vendor-class-identifier = "SUNW.Sun-Fire-T1000" or
option vendor-class-identifier = "SUNW.Sun-Fire-T200" {
filename = concat ("inetboot.SUN4V.",
config-option MY.jumpstart-osversion, "-1");
} else {
filename = concat ("inetboot.SUN4U.",
config-option MY.jumpstart-osversion, "-1");
}

option dhcp-parameter-request-list 1,3,6,12,15,42,43,128,129,130;

site-option-space "MY";

vendor-option-space SUNW;

option SUNW.terminal-type "vt100";
option SUNW.root-mount-options "rsize=32768";
option SUNW.install-path = concat("/srv/install/OS/",
config-option MY.jumpstart-osversion);
option SUNW.install-server-address = config-option SUNW.root-server-address;
option SUNW.install-server-hostname = config-option SUNW.root-server-hostname;

# the path to the miniroot
option SUNW.root-path-name = concat(config-option SUNW.install-path,
"/", config-option MY.jumpstart-osversion, "/Tools/Boot");

# the path to correct the version of the jumpstart scripts
option SUNW.jumpstart-cfg-path = concat(config-option SUNW.root-server-hostname,
":/srv/jass/", config-option MY.jumpstart-revision);

# the path to the OS specific sysidcfg file
option SUNW.sysidcfg-path = concat(config-option SUNW.jumpstart-cfg-path,
"/Sysidcfg/", config-option MY.jumpstart-osversion);

# there is always a symlink in /srv/jass to the latest release.
option MY.jumpstart-revision "latest";
}

# Solaris host declarations
include "/srv/dhcp/hosts/solaris";
A host declaration typically looks like this:
host testfw01 {
hardware ethernet 8:0:20:ab:cd:e1;
fixed-address 192.168.1.10;
option MY.jumpstart-build "FIREWALL";
option MY.jumpstart-revision "2.555";
option MY.jumpstart-osversion "Solaris_9";
}
I include the host declarations from a separate file so that I can assign group privileges to users to edit that file only or generate it from the information the user enters into a Django web front-end to a database.

Now on the client I can drop down to the ok prompt and type:
ok boot net:dhcp -s
and when it gets to the root prompt run:
# /sbin/dhcpinfo JSBuild
FIREWALL
# /sbin/dhcpinfo JSRevision
2.555
There you have it. The client now knows which build it is meant to be. You'll notice that I haven't actually saved the user any work yet. In the next part of this series I'll demonstrate how I use this information in the begin script to select a jumpstart profile and pass this information to the JASS driver for post-installation processing.