BrickstorOS Installer Utility for hosted installs both virtual and physical.
#!/bin/sh
# Copyright 2009-2018 RackTop Systems Inc. and/or its affiliates.
# http://www.racktopsystems.com
#
# The methods and techniques utilized herein are considered TRADE SECRETS
# and/or CONFIDENTIAL unless otherwise noted. REPRODUCTION or DISTRIBUTION
# is FORBIDDEN, in whole and/or in part, except by express written permission
# of RackTop Systems.
#
# @@DESCRIPTION@@ Script used to install BrickstorOS to disk device
# @@NAME@@ osinstall
# @@STABILITY@@ unstable
# @@VERSION@@ 1.1.10
# Run this script as follows. To set device via an environment variable, prefix
# this command with ENV_BSROS_BOOT_DEV=c2t1d0, assuming device is c2t1d0.
# Otherwise if the device path is not same as default c2t1d0, add a `-b` (boot)
# argument to the command.
#
# Example with environment variable:
# ENV_BSROS_BOOT_DEV=c10t0d0 \
# /path/to/osinstall /rpool/images/tmp/<some guid>.rap
#
# or, if environment variable has already been set previously:
# /path/to/osinstall /rpool/images/tmp/<some guid>.rap
#
# Example with -b:
# /path/to/osinstall -b c10t0d0 /rpool/images/tmp/<some guid>.rap
#
# Keep DEBUG at 0, otherwise set to 1 or above to get much more output.
DEBUG=0
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
PROGNAME=`basename $0`
PREFIX=/dev/rdsk
OSVER=`uname -s`
# Override device below with $ENV_BSROS_BOOT_DEV variable
DEFAULT_DEV=c2t1d0
ROOT=bp/ROOT
# These variables may be overriden by environment variables, when set on same
# line as running the command, or set as part of the environment, or
# via config file, which is nothing more than a one-pair per line of:
# <VAR>=<VALUE> mappings. Comments are perfectly fine as well.
# A config file may look like this:
# *cfg*
# BD=c1t10d0
# TMP_WORK_AREA=/var/tmp
# SRCDIR=/bp/images
#
# First line of the config should have __cfg__ commented out.
# This is a string that we use in effect as a magic number or shabang
# to confirm that this is a valid config, which otherwise could be anything.
# The goal is to avoid blindly sourcing just anything.
#
SRCDIR="${ENV_SRCDIR:-/rpool/images}"
# When we are booting a live CD/DVD, we won't have a mounted pool at all.
# In those instances we should assume that we will create a volatile dir.
# in /tmp. It will of course be gone after reboot.
NOPERSIST_DIR="/tmp/nopersist"
OSINSTALL_CONF="${ENV_OSINSTALL_CONF:-/root/osi.conf}"
BD="${ENV_BSROS_BOOT_DEV:-$DEFAULT_DEV}"
MNT_DIR="${ENV_MNT_DIR:-/mnt}"
OS_IMAGE=
SRC_RAP_PKG=
TMP_WORK_AREA="${ENV_TMP_WORK_AREA:-/tmp/.osinstall}"
ALT_ROOT="${ENV_ALT_ROOT:-/t}"
FORMAT_PARAMS_FILE="${TMP_WORK_AREA}/format.params"
# Grub files need to come from somewhere. At the moment we cheat and we place
# correct files on the host system doing installation. We could just as well
# use host-symstem supplied versions of these files.
SRC_GRUB_FILES="${ENV_SRC_GRUB_FILES:-${TMP_WORK_AREA}/boot/grub}"
GRUB_MENU_FILE="${ALT_ROOT}/bp/boot/grub/menu.lst"
#
# Global variables
#
remove_rap_pkg=
#
# process_config_file: If there is a config file present, use it to alter
# values that are otherwise already set to some sane default.
#
function process_config_file {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
# A lousy check, but it is good enough. We don't want to blindly
# source just anything. If the first line of the config does not have a
# commented out __cfg__ entry, this file won't be sourced.
if [[ -f "${OSINSTALL_CONF}" && \
`awk '{if(NR==1) {if (match($0, "\\\B.__(cfg)__")) { print 1 } } }' \
< "${OSINSTALL_CONF}"` -eq 1 ]]; then
. "${OSINSTALL_CONF}"
else
return
fi
# Because these will not dynamically change if $TMP_WORK_AREA or $ALT_ROOT
# are modified here, we want to make sure that they are reset here.
# Environment variable, if exists, still has priority, with $TMP_WORK_AREA
# as fallback.
SRC_GRUB_FILES="${ENV_SRC_GRUB_FILES:-${TMP_WORK_AREA}/boot/grub}"
GRUB_MENU_FILE="${ALT_ROOT}/bp/boot/grub/menu.lst"
}
function usage {
printf "%s\n" "${PROGNAME} BrickstorOS Portable Installer Utility"
printf "%s\n" \
"" \
"Description:" \
"Utility for installation of BrickstorOS to physical media." \
"" \
"The tool is meant to be run on a host system with a temporarily attached" \
"disk drive or more likey a SSD or SATADOM." \
"" \
"This device is prepared by being made bootable first." \
"Pool named {bp} is created and configured as required by BrickstorOS." \
"" \
"Contents of RAP package are extracted and sent to temporarily attached" \
"device using zfs send/recv method, which makes ZFS a pre-requisite " \
"on the host system." \
"" \
"Usage: ${PROGNAME} [-b <c#t#d#>] <${SRCDIR}/guid/guid.rap>" >&2
printf "%s\n" \
"Command requires only one positional parameter, path to RAP package." \
"If target device is not given as argument: -b <dev>," \
"and no config file <${OSINSTALL_CONF}> is found," \
"we use ${DEFAULT_DEV} automatically." \
"" >&2
printf "%s\n" "Optional Arguments:" >&2
printf " %-24s %s\n" \
"-b | --bootdev <dev>" \
"Name of device as reported by iostat, i.e.: ${DEFAULT_DEV}, no slices" \
"-r | --remove-rap" \
"If set, remove the RAP package after installation" \
"" >&2
printf "%s\n" "Environment Variables:" >&2
printf "\t%s\n" "ENV_BSROS_BOOT_DEV - equivalent to -b|--bootdev" >&2
exit 1
}
function write_err {
printf "[ERROR] %s\n" "$@" >&2
exit 1
}
function write_info {
printf "[INFO] %s\n" "$@"
}
function write_warn {
printf "[WARN] %s\n" "$@" >&2
}
function bp_is_mounted {
zpool list -Honame bp >/dev/null 2>&1 ; return $?
}
function device_not_found {
write_err "Unable to validate this disk: ${BD} please confirm disk exists."
}
function preinstall_tasks {
if ! `mkdir -p "${TMP_WORK_AREA}"` ; then
write_err "Failed to create temporary work area ${TMP_WORK_AREA}!"
return 1
fi
}
#
# postinstall_tasks: At the end of the script before any housekeeping is taking
# place we may want to add tasks that require newly built pool to still be
# imported and online. This function is meant for these tasks.
#
function postinstall_tasks {
return
}
#
# cleanup: At the end of the script do necessary housekeeping
#
function cleanup {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
write_info "Exporting pool and cleaning-up host system."
if ! zpool export bp; then
zpool export -f bp
write_warn "Normal export did not succeed, used force to export bp."
fi
# Drop temporary work area
rm -rf "${TMP_WORK_AREA}"
# Drop RAP package if asked, otherwise it is left alone.
if [ "${remove_rap_pkg}" -eq 1 ]; then
rm "${SRC_RAP_PKG}"
fi
}
#
# zero_out_pt: Remove any partitions found on the device before building new
# layout on it. This assures consistent state of a device being built.
#
function zero_out_pt {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
write_info "Zeroing out existing partitions on ${BD}."
device="${PREFIX}/${BD}p0"
for partition in \
`fdisk -W- "${device}" | \
awk '!/\*/ { if($1 > 0 && $10 > 0) {
printf("%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", $1,$2,$3,$4,$5,$6,$7,$8,$9,$10)}}'` ; do
fdisk -D "${partition}" "${device}"
if [[ $? -ne 0 ]]; then
write_err "Failed to delete already existing partition. Please check!"
else
write_info "Deleted partition ${partition} on device ${device}."
fi
done
}
#
# validate_prereqs: Check that basic system requirements are met,
# such as existence of required tools, which may or may not be there.
#
function validate_prereqs {
reqs_unmet=0
typeset -a missing
[ ! -x `which fdisk` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("fdisk") ; }
[ ! -x `which installgrub` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("installgrub") ; }
[ ! -x `which cp` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("cp") ; }
[ ! -x `which zfs` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("zfs") ; }
[ ! -x `which zpool` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("zpool") ; }
[ ! -x `which prtvtoc` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("prtvtoc") ; }
if [ "${OSVER}" != "BrickstorOS" ]; then
[ ! -x `which unzip` ] && \
{ reqs_unmet=$((++reqs_unmet)) ; missing+=("unzip") ; }
fi
if [ "${reqs_unmet}" -gt 0 ] ; then
printf "%s\n" "Cannot continue. Identified ${reqs_unmet} missing tools."
printf "required: %s\n" "${missing[@]}"
return 1
fi
return 0
}
#
# prepare_new_disk: Low-level interaction with the device to which we are
# installing BrickstorOS. We create a partition table, slices and such.
#
function prepare_new_disk {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
write_info "Preparing Disk for Installation of BrickstorOS."
if [ "${ENV_SKIP_DISK_PREP}" -eq 1 ] ; then return ; fi
device="${PREFIX}/${BD}p0"
zeroed=0
# Does this disk need to be cleared?
result=`fdisk -W- "${device}" | \
awk '{
if($1 != "*" && $1 != "")
{
if($1 != 0) {print 1}
}
}'`
if [[ "${result}" -eq "1" ]]; then
zero_out_pt && zeroed=1
else
zeroed=1
fi
if [[ $? -ne 0 ]]; then
write_err "Failed to zero out device ${BD}, please do so manually."
else
write_info "Successfully zeroed out device ${BD}."
fi
# Apply partition table over the now hopefully clean disk.
[[ "${zeroed}" -eq 1 ]] && fdisk -B "${device}"
end=$(( `prtvtoc "${PREFIX}/${BD}s0" \
| awk '/accessible cylinders/ {print $2}'` + -1 ))
cat > "${FORMAT_PARAMS_FILE}" <<EOF
p
0
root
wm
1
${end}c
label
EOF
# Make sure we had a chance to sync things and update device links.
sync
# Setup slice information and save it. Format is messing with us, so we
# return 0 here, ignoring format's return code. This should be addressed.
if [ "${DEBUG}" -gt 0 ] ; then
format -d "${BD}" -f "${FORMAT_PARAMS_FILE}"
else
format -d "${BD}" -f "${FORMAT_PARAMS_FILE}" > /dev/null 2>&1
fi
return 0
}
function create_pool {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
write_info "Preparing boot pool for Installation of BrickstorOS."
# Create pool and setup necessary bits for next step, installation.
zpool create -f -oaltroot=/t bp "${BD}s0" \
&& zfs create -p "${ROOT}" \
&& zfs set mountpoint=legacy "${ROOT}"
if [[ $? -ne 0 ]]; then
write_err "All or part of pool setup failed. Please inspect manually!"
fi
return $?
}
#
# prepare_persistent_datasets: Creates necessary datasets and stuffs
# them with initial seed data from the boot archive.
#
function prepare_persistent_datasets {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
mkdir -p "${TMP_WORK_AREA}" "${MNT_DIR}/bp" "${MNT_DIR}/boot_archive" || \
{
write_err "Unable to create work area or mountpoints."
}
# Mount filesystem extracted from RAP package, then mount boot archive
# and extract data out of `/etc` and `/var`, populating newly created
# `bp/etc` and `bp/var` in the process.
mount -F zfs -o ro "${BOOTFS}" "${MNT_DIR}/bp"
mount -F ufs -r \
"${MNT_DIR}/bp/platform/i86pc/amd64/boot_archive" \
"${MNT_DIR}/boot_archive"
for ds in etc var ; do
zfs create \
-o compression=lz4 \
-o logbias=throughput \
-o mountpoint="/${ds}" "bp/${ds}"
if [ "${DEBUG}" -gt 0 ]; then
/usr/bin/cp -a \
${MNT_DIR}/boot_archive/${ds}/* "${ALT_ROOT}/${ds}/"
else
/usr/bin/cp -a \
${MNT_DIR}/boot_archive/${ds}/* "${ALT_ROOT}/${ds}/" > /dev/null
fi
done
# Before we umount stuff, we copy grub files from the boot archive. We
# will use these files later when we install grub making device bootable.
mkdir -p "${TMP_WORK_AREA}/boot"
if [ "${DEBUG}" -gt 0 ]; then
/usr/bin/cp -r ${MNT_DIR}/bp/boot ${TMP_WORK_AREA}
else
/usr/bin/cp -r ${MNT_DIR}/bp/boot ${TMP_WORK_AREA} > /dev/null 2>&1
fi
# Drop mounted filesystems, we are done with them.
umount "${MNT_DIR}/boot_archive" ; umount "${MNT_DIR}/bp"
}
function install_image {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
img="${SRCDIR}/${OS_IMAGE}/${OS_IMAGE}"
write_info "Writing contents of package ${OS_IMAGE} to ${BOOTFS} on ${BD}"
# If file exists, write contents of the image over the boot device,
# otherwise fail with a message about missing image.
if [[ ! -f "${img}" ]]; then
write_err \
"Unable to locate ${img}! Please, confirm this file was staged."
else
dd if="${img}" bs=1M 2>/dev/null | zfs recv "${BOOTFS}"
if [[ $? -eq 0 ]]; then
# Set bootfs prop on bp, otherwise pool will not boot correctly.
zpool set bootfs="${BOOTFS}" bp
write_info "Successfully wrote OS image to ${BOOTFS}."
else
write_err "Failed to write OS image to device ${BD}!"
fi
fi
}
#
# copy_boot_files: Transfer necessary files for device to bootstrap
# correctly from the temporary work area to the device.
#
function copy_boot_files {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
bproot="${ALT_ROOT}/bp"
grubdir="${ALT_ROOT}/bp/boot/grub"
# We use altroot to mount pool under /t, which makes it easier to manage
# `/etc` and `/var` creation.
if [ ${DEBUG} -gt 0 ]; then
/usr/bin/cp -r ${TMP_WORK_AREA}/boot "${bproot}/"
else
/usr/bin/cp -r ${TMP_WORK_AREA}/boot "${bproot}/" > /dev/null 2>&1
fi
umask 0222 # file under bootsign is ready-only, which says little to root. ;)
mkdir "${grubdir}/bootsign/" && touch "${grubdir}/bootsign/pool_bp"
}
#
# check_grub_files_exist: Checks for existence of grub stage files.
#
function check_grub_files_exist {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
[[ -f "${SRC_GRUB_FILES}/stage1" &&
-f "${SRC_GRUB_FILES}/stage2" ]] || return 1
}
#
# install_grub: Writes grub bootlader code into the MBR area of the device.
#
function install_grub {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
device="${PREFIX}/${BD}s0"
check_grub_files_exist || return 1
write_info "Installing GRUB bootloader to ${BD}."
# Need to make sure disk is actually bootable.
if [ "${DEBUG}" -gt 0 ]; then
installgrub "${SRC_GRUB_FILES}/stage1" "${SRC_GRUB_FILES}/stage2" \
"${device}"
else
installgrub "${SRC_GRUB_FILES}/stage1" "${SRC_GRUB_FILES}/stage2" \
"${device}" >/dev/null 2>&1
fi
return $?
}
#
# build_menu: Build grub boot menu, initially containing two entries for
# the installed OS, one for normal boot and another for SAFE MODE boot,
# which effectively means the boot pool `bp` is not imported.
#
function build_menu {
grub_title=`zfs get -H -o value brickstor:title "${BOOTFS}"`
grub_safemode_title=`sed -e 's/\[/\[SAFE MODE\//' <<< "${grub_title}"`
cat > "${GRUB_MENU_FILE}" <<EOF
#
# Automatically generated. Do not edit!
#
timeout 5
splashimage /boot/grub/splash.xpm.gz
default 0
title ${grub_title}
findroot (pool_bp,0,a)
bootfs ${BOOTFS}
kernel\$ /platform/i86pc/kernel/amd64/unix -B \$ZFS-BOOTFS
module /platform/i86pc/amd64/boot_archive type=rootfs
module /platform/i86pc/amd64/boot_archive.hash type=hash
title ${grub_safemode_title}
findroot (pool_bp,0,a)
bootfs ${BOOTFS}
kernel /platform/i86pc/kernel/amd64/unix
module /platform/i86pc/amd64/boot_archive type=rootfs
module /platform/i86pc/amd64/boot_archive.hash type=hash
EOF
}
#
# unpack_rap_package: Extract the stream from the zip file using proper tool,
# depending on the version of OS the script is running on. We should be able to
# run this on OmniOS and BrickstorOS. At the moment we are not targeting any
# other operating systems.
#
function unpack_rap_package {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
rap="${SRCDIR}/${OS_IMAGE}.rap" # Absolute path to the rap package.
case "${OSVER}" in
"BrickstorOS")
(
cd "${SRCDIR}/${OS_IMAGE}" ;
if [[ ! -f "./${OS_IMAGE}.rap" || ! -f "./HEADER" ]]; then
write_info "Unpacking RAP with GUID ${OS_IMAGE}"
/usr/racktop/bin/unpackrap "./${OS_IMAGE}.rap" > /dev/null
fi
)
return $?
;;
"SunOS")
(
cd "${SRCDIR}/${OS_IMAGE}" ;
if [[ ! -f "./${OS_IMAGE}.rap" || ! -f "./HEADER" ]]; then
write_info "Unpacking RAP with GUID ${OS_IMAGE}"
unzip -nq "./${OS_IMAGE}.rap"
fi
)
return $?
;;
*) return 1
write_err "This is not a known and supported operating system!"
;;
esac
}
#
# stage_rap_package: We want to keep all RAP packages in one area and for each
# package we want to create a separate directory, which promotes better
# organization.
#
function stage_rap_package {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
echo "SRCDIR=${SRCDIR}"
needcopy=1
# Drop leading and trailing (if any) slashes from the path to location of
# images. This path, if it were /rpool/images/ is transformed into a
# digestable ZFS-friendly form: rpool/images and used as argument to ZFS
# commands.
rapds=`
awk '{ a[0]="^/" ; a[1]="/$"
for (i in a) { gsub(a[i], "") }
} END {print $1}' <<< "${SRCDIR}"`
if ! `zpool list ${rapds%%/*}` >/dev/null 2>&1 ; then
write_warn "Pool ${rapds%%/*} does not exist, falling back to ${NOPERSIST_DIR}."
SRCDIR="${NOPERSIST_DIR}"
# There may be cases where we are running this multiple times and the
# directory is already there. In these cases we don't want to fail on
# creating an already-existing directory.
mkdir -p ${SRCDIR} || \
write_err "Failed to stage package, check capacity of ${NOPERSIST_DIR}."
else
if ! `zfs list -Honame "${rapds}" >/dev/null 2>&1`; then
zfs create "${rapds}"
fi
fi
# If the image was already placed into correct location on the host system
# we don't need to do anything more, otherwise we should move the file
# to location expected by this tool.
if [[ `dirname "${SRC_RAP_PKG}"` == "${SRCDIR}/${OS_IMAGE}" ]]; then
if [[ -f "${SRC_RAP_PKG}" ]]; then
needcopy=0
fi
elif [[ -f "${SRCDIR}/${OS_IMAGE}/${OS_IMAGE}.rap" ]]; then
needcopy=0
fi
# Copy RAP package to proper destination first, remove it from temporary
# location after copy is done.
if [[ "${needcopy}" -eq 1 ]]; then
write_info \
"Moving file to work area before extraction ${SRCDIR}/${OS_IMAGE}"
# Prepare work area and copy file there, don't unlink original file unless
# explicitly told to do so.
mkdir -p "${SRCDIR}/${OS_IMAGE}"
cp -rp "${SRC_RAP_PKG}" "${SRCDIR}/${OS_IMAGE}/${OS_IMAGE}.rap"
else
write_info \
"Skip copy of ${OS_IMAGE} RAP to workarea, package already there."
fi
# Unpack the rap package, extracting ZFS stream of BrickstorOS.
unpack_rap_package || write_err "Failed to extract data from RAP package."
}
#
# parse_cmdline_args: Process arguments passed by caller and setup variables
# used by rest of the script.
#
function parse_cmdline_args {
[[ "${DEBUG}" -gt 0 ]] && set -o xtrace
[ ${#@} -eq 0 ] && { usage ; }
# Reset all variables that might be set
fn=
bd=
verbose=0
while :; do
case $1 in
-h|-\?|--help)
usage
;;
-b|--bootdev) # Takes an option argument, argument is required.
if [ -n "$2" ]; then
bd=$2
shift
else
write_err "-b|--bootdev requires an option argument."
exit 1
fi
;;
--bootdev=?*)
bd=${1#*=} # Delete everything up to "=" and assign the remainder.
;;
--bootdev=) # Handle the case of an empty --bootdev=
write_err "-b|--bootdev requires an option argument."
exit 1
;;
-r|--remove-rap)
remove_rap_pkg=1
;;
-v|--verbose)
verbose=$((verbose + 1)) # Each -v argument adds 1 to verbosity.
;;
*/* | [a-zA-Z0-9]*) # This should be path to RAP package.
# If path is a valid file, set ${fn} to this value.
if [[ -f "$1" ]]; then fn=$1; fi
;;
--) # End of all options.
shift
break
;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*) # Default case: If no more options then break out of the loop.
break
esac
shift
done
# If we have values, which we should, after processing command line args,
# we update global variables used by other parts of the tool.
# After we are done, if "${BD}" or "${OS_IMAGE}" are still null strings,
# we bail, because there is nothing more we can do.
# We may have set "${BD}" to a value from exported environment variable, and
# if so, nothing further is necessary.
if [[ -n "${bd}" ]] ; then
BD="${bd}"
fi
[[ -n "${fn}" ]] && \
{
SRC_RAP_PKG="${fn}" ;
tmpv=`basename "${fn}"` ;
OS_IMAGE="${tmpv%.rap}" ;
BOOTFS="${ROOT}/${OS_IMAGE}" ;
}
}
#
# Process config file, if exists...
#
process_config_file
#
# Process command line arguments
#
# We need to make sure we have minimum required information from user
# in order to continue. Determine that now and break out if we don't.
#
parse_cmdline_args $@
#
# Validate basic system compatibility. If we have missing tools, we should
# catch that here and report.
#
validate_prereqs || exit 1
if bp_is_mounted; then
write_err "Cannot continue. Pool bp is already mounted!"
fi
# If these don't have values, we are done and should print out usage.
# We should not ever have a case of null "${BD}".
if [[ -z "${BD}" || -z "${OS_IMAGE}" ]]; then
[[ -z "${OS_IMAGE}" ]] && write_err \
"Path to RAP package may not be valid."
fi
preinstall_tasks || exit 1
#
# Move RAP into workarea if necessary
#
if ! stage_rap_package; then
write_err "Failed to prepare RAP package for installation to device ${BD}!"
fi
#
# Validate and prepare disk
#
# Check device exists. We should have at least the p0 partition if
# device is functional.
#
if [[ ! -h "${PREFIX}/${BD}p0" ]]; then
device_not_found
fi
#
# Make disk ready for bp
#
if ! prepare_new_disk; then
write_err "Failed to prepare disk for installation of BrickstorOS!"
else
write_info "Device is ready for BrickstorOS installation."
fi
#
# Install the OS to disk
#
if ! create_pool; then
write_err "Failed to complete setup of new pool, cannot continue!"
fi
if ! install_image; then
write_err "Failed to install image ${OS_IMAGE} to device ${BD}!"
fi
prepare_persistent_datasets
# Copy what is in /bp/boot, could be a tarball or whatever.
if ! copy_boot_files; then
write_err "Failed to copy boot files to device ${BD}!"
fi
#
# Make disk bootable
#
if ! install_grub; then
write_err "Failed to install grub. Device ${BD} will not be bootable!"
fi
#
# Build initial grub menu file based on just installed OS.
#
build_menu
#
# Cleanup before exiting
#
if ! cleanup; then
write_err \
"Post-install cleanup did not complete. Export bp manually if necessary."
fi