Helio/net-imager

From FBSD_tips

Jump to: navigation, search

Please read the parent article before continuing.

WARNING: THIS ARTICLE IS A DRAFT. STEPS MAY BE INCORRECT OR DESTRUCTIVE.

WARNING: THESE METHODS ARE DESTRUCTIVE. BACKUP ALL DATA BEFORE YOU START, OR IT WILL BE LOST

Contents

[edit] Programs

To create this product, you'll need to following binaries (and their libs) from /usr/bin or /usr/sbin:

  • grep
  • awk
  • head
  • cut
  • uniq

You'll also need the following ports, with these binaries installed:

  • mbone/udpcast
    • udp-sender
    • udp-receiver

[edit] Setting some sane defaults

This starts off with the same /etc/rc as we created previously, however, we first create some useful environment variables to use as defaults:

export RATE=90m
export PRODUCT="$(kenv -q smbios.planar.product | awk '{ print $0; }')"
export SERIAL="$(kenv -q smbios.system.serial | awk '{ print $0; }')"
export PRODUCTNAME="$(kenv -q smbios.system.maker) $(kenv -q smbios.system.product)"
export PRODUCTNAME=$(echo ${PRODUCTNAME} | awk '{ print $0; }')

This sets our default packet rate to 90 megabit (a quirk in the multicast udp implementation we have, requires it), as well as setting some variables about the machine, for possible use later.

Now, we'll add a routine that gives us all the device nodes of class 'disk' that we expect to use:

get_disk () {
       echo $(geom disk status -s | awk '{ print $1; }' | grep -v afd | grep -v cd | grep -v acd | grep -v fd)
}

and set DISK to the first result:

export DISK=$(get_disk | awk ' { print $1; }')

Let's add a routine, like get_disk, for NIC's. Note we filter out the loopback, and various point to point virtual devices. Odds are you'd never use them in an imaging situation, and they tend to end up high on the list (so the default could be slip0, which is unusable with just a dhclient. If you need these methods, it's up to you to add the glue to configure them:

list_if () {
  for i in `ifconfig -l`
       do
               case ${i} in
                        lo*)
                       ;;
                        plip*)
                       ;;
                        slip*)
                       ;;
                        fwe*)
                       ;;
                        ppp*)
                       ;;
                       *)
                               echo "${i}"
                       ;;
               esac
        done
}

Now, let's first set IF to the interface used to PXE boot, and if it does not exist, set it to the first result from list_if.

IF=`kenv -q boot.netif.name`
if [ "${IF}" = "" ] ; then
       IF=$(list_if | head -n 1)
fi
export IF

Then we use dhclient to configure it:

dhclient ${IF}

[edit] Presentation

Every good product needs to have a halfway good presentation, and it does not involve bells and whistles. Merely making the information available in a sane way. These two routines do just that:

This one shows a 'pretty print' name of a NIC, which can be more useful for a user then 'fxp0':

pretty_if () {
       echo "($(dmesg | grep ${1} | grep device | cut -d "<" -f 2 | cut -d ">" -f 1 | uniq))"
}

This routine prints a rather verbose header (the empty echo lines are to emulate 'clear', so that this script works on extremely dumb terminals). Some of the information provided to you is the machines name, manufacturer, and serial number (for administrative purposes), the NIC it will use, and the disk it will use. There is room for improvement here, for example, returning the MAC for the NIC and the size and model of the disk drive.

print_header () {
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo ""
       echo "BSD Imager ${VERSION}"
       echo "Model: ${PRODUCT} \"${PRODUCTNAME}\""
       echo "Serial: ${SERIAL}"
       echo "NIC: ${IF} $(pretty_if ${IF})"
       echo "Target Disk: ${DISK}"
       echo "Current Rate Limit: ${RATE}"
       echo "------------------------------------"
}

[edit] A helpful little automater

Right now we'll put in a function called receive-image, that implements a basic image receiving routine; It will be used for 'unattended' installations:

receive-image() {
       udp-receiver --interface ${IF} -f /dev/${DISK}
}

[edit] The big routine

First, we check to see if 'unattended' is set in the kernel's environment.

if [ "$(kenv -q unattended)" = "" ] ; then

This allows us to implement a boot menu option for 'unattended' mode, or have it as a static option in loader.conf.

Now, we create an infinite loop to wrap around the main menu:

 until [ "${PATH}" = "1" ]
 do

We print our pretty little header:

       print_header

And supply all of the options available:


       echo " 1 -  Send image"
       echo " 2 -  Recieve image"
       echo " 3 -  Change Target Disk"
       echo " 4 -  Change Rate Limit (requires some tweaking)"
       echo " 5 -  Change NIC"
       echo " 6 -  Install FreeBSD"
       echo " z -  Wipe Disk"
       echo " s -  Drop to Shell"
       echo " q -  Reboot"

Now we get an option using read:

       echo ""
       echo ""
       read -p "Enter choice: " choice

We use choice to determine our action via a generic 'case' statement:

       case "${choice}" in 

These are the two simplest options, q reboots, and s forks tcsh for commands (I find being able to fork tcsh to be a requirement for most products, as they allow you to test in the environment directly)

                       q)
                               reboot ;;
                       s)
                               tcsh ;;

Now, the actual sending and receiving of disk images. udp-sender is the multicast UDP program we'll use, as it's intended for use in these situations (they even have a CD image on their website which implements some of the features we are creating, except Linux oriented instead of FreeBSD oriented):

                       1)
                               udp-sender -f /dev/${DISK} --max-bitrate ${RATE} --interface ${IF}
                               ;;
                       2)

We'll use the receive-image function we created earlier, so everything is in one place:

                               receive-image
                               ;;

The next block is for changing options, which for our products is just changing the disk, nic, and the rate cap.

                       3)
                               echo "Avaliable Disk's:"

This will give us a nice list of available disk devices the user can choose from, using the same routine we used to get the default disk:

                               get_disk
                               read -p "Enter New Target Disk [${DISK}]: " NDISK

Check to make sure it isn't empty (note, we could check to see if the disk device is in the list, however, that is left as an exercise for the user):

                               if [ "${NDISK}" != "" ] ; then
                                      DISK=${NDISK}
                               fi

Make it an environment variable:

                               export DISK
                               ;;

We'll do the same for RATE, except we don't need to give a list, just examples:

                       4)
                               read -p "Enter New Limit (ex. 90m, 56k, 1000m) [${RATE}]: " NRATE
                               if [ "${NRATE}" != "" ] ; then
                                       RATE=${NRATE}
                               fi
                               export RATE
                               ;;

Now, for IF, we go through the list returned by list_if, and use the pretty_if routine we described earlier, to show the user the logical name for an interface.

                       5)
                               echo "Available NIC's:"
                               LIST=$(list_if)
                               for i in ${LIST}
                               do
                                       echo "${i} $(pretty_if ${i})"
                               done
                               read -p "Enter NIC (ex. xl0, fxp0) [${IF}]: " NIF
                               if [ "${NIF}" != "" ] ; then
                                       IF=${NIF}
                               fi
                               export IF
                               ;;

And we'll add a simple and fast disk eraser system for creating 'clean' images.

                       z)
                               echo "Wiping Disk, May take hours.."
                               dd if=/dev/zero bs=16m of=/dev/${DISK}
                               ;;

And a wrapper for the freebsd_install command:

                       6)
                               freebsd_install
                               ;;

We'll fall back to doing nothing, as any default is potentially destructive:

                       *) ;;
       esac

Simple pause to view output.

       echo ""
       echo ""
       echo ""
       echo "Press Enter to Continue"
       read BLAH
 done

Now, for what we do if 'unattended' is set:

else
       print_header
       receive-image
fi

And to keep init from throwing errors from our lack of an /etc/ttys file:

reboot

[edit] A Naive FreeBSD Cloning Script

freebsd_install () {
 until [ "${PATH}" = "1" ]
 do
       print_header
       echo " 1 -  Send image"
       echo " 2 -  Recieve image"
       echo " 3 -  Change Target Disk"
       echo " 4 -  Change Rate Limit (requires some tweaking)"
       echo " 5 -  Change NIC"
       echo " q -  Back to main"
       echo ""
       echo ""
       read -p "Enter choice: " choice
       case "${choice}" in 
                       1)
                               mount /dev/${DISK}s1a /mnt
                               cd /mnt
                               tar -cpvf - * | udp-sender --mcast-all-addr ${ADDRESS} --max-bitrate ${RATE} --interface ${IF}
                               cd ..
                               umount /mnt
                               ;;
                       2)
                               fdisk -IB /dev/${DISK}
                               bsdlabel -wB /dev/${DISK}s1
                               LABEL=$(dd if=/dev/urandom bs=16m count=2 | md5 | cut -b 1-31)
                               newfs -L ${LABEL} /dev/${DISK}s1a
                               mount /dev/${DISK}s1a /mnt
                               udp-receiver --mcast-all-addr ${ADDRESS} --interface ${IF} | tar -xpf - -C /mnt
                               dd if=/dev/zero of=/mnt/swapfile bs=16m count=32
                               echo "/dev/ufs/${LABEL} /       ufs     rw      1       1" >/mnt/etc/fstab
                               echo "swapfile=\"/swapfile\"" >>/mnt/etc/rc.conf
                               echo "geom_label_load=\"YES\"" >> /mnt/boot/loader.conf
                               grep -v hostname /mnt/etc/rc.conf /mnt/etc/rc.conf.new
                               mv /mnt/etc/rc.conf.new /mnt/etc/rc.conf
                               echo "hostname=\"\$\(kenv -q system.planer.product\)\"" >> /mnt/etc/rc.conf
                               cp /mnt/boot/boot /var/
                               cp /mnt/boot/mbr /var/
                               umount /dev/${DISK}s1a                          
                               bsdlabel -B -b /var/boot /dev/${DISK}s1
                               fdisk -B -b /var/mbr /dev/${DISK}
                               ;;
                       q)
                               return ;;
                       3)
                               echo "Avaliable Disk's:"
                               get_disk
                               ODISK=${DISK}
                               read -p "Enter New Target Disk [${ODISK}]: " DISK
                               if [ "${DISK}" = "" ] ; then
                                       DISK=${ODISK}
                               fi
                               ;;
                       4)
                               ORATE=${RATE}
                               read -p "Enter New Limit (ex. 90m, 56k, 1000m) [${ORATE}]: " RATE
                               if [ "${RATE}" = "" ] ; then
                                       RATE=${ORATE}
                               fi
                               ;;
                       5)
                               OIF=${IF}
                               echo "Available NIC's:"
                               LIST=list_if
                               for i in ${LIST}
                               do
                                       echo "${i} $(pretty_if ${i})"
                               done
                               read -p "Enter NIC (ex. xl0, fxp0) [${OIF}]: " IF
                               if [ "${IF}" = "" ] ; then
                                       IF=${OIF}
                               fi
                               ;;
                       i)
                               OADDRESS=${ADDRESS}
                               read -p "Enter New Image Group (ex. 224.0.0.1, 224.16.2.3) [${OADDRESS}]: " ADDRESS
                               if [ "${ADDRESS}" = "" ] ; then
                                       ADDRESS=${OADDRESS}
                               fi
                               ;;
                       *) ;;
       esac
       echo ""
       echo ""
       echo ""
       echo "Press Enter to Continue"
       read BLAH
 done 
}

[edit] Download

This is actually a listing of an already existing system, which can be found [[1]]

Note I do not support it, as it has some bugs, and is as of right now, unmaintained. At some point I will maintain it again, however, it is mostly functional.

Personal tools