Wednesday, July 19, 2006

How small can you make Open Solaris - Part 4

Below, I have posted the new version of the Quick and Dirty Solaris Installer. This version will install Open Solaris directly onto a zfs filesystem. It took me a little longer than expected as I was having problems with the create_ramdisk.ksh script which is used by bootadm. The problem is that create_ramdisk.ksh does a 'du' on every file it is copying to the ramdisk to calculate the space required. If the 'du' command is used on a compressed file, it will return the compressed size. The problem comes about when a UFS filesystem is used to create the boot_archive image. Files copied from a compress zfs volume, will soon fill the ramdisk.

Not being able to create a boot archive is not good. The current work around is to turn compression off for the root filesystem. In the next update, I will modify the script to start excluding packages, so we can see what is needed and what is not.


#!/bin/bash
#
# Quick and Dirty Solaris installer
# Version 0.2
#
PROD=/cdrom/sol_11_x86/Solaris_11/Product
# PROD=/a/Solaris_11/Product
TOK=${PROD}/.clustertoc
ORDER=${PROD}/.order

# Current metaclusters are
# SUNWCXall - Entire Distribution plus OEM support
# SUNWCall - Entire Distribution
# SUNWCprog - Developer System Support
# SUNWCuser - End User System Suppor
# SUNWCreq - Core System Support
# SUNWCrnet - Reduced Networking Core System Support
# SUNWCmreq - Minimal Core System Support
METACLUSTER=SUNWCrnet

SWAP=/dev/dsk/c0d0s1
LOG=/tmp/install.log
SVCPROFILE=generic_limited_net.xml

#
# Change these to relect your system.
#
FS=zfs
[ "${FS}" != "zfs" -a "${FS}" != "ufs" ] && {
printf "Unknown filesystem ${FS}\n"
exit 1
}

[ "${FS}" = "zfs" ] && {
ROOTDEV=intdisk/snv43_zfs
RAWROOTDEV=-
GRUBFS=/dev/dsk/c0d0s0
ZFSBOOTARCHIVE=/grub/boot/boot_zfs
ZFSCOMPRESS=off
# ZFSCOMPRESS=on
MOB=yes
}

[ "${FS}" = "ufs" ] && {
ROOTDEV=/dev/dsk/c0d0s3
RAWROOTDEV=${ROOTDEV/dsk/rdsk}
MOB=no
}

typeset -a pkgs

#
# Solaris packages need to be installed in the correct order.
# The .order file contains all the packages in the correct
# installation order
#
function reorder_pkgs() {
typeset -a pkglist=( ${pkgs[@]} )
pkgcnt=0

while read order_pkg ; do
for i in ${pkglist[@]} ; do
[ "$order_pkg" = "${i%.i}" ] && {
pkgs[$(( pkgcnt++ ))]="${i}"
printf "."
}
done
done < ${ORDER}
}

#
# This function builds a list of packages in a cluster
# If there is a cluster within a cluster, it will call itself to
# resolve all the packages.
#
# Before calling make sure you initialize pkgcnt to 0
# Arg: $1 contains the cluster name
# Affected vars: pkgs, pkgcnt
#
function get_pkg_list() {
local IFS="="
local print_on=0
local cluster=$1

while read arg1 arg2
do
[ "${arg1}" = "END" -a "${print_on}" = "1" ] && break;
[ -z "${arg2}" ] && continue;

[ "${arg2}" = "${cluster}" ] && {
print_on=1
continue
}

[ "${print_on}" = "1" -a "${arg1}" = "SUNW_CSRMEMBER" ] && {
ifcluster=`expr "${arg2}" : '\(SUNWC\)'`
if [ "${ifcluster}" = "SUNWC" ]; then
get_pkg_list ${arg2}
else
[ -d ${PROD}/${arg2} ] && {
pkgs[$(( pkgcnt++ ))]="${arg2}"
printf "."
continue;
}

arg2="${arg2}.i"
[ -d ${PROD}/${arg2} ] && pkgs[$(( pkgcnt++ ))]="${arg2}"
printf "."
fi
}
done < ${TOK}
}

#
# Check for the installation image before proceeding
#
[ ! -d ${PROD} ] && {
echo "Cannot find Solaris Installation"
exit 1
}

#
# Create a pkg admin file - see man admin(4)
#
sed 's/ask/nocheck/' /var/sadm/install/admin/default > /tmp/.admin.doit

#
# Build an ordered list of packages from the Solaris installation image
#
printf "Building a list of packages "
pkgcnt=0
get_pkg_list ${METACLUSTER}
echo
printf "Sorting packages into the correct order for installation "
reorder_pkgs
echo

#
# Try to create the filesystem you define at the beginning
#
case ${FS} in
"zfs") zfs create ${ROOTDEV} || {
echo "Cannot create zfs filesystem!"
exit 1
}
zfs set mountpoint=legacy ${ROOTDEV}
zfs set compression=${ZFSCOMPRESS} ${ROOTDEV}
;;
"ufs") newfs ${ROOTDEV} || {
echo "Cannot create ufs filesystem!"
exit 1
} ;;
*) echo "Cannot create ufs filesystem!"
exit 1
;;
esac

mount -F ${FS} ${ROOTDEV} /mnt || {
echo "Cannot mount filesystem!"
exit 1
}

#
# Install packages from Solaris installation image
#
echo "Starting installation of packages"
echo
(
for i in ${pkgs[@]} ; do
pkgadd -n -a /tmp/.admin.doit -d ${PROD} -R /mnt $i
done
) > ${LOG}

#
# Update /etc/vfstab with swap and root partitions
#
(
printf "${SWAP}\t-\t-\tswap\t-\tno\t-\n"
printf "${ROOTDEV}\t${RAWROOTDEV}\t/\t${FS}\t1\t${MOB}\t-\n"
[ "$FS" = "zfs" ] && {
mkdir -m 0755 /mnt/grub
printf "${GRUBFS}\t${GRUBFS/dsk/rdsk}\t/grub\tufs\t3\tyes\t-\n"
}
) >> /mnt/etc/vfstab

#
# Copy links for disk partitions in /dev/dsk and /dev/rdsk
# This is needed so the system can find the root partion on boot
#
( cd /dev && find dsk rdsk -depth | cpio -pdm /mnt/dev 2>/dev/null )

#
# Configure system to initialize identity on first boot
# If there is a sysidcfg file in the current directory. This will
# be copied across.
#
PROFILEDIR=/mnt/var/svc/profile
[ -f ${PROFILEDIR}/${SVCPROFILE} ] && {
if [ -f ./sysidcfg ]; then
cp ./sysidcfg /mnt/etc
else
touch /mnt/etc/.UNCONFIGURED
fi
cp -p ${PROFILEDIR}/${SVCPROFILE} ${PROFILEDIR}/generic.xml
}

#
# set bootpath to root filesystem.
# Also set the console to text
#
(
[ "${FS}" = "ufs" ] && {
BOOTPATH=$( ls -l ${ROOTDEV} | nawk '{print $11}' |
sed -e 's#[./]*/devices/#/#' )

printf "setprop bootpath ${BOOTPATH}\n"
}
printf "setprop console 'text'\n"
) >> /mnt/boot/solaris/bootenv.rc

#
# If found execute local script before /mnt is unmounted
#
[ -x ./local_install.bash ] && ./local_install.bash

#
# Finish off installation
#
[ -f /etc/zfs/zpool.cache ] && {
cp -p /etc/zfs/zpool.cache /mnt/etc/zfs
echo "etc/zfs/zpool.cache" >> /mnt/boot/solaris/filelist.ramdisk
}

#
# Configure for a ZFS boot. At the moment you need a small UFS partition
# somewhere for grub.
#
[ "${FS}" = "zfs" ] && {
(
printf "rootfs:zfs\n"
printf "zfsroot:${ROOTDEV}\n"
) >> /mnt/etc/system
}

devfsadm -r /mnt
rm -f /mnt/reconfigure
bootadm update-archive -R /mnt

[ "${FS}" = "zfs" ] && {
cp /mnt/platform/i86pc/boot_archive ${ZFSBOOTARCHIVE}
cp -p /mnt/sbin/bootadm /mnt/sbin/bootadm.real
cat >/mnt/sbin/bootadm << EOM
#!/usr/bin/sh

/sbin/bootadm.real "\$@"
if [ "\$1" = "update-archive" -a -d /grub/boot/grub ]; then
/usr/bin/cp /platform/i86pc/boot_archive ${ZFSBOOTARCHIVE}
fi
exit 0
EOM
}

echo
echo "If you have not already, you will need to configure menu.lst to"
echo "boot this partition."

umount /mnt
# eject cdrom

1 comment:

Anonymous said...

first thing: thanks for your AWESOME posts.
second thing: I'm doing experiments with ramdisks too, mainly with schillix's one because it just seems smaller and more polite to me (I'm more used to *bsd than to solaris...) and I can't understand how they can make a root ramdisk that has still FREE space on it, when I try to modify a ramdisk it always came up 100% full in the end, so the system comes up and runs ok, but it keeps yelling at me about disk being full :P

any hints on that? =)

(all this experimenting is just because I'd love to be able to build a simple nfs server with like 4 cheap sata2 300gb disks in raidz with files on zfs and the root and the system on the usb key in read only, I'm trying with an external usb2 drive and it's working good in read write...)