Compare commits

...

9 Commits

Author SHA1 Message Date
Joshua Boniface 7b3f0e5f0d Use more reliable grep method 2021-12-12 20:18:47 -05:00
Joshua Boniface 0767984729 Add interactive shell too 2021-12-12 20:05:11 -05:00
Joshua Boniface 19b96b4ade Fix bad grep 2021-12-12 19:50:00 -05:00
Joshua Boniface d17073dc7f Fix panic spawning shell 2021-12-12 19:43:43 -05:00
Joshua Boniface 64b36c0c67 Fix missing test command 2021-12-12 18:49:51 -05:00
Joshua Boniface 337fe22b29 Correct location of remove-root-pw.sh 2021-12-12 18:36:10 -05:00
Joshua Boniface 0741e5ea6b Fix typo in remove-root-pw script 2021-12-12 18:35:23 -05:00
Joshua Boniface 5642779dbf Improve TTY locking
Prefer serial consoles to TTYs, and present a timeout on graphical TTYs.
2021-12-12 16:44:46 -05:00
Joshua Boniface 0f52de5b66 Move to template format instead of cat-EOF
Simplifies the scripts and adds the ability to easily and cleanly change
the contents of these files without dealing with escape characters, etc.
2021-12-12 16:44:39 -05:00
22 changed files with 1049 additions and 923 deletions

View File

@ -63,85 +63,87 @@ while [ $# -gt 0 ]; do
esac
done
PACKAGE_LIST_MAIN="live-tools live-boot live-boot-initramfs-tools linux-image-amd64 mdadm lvm2 parted gdisk dosfstools debootstrap grub-pc-bin grub-efi-amd64 sipcalc vim ca-certificates vlan tftp-hpa curl ipmitool"
PACKAGE_LIST_NONFREE="firmware-bnx2 firmware-bnx2x"
mkdir -p artifacts/lb
pushd artifacts/lb &>/dev/null
echo "Pre-cleaning live-build environment..."
sudo lb clean
echo
echo "Initializing config..."
# Initialize the live-build config
lb config --distribution buster --architectures amd64 --archive-areas "main contrib non-free" --apt-recommends false
lb config --distribution buster --architectures amd64 --archive-areas "main contrib non-free" --apt-recommends false || fail "Failed to initialize live-build config"
echo
# Configure the "standard" live task (no GUI)
echo "live-task-standard" > config/package-lists/desktop.list.chroot
# Add additional live packages
echo ${PACKAGE_LIST_MAIN} > config/package-lists/installer.list.chroot
echo ${PACKAGE_LIST_NONFREE} > config/package-lists/nonfree.list.chroot
# Configure the package lists
echo -n "Copying package lists... "
cp ../../templates/installer.list.chroot config/package-lists/installer.list.chroot || fail "Failed to copy critical template file"
cp ../../templates/firmware.list.chroot config/package-lists/firmware.list.chroot || fail "Failed to copy critical template file"
echo "done."
# Add root password hook
echo -n "Copying live-boot templates... "
mkdir -p config/includes.chroot/lib/live/boot/
cp ../../templates/9990-initramfs-tools.sh config/includes.chroot/lib/live/boot/9990-initramfs-tools.sh || fail "Failed to copy critical template file"
chmod +x config/includes.chroot/lib/live/boot/9990-initramfs-tools.sh || fail "Failed to copy critical template file"
mkdir -p config/includes.chroot/lib/live/config/
cat <<EOF > config/includes.chroot/lib/live/config/2000-remove-root-pw
#!/bin/sh
echo "I: remove root password"
passwd --delete root
EOF
chmod +x config/includes.chroot/lib/live/config/2000-remove-root-pw
cp ../../templates/2000-remove-root-pw.sh config/includes.chroot/lib/live/config/2000-remove-root-pw.sh || fail "Failed to copy critical template file"
chmod +x config/includes.chroot/lib/live/config/2000-remove-root-pw.sh || fail "Failed to copy critical template file"
echo "done."
# Set root bashrc
echo -n "Copying root bashrc template... "
mkdir -p config/includes.chroot/root
echo "/install.sh" > config/includes.chroot/root/.bashrc
cp ../../templates/root.bashrc config/includes.chroot/root/.bashrc || fail "Failed to copy critical template file"
echo "done."
# Set hostname and resolv.conf
echo -n "Copying networking templates... "
mkdir -p config/includes.chroot/etc
echo "pvc-live-installer" > config/includes.chroot/etc/hostname
echo "nameserver 8.8.8.8" > config/includes.chroot/etc/resolv.conf
cp ../../templates/hostname config/includes.chroot/etc/hostname || fail "Failed to copy critical template file"
cp ../../templates/resolv.conf config/includes.chroot/etc/resolv.conf || fail "Failed to copy critical template file"
echo "done."
# Set single vty
# Set single vty and autologin
echo -n "Copying getty templates... "
mkdir -p config/includes.chroot/etc/systemd/
cat <<EOF > config/includes.chroot/etc/systemd/logind.conf
[Login]
NAutoVTs=2
EOF
cp ../../templates/logind.conf config/includes.chroot/etc/systemd/logind.conf || fail "Failed to copy critical template file"
mkdir -p config/includes.chroot/etc/systemd/system/getty@.service.d
cat <<EOF > config/includes.chroot/etc/systemd/system/getty@.service.d/override.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -- \\\u' --autologin root --noclear %I \$TERM
EOF
cp ../../templates/getty-override.conf config/includes.chroot/etc/systemd/system/getty@.service.d/override.conf || fail "Failed to copy critical template file"
mkdir -p config/includes.chroot/etc/systemd/system/serial-getty@.service.d
cat <<EOF > config/includes.chroot/etc/systemd/system/serial-getty@.service.d/override.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -- \\\u' --autologin root --noclear --keep-baud 115200,38400,9600 %I \$TERM
EOF
cp ../../templates/serial-getty-override.conf config/includes.chroot/etc/systemd/system/serial-getty@.service.d/override.conf || fail "Failed to copy critical template file"
echo "done."
# Install GRUB config, theme, and splash
echo -n "Copying GRUB templates... "
mkdir -p config/includes.chroot/boot/grub
cp ../../grub.cfg config/includes.chroot/boot/grub/grub.cfg
cp ../../theme.txt config/includes.chroot/boot/grub/theme.txt
cp ../../splash.png config/includes.chroot/splash.png
cp ../../templates/grub.cfg config/includes.chroot/boot/grub/grub.cfg || fail "Failed to copy critical template file"
cp ../../templates/theme.txt config/includes.chroot/boot/grub/theme.txt || fail "Failed to copy critical template file"
cp ../../templates/splash.png config/includes.chroot/splash.png || fail "Failed to copy critical template file"
echo "done."
# Install install.sh script
cp ../../install.sh config/includes.chroot/install.sh
echo -n "Copying PVC node installer script template... "
cp ../../templates/install.sh config/includes.chroot/install.sh || fail "Failed to copy critical template file"
chmod +x config/includes.chroot/install.sh
echo "done."
# Customize install.sh script
echo -n "Customizing PVC node installer script... "
sed -i "s/XXDATEXX/$(date)/g" config/includes.chroot/install.sh
sed -i "s/XXDEPLOYUSERXX/${deployusername}/g" config/includes.chroot/install.sh
echo "done."
echo
# Build the live image
echo "Building live image..."
sudo lb build
sudo lb build || fail "Failed to build live image"
echo
# Move the ISO image out
echo -n "Copying generated ISO to repository root... "
cp live-image-amd64.hybrid.iso ../../${isofilename}
echo "done."
# Clean up the artifacts
if [[ -z ${preserve_artifacts} ]]; then

View File

@ -108,25 +108,6 @@ build_pxe() {
rmdir ${tmpdir}
echo "done."
echo -n "Creating base iPXE configuration... "
cat <<EOF > ${outputdir}/boot.pxe
#!ipxe
# Set global variables
set root-url tftp://\${next-server}
set menu-default pvc-installer
set submenu-default pvc-installer
:pvc-installer
kernel \${root-url}/vmlinuz
initrd \${root-url}/initrd.img
imgargs vmlinuz console=tty0 vga=normal nomodeset boot=live components ethdevice-timeout=600 timezone=America/Toronto fetch=\${root-url}/filesystem.squashfs username=root pvcinstall.preseed=on pvcinstall.seed_host=\${next-server} pvcinstall.seed_file=/host/mac-\${mac:hexraw}.preseed
boot
EOF
echo "done."
echo -n "Downloading iPXE binary undionly.kpxe (chainloads arbitrary PXE clients)... "
pushd ${outputdir} &>/dev/null
wget -O undionly.kpxe https://boot.ipxe.org/undionly.kpxe &>/dev/null || fail "failed to download undionly.kpxe."
@ -139,6 +120,10 @@ EOF
popd &>/dev/null
echo "done."
echo -n "Copying base iPXE configuration... "
cp templates/boot.pxe ${outputdir}/boot.pxe
echo "done."
sudo chown -R $(whoami) ${outputdir}
sudo chmod -R u+w ${outputdir}

View File

@ -1,863 +0,0 @@
#!/usr/bin/env bash
logfile="/tmp/pvc-install.log"
lockfile="/run/pvc-install.lock"
if [[ $( whoami ) != "root" ]]; then
echo "STOP! This script is designed to run as root within the installer only!"
echo "Do not run it on your system. To build a PVC installer ISO, use './buildiso.sh' instead!"
exit 1
fi
# Random delay to prevent overlaps
DELAY=$(( ${RANDOM} % 10 ))
echo -n "Waiting ${DELAY} seconds... "
sleep ${DELAY}
echo "done."
if [[ -f ${lockfile} ]]; then
echo "An instance of 'install.sh' is already running!"
exit 1
fi
touch ${lockfile}
iso_name="XXDATEXX"
target_deploy_user="XXDEPLOYUSERXX"
supported_debrelease="buster bullseye"
default_debrelease="buster"
default_debmirror="http://debian.mirror.rafal.ca/debian"
inclpkglist="lvm2,parted,gdisk,grub-pc,grub-efi-amd64,linux-image-amd64,sudo,vim,gpg,gpg-agent,aptitude,openssh-server,vlan,ifenslave,python3,ca-certificates,curl"
suppkglist="firmware-linux,firmware-linux-nonfree,firmware-bnx2,firmware-bnx2x,ntp"
# DANGER - THIS PASSWORD IS PUBLIC
# It should be used ONLY immediately after booting the PVC node in a SECURE environment
# to facilitate troubleshooting of a failed boot. It should NOT be exposed to the Internet,
# and it should NOT be left in place after system configuration. The PVC Ansible deployment
# roles will overwrite it by default during configuration.
root_password="hCb1y2PF"
# Respawn function
respawn() (
$0 & disown
)
# Checkin function
seed_checkin() (
case ${1} in
start)
action="install-start"
;;
end)
action="install-complete"
;;
esac
macaddr=$( ip -br link show ${target_interface} | awk '{ print $3 }' )
ipaddr=$( ip -br address show ${target_interface} | awk '{ print $3 }' | awk -F '/' '{ print $1 }' )
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"action\":\"${action}\",\"macaddr\":\"${macaddr}\",\"ipaddr\":\"${ipaddr}\",\"hostname\":\"${target_hostname}\",\"bmc_macaddr\":\"${bmc_macaddr}\",\"bmc_ipaddr\":\"${bmc_ipaddr}\"}" \
${pvcbootstrapd_checkin_uri}
)
# Obtain the preseed options from the kernel command line
install_option=""
seed_host=""
seed_file=""
kernel_cmdline=( $( cat /proc/cmdline ) )
for option in ${kernel_cmdline[@]}; do
case ${option} in
pvcinstall.preseed=*)
install_option=${option#pvcinstall.preseed=}
;;
pvcinstall.seed_host=*)
seed_host=${option#pvcinstall.seed_host=}
;;
pvcinstall.seed_file=*)
seed_file=${option#pvcinstall.seed_file=}
;;
esac
done
seed_config() {
# Get IPMI BMC MAC for checkings
bmc_macaddr="$( ipmitool lan print | grep 'MAC Address ' | awk '{ print $NF }' )"
bmc_ipaddr="$( ipmitool lan print | grep 'IP Address ' | awk '{ print $NF }' )"
# Perform DHCP on all interfaces to come online
for interface in $( ip address | grep '^[0-9]' | grep 'eno\|enp\|ens\|wlp' | awk '{ print $2 }' | tr -d ':' ); do
ip link set ${interface} up
pgrep dhclient || dhclient ${interface}
done
# Fetch the seed config
tftp -m binary "${seed_host}" -c get "${seed_file}" /tmp/install.seed
. /tmp/install.seed || exit 1
# Handle the target interface
target_route="$( ip route show to match ${seed_host} | grep 'scope link' )"
target_interface="$( grep -E -o 'e[a-z]+[0-9]+[a-z0-9]*' <<<"${target_route}" )"
# Handle the target disk
if [[ -n ${target_disk_path} ]]; then
target_disk="$( realpath ${target_disk_path} )"
else
# Find the (first) disk with the given model
for disk in /dev/sd?; do
disk_model="$( fdisk -l ${disk} | grep 'Disk model:' | sed 's/Disk model: //g' )"
if [[ ${disk_model} == ${target_disk_model} ]]; then
target_disk="${disk}"
break
fi
done
fi
if [[ ! -b ${target_disk} ]]; then
echo "Invalid disk or disk not found!"
exit 1
fi
}
interactive_config() {
clear
echo "--------------------------------------------------------"
echo "| PVC Node installer (${iso_name}) |"
echo "--------------------------------------------------------"
echo
echo "This LiveCD will install a PVC node base system ready for bootstrapping with 'pvc-ansible'."
echo
echo "* NOTE: If you make a mistake and need to restart the installer while answering"
echo " the questions below, you may do so by typing ^C to cancel the script,"
echo " then re-running it by calling /install.sh in the resulting shell."
echo
echo "1) Please enter a fully-qualified hostname for the system. This should match the hostname"
echo "in the 'pvc-ansible' inventory."
while [[ -z ${target_hostname} ]]; do
echo
echo -n "> "
read target_hostname
if [[ -z ${target_hostname} ]]; then
echo
echo "Please enter a hostname."
continue
fi
echo
done
disks="$(
for disk in /dev/sd? /dev/nvme?n?; do
if [[ ! -b ${disk} ]]; then
continue
fi
disk_data="$( fdisk -l ${disk} 2>/dev/null )"
echo -n "${disk}"
echo -en "\t$( grep "^Disk model:" <<<"${disk_data}" | awk '{ $1=""; print $0 }' )"
echo -en " $( grep "^Disk ${disk}:" <<<"${disk_data}" | awk '{ $1=""; $2="size:"; print $0 }' )"
echo
done
)"
echo "2) Please enter the disk to install the PVC base system to. This disk will be"
echo "wiped, an LVM PV created on it, and the system installed to this LVM."
echo "* NOTE: PVC requires a disk of at least 30GB to be installed to, and 100GB is the"
echo "recommended minimum size for optimal production partition sizes."
echo "* NOTE: For optimal performance, this disk should be high-performance flash (SSD, etc.)."
echo "* NOTE: This disk should be a RAID-1 volume configured in hardware, or a durable storage"
echo "device, maximum redundancy and resiliency."
echo
echo "Available disks:"
echo
echo -e "$( sed 's/\(.*\)/ \1/' <<<"${disks[@]}" )"
while [[ ! -b ${target_disk} ]]; do
echo
echo -n "> "
read target_disk
if [[ ! -b ${target_disk} ]]; then
echo
echo "Please enter a valid target disk."
continue
fi
blockdev_size_gbytes="$(( $( blockdev --getsize64 ${target_disk} ) / 1024 / 1024 / 1024 - 1))"
if [[ ${blockdev_size_gbytes} -lt 30 ]]; then
target_disk=""
echo
echo "The specified disk is too small (<30 GB) to use as a PVC system disk."
echo "Please choose an alternative disk."
continue
fi
echo
done
for interface in $( ip address | grep '^[0-9]' | grep 'eno\|enp\|ens\|wlp' | awk '{ print $2 }' | tr -d ':' ); do
ip link set ${interface} up
done
sleep 2
interfaces="$(
ip address | grep '^[0-9]' | grep 'eno\|enp\|ens\|wlp' | awk '{ print $2"\t"$3 }' | tr -d ':'
)"
echo "3a) Please enter the primary network interface for external connectivity. If"
echo "no entries are shown here, ensure a cable is connected, then restart the"
echo "installer with ^C and '/install.sh'."
echo
echo "Available interfaces:"
echo
echo -e "$( sed 's/\(.*\)/ \1/' <<<"${interfaces[@]}" )"
while [[ -z ${target_interface} ]]; do
echo
echo -n "> "
read target_interface
if [[ -z ${target_interface} ]]; then
echo
echo "Please enter a valid interface."
continue
fi
if ! grep -qw "${target_interface}" <<<"${interfaces[@]}"; then
target_interface=""
echo
echo "Please enter a valid interface."
continue
fi
echo
done
echo -n "3b) Is a tagged vLAN required for the primary network interface? [y/N] "
read vlans_req
if [[ ${vlans_req} == 'y' || ${vlans_req} == 'Y' ]]; then
echo
echo "Please enter the vLAN ID for the interface."
while [[ -z ${vlan_id} ]]; do
echo
echo -n "> "
read vlan_id
if [[ -z ${vlan_id} ]]; then
echo
echo "Please enter a numeric vLAN ID."
continue
fi
done
echo
else
vlan_id=""
echo
fi
echo "3c) Please enter the IP address, in CIDR format [X.X.X.X/YY], of the primary"
echo "network interface. Leave blank for DHCP configuration of the interface on boot."
echo
echo -n "> "
read target_ipaddr
if [[ -n ${target_ipaddr} ]]; then
target_netformat="static"
echo
echo "3d) Please enter the default gateway IP address of the primary"
echo "network interface."
while [[ -z ${target_defgw} ]]; do
echo
echo -n "> "
read target_defgw
if [[ -z ${target_defgw} ]]; then
echo
echo "Please enter a default gateway; the installer requires Internet access."
continue
fi
echo
done
else
target_netformat="dhcp"
echo
fi
echo -n "Bringing up primary network interface in ${target_netformat} mode... "
case ${target_netformat} in
'static')
if [[ -n ${vlan_id} ]]; then
modprobe 8021q >&2
vconfig add ${target_interface} ${vlan_id} >&2
vlan_interface=${target_interface}.${vlan_id}
ip link set ${target_interface} up >&2 || true
ip link set ${vlan_interface} up >&2 || true
ip address add ${target_ipaddr} dev ${vlan_interface} >&2 || true
ip route add default via ${target_defgw} >&2 || true
formatted_ipaddr="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Host address/{ print $NF }' )"
formatted_netmask="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Network mask/{ print $NF }' )"
real_interface="${vlan_interface}"
target_interfaces_is="static-vlan"
else
ip link set ${target_interface} up >&2 || true
ip address add ${target_ipaddr} dev ${target_interface} >&2 || true
ip route add default via ${target_defgw} >&2 || true
formatted_ipaddr="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Host address/{ print $NF }' )"
formatted_netmask="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Network mask/{ print $NF }' )"
real_interface="${target_interface}"
target_interfaces_is="static-raw"
fi
cat <<EOF >/etc/resolv.conf
nameserver 8.8.8.8
EOF
;;
'dhcp')
if [[ -n ${vlan_id} ]]; then
modprobe 8021q >&2
vconfig add ${target_interface} ${vlan_id} &>/dev/null
vlan_interface=${target_interface}.${vlan_id}
dhclient ${vlan_interface} >&2
real_interface="${vlan_interface}"
target_interfaces_is="dhcp-vlan"
else
dhclient ${target_interface} >&2
real_interface="${target_interface}"
target_interfaces_is="dhcp-raw"
fi
;;
esac
echo "done."
echo
echo -n "Waiting for networking to become ready... "
while ! ping -q -c 1 8.8.8.8 &>/dev/null; do
sleep 1
done
echo "done."
echo
echo "4a) Please enter an alternate Debian release codename for the system if desired."
echo " Supported: ${supported_debrelease}"
echo " Default: ${default_debrelease}"
while [[ -z ${debrelease} ]]; do
echo
echo -n "> "
read debrelease
if [[ -z ${debrelease} ]]; then
debrelease="${default_debrelease}"
fi
if ! grep -qw "${debrelease}" <<<"${supported_debrelease}"; then
debrelease=""
echo
echo "Please enter a valid release."
continue
fi
echo
done
echo "4b) Please enter an HTTP URL for an alternate Debian mirror if desired."
echo " Default: ${default_debmirror}"
while [[ -z ${debmirror} ]]; do
echo
echo -n "> "
read debmirror
if [[ -z ${debmirror} ]]; then
debmirror="${default_debmirror}"
fi
if ! wget -O /dev/null ${debmirror}/dists/${debrelease}/Release &>/dev/null; then
debmirror=""
echo
echo "Please enter a valid Debian mirror URL."
continue
fi
echo
echo "Repository mirror '${debmirror}' successfully validated."
echo
done
target_keys_method="wget"
echo "5) Please enter an HTTP URL containing a text list of SSH authorized keys to"
echo "fetch. These keys will be allowed access to the deployment user 'XXDEPLOYUSER'"
echo "via SSH."
echo ""
echo "Leave blank to bypass this and use a password instead."
echo
echo -n "> "
read target_keys_path
if [[ -z ${target_keys_path} ]]; then
echo
echo "No SSH keys URL specified. Falling back to password configuration."
echo
echo "5) Please enter a password (hidden), twice, for the deployment user '${target_deploy_user}'."
while [[ -z "${target_password}" ]]; do
echo
echo -n "> "
read -s target_password_1
echo
echo -n "> "
read -s target_password_2
echo
if [[ -n "${target_password_1}" && "${target_password_1}" -eq "${target_password_2}" ]]; then
target_password="${target_password_1}"
else
echo
echo "The specified passwords do not match or are empty."
fi
done
else
while ! wget -O /dev/null ${target_keys_path} &>/dev/null; do
echo
echo "Please enter a valid SSH keys URL."
echo
echo -n "> "
read target_keys_path
done
echo
echo "SSH key source '${target_keys_path}' successfully validated."
fi
echo
}
case ${install_option} in
on)
seed_config
;;
*)
interactive_config
;;
esac
titlestring_text="| Proceeding with installation of host '${target_hostname}'. |"
titlestring_len="$(( $( wc -c <<<"${titlestring_text}" ) - 2 ))"
for i in $( seq 0 ${titlestring_len} ); do echo -n "-"; done; echo
echo "${titlestring_text}"
for i in $( seq 0 ${titlestring_len} ); do echo -n "-"; done; echo
echo
### Script begins ###
echo "LOGFILE: ${logfile}"
echo
exec 1> >( tee -ia ${logfile} )
exec 2> >( tee -ia ${logfile} >/dev/null )
set -o errexit
set -o xtrace
case ${install_option} in
on)
seed_checkin start
;;
*)
# noop
true
;;
esac
cleanup() {
set +o errexit
echo -n "Cleaning up... "
umount ${target}/tmp >&2
umount ${target}/run >&2
umount ${target}/sys >&2
umount ${target}/proc >&2
umount ${target}/dev/pts >&2
umount ${target}/dev >&2
umount ${target}/var/lib/ceph >&2
umount ${target}/var/lib/zookeeper >&2
umount ${target}/boot/efi >&2
umount ${target}/boot >&2
umount ${target} >&2
vgchange -an >&2
rmdir ${target} >&2
rm ${lockfile}
echo "done."
echo
case ${install_option} in
on)
respawn
;;
*)
# noop
true
;;
esac
}
trap cleanup EXIT
echo -n "Determining partition sizes... "
blockdev_size_bytes="$( blockdev --getsize64 ${target_disk} )"
blockdev_size_gbytes="$(( ${blockdev_size_bytes} / 1024 / 1024 / 1024 - 1))"
if [[ ${blockdev_size_gbytes} -ge 100 ]]; then
# Optimal sized system disk (>=100GB), use large partitions
size_root_lv="32"
size_ceph_lv="8"
size_zookeeper_lv="32"
size_swap_lv="16"
echo "found large disk (${blockdev_size_gbytes} >= 100GB), using optimal partition sizes."
else
# Minimum sized disk (>=30GB), use small partitions
size_root_lv="8"
size_ceph_lv="4"
size_zookeeper_lv="8"
size_swap_lv="8"
echo "found small disk (${blockdev_size_gbytes} < 100GB), using small partition sizes."
fi
echo -n "Disabing existing volume groups... "
vgchange -an >&2 || true
echo "done."
blockcheck() {
# Use for testing only
if [[ -n ${SKIP_BLOCKCHECK} ]]; then
return
fi
# Determine optimal block size for zeroing
exponent=16
remainder=1
while [[ ${remainder} -gt 0 && ${exponent} -gt 0 ]]; do
exponent=$(( ${exponent} - 1 ))
size=$(( 2**9 * 2 ** ${exponent} ))
count=$(( ${blockdev_size_bytes} / ${size} ))
remainder=$(( ${blockdev_size_bytes} - ${count} * ${size} ))
done
if [[ ${remainder} -gt 0 ]]; then
echo "Failed to find a suitable block size for wiping... skipping."
return
fi
echo -n "Checking if block device '${target_disk}' is already wiped... "
if ! cmp --silent --bytes ${blockdev_size_bytes} /dev/zero ${target_disk}; then
echo "false."
echo "Wiping block device '${target_disk}' (${count} blocks of ${size} bytes)..."
dd if=/dev/zero of=${target_disk} bs=${size} count=${count} oflag=direct status=progress 2>&1
else
echo "done."
fi
}
blockcheck
echo -n "Preparing block device '${target_disk}'... "
# New GPT, part 1 64MB ESP, part 2 960MB BOOT, part 3 inf LVM PV
echo -e "o\ny\nn\n1\n\n64M\nEF00\nn\n2\n\n960M\n8300\nn\n3\n\n\n8E00\nw\ny\n" | gdisk ${target_disk} >&2
echo "done."
echo -n "Rescanning disks... "
partprobe >&2 || true
echo "done."
echo -n "Creating LVM PV... "
yes | pvcreate -ffy ${target_disk}3 >&2
echo "done."
echo -n "Creating LVM VG 'vgx'... "
yes | vgcreate vgx ${target_disk}3 >&2
echo "done."
echo -n "Creating root logical volume (${size_root_lv}GB)... "
yes | lvcreate -L ${size_root_lv}G -n root vgx >&2
echo "done."
echo -n "Creating filesystem on root logical volume (ext4)... "
yes | mkfs.ext4 /dev/vgx/root >&2
echo "done."
echo -n "Creating ceph logical volume (${size_ceph_lv}GB)... "
yes | lvcreate -L ${size_ceph_lv}G -n ceph vgx >&2
echo "done."
echo -n "Creating filesystem on ceph logical volume (ext4)... "
yes | mkfs.ext4 /dev/vgx/ceph >&2
echo "done."
echo -n "Creating zookeeper logical volume (${size_zookeeper_lv}GB)... "
yes | lvcreate -L ${size_zookeeper_lv}G -n zookeeper vgx >&2
echo "done."
echo -n "Creating filesystem on zookeeper logical volume (ext4)... "
yes | mkfs.ext4 /dev/vgx/zookeeper >&2
echo "done."
echo -n "Creating swap logical volume (${size_swap_lv}GB)... "
yes | lvcreate -L ${size_swap_lv}G -n swap vgx >&2
echo "done."
echo -n "Creating swap space on swap logical volume... "
yes | mkswap -f /dev/vgx/swap >&2
echo "done."
echo -n "Creating filesystem on boot partition (ext2)... "
yes | mkfs.ext2 ${target_disk}2 >&2
echo "done."
echo -n "Creating filesystem on ESP partition (vfat)... "
yes | mkdosfs -F32 ${target_disk}1 >&2
echo "done."
vgchange -ay >&2
target="/tmp/target"
mkdir -p ${target} >&2
echo -n "Mounting disks on temporary target '${target}'... "
mount /dev/vgx/root ${target} >&2
mkdir -p ${target}/boot >&2
chattr +i ${target}/boot >&2
mount ${target_disk}2 ${target}/boot >&2
mkdir -p ${target}/boot/efi >&2
chattr +i ${target}/boot/efi >&2
mount ${target_disk}1 ${target}/boot/efi >&2
mkdir -p ${target}/var/lib/ceph >&2
chattr +i ${target}/var/lib/ceph >&2
mount /dev/vgx/ceph ${target}/var/lib/ceph >&2
mkdir -p ${target}/var/lib/zookeeper >&2
chattr +i ${target}/var/lib/zookeeper >&2
mount /dev/vgx/zookeeper ${target}/var/lib/zookeeper >&2
mkdir -p ${target}/tmp >&2
chattr +i ${target}/tmp >&2
mount -t tmpfs tmpfs ${target}/tmp >&2
echo "done."
echo -n "Running debootstrap install... "
echo "Command: debootstrap --include=${inclpkglist} ${debrelease} ${target}/ ${debmirror}" >&2
debootstrap --include=${inclpkglist} ${debrelease} ${target}/ ${debmirror} >&2
echo "done."
echo -n "Adding non-free repository (firmware, etc.)... "
mkdir -p ${target}/etc/apt/sources.list.d/ >&2
echo "deb ${debmirror} ${debrelease} contrib non-free" | tee -a ${target}/etc/apt/sources.list >&2
chroot ${target} apt update >&2
echo "done."
echo -n "Installing supplemental packages... "
chroot ${target} apt install -y --no-install-recommends $( sed 's/,/ /g' <<<"${suppkglist}" ) >&2
echo "done."
# Determine the bypath name of the specified system disk
for disk in /dev/disk/by-path/*; do
bypathlink="$( realpath ${disk} | awk -F'/' '{ print $NF }' )"
enteredname="$( awk -F'/' '{ print $NF }' <<<"${target_disk}" )"
if [[ ${bypathlink} == ${enteredname} ]]; then
bypath_disk="${disk}"
fi
done
echo -n "Adding fstab entries... "
echo "/dev/mapper/vgx-root / ext4 errors=remount-ro 0 1" | tee -a ${target}/etc/fstab >&2
echo "/dev/mapper/vgx-ceph /var/lib/ceph ext4 errors=remount-ro 0 2" | tee -a ${target}/etc/fstab >&2
echo "/dev/mapper/vgx-zookeeper /var/lib/zookeeper ext4 errors=remount-ro 0 2" | tee -a ${target}/etc/fstab >&2
echo "/dev/mapper/vgx-swap none swap sw 0 0" | tee -a ${target}/etc/fstab >&2
echo "${bypath_disk}-part2 /boot ext2 defaults 0 2" | tee -a ${target}/etc/fstab >&2
echo "${bypath_disk}-part1 /boot/efi vfat umask=0077 0 2" | tee -a ${target}/etc/fstab >&2
echo "tmpfs /tmp tmpfs defaults 0 0" | tee -a ${target}/etc/fstab >&2
echo "done."
seed_interfaces_segment() {
# A seed install is always "dhcp-raw" since the provisioner is always a dedicated, access port
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet dhcp\npost-up /etc/network/pvcprovisionerd.checkin.sh \$IFACE"
}
interactive_interfaces_segment() {
case ${target_interfaces_is} in
static-vlan)
target_interfaces_block="auto ${vlan_interface}\niface ${vlan_interface} inet ${target_netformat}\n\tvlan_raw_device ${target_interface}\n\taddress ${formatted_ipaddr}\n\tnetmask ${formatted_netmask}\n\tgateway ${target_defgw}"
;;
static-raw)
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet ${target_netformat}\n\taddress ${formatted_ipaddr}\n\tnetmask ${formatted_netmask}\n\tgateway ${target_defgw}"
;;
dhcp-vlan)
target_interfaces_block="auto ${vlan_interface}\niface ${vlan_interface} inet ${target_netformat}\n\tvlan_raw_device${target_interface}"
;;
dhcp-raw)
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet ${target_netformat}"
;;
esac
}
echo -n "Creating bootstrap interface segment... "
case ${install_option} in
on)
seed_interfaces_segment
;;
*)
interactive_interfaces_segment
;;
esac
echo "done."
echo -n "Adding bootstrap interface segment... "
echo -e "${target_interfaces_block}" | tee ${target}/etc/network/interfaces.d/${target_interface} >&2
echo "done."
case ${install_option} in
on)
echo -n "Creating bond interface segment... "
bond_interfaces="$( ip -br link show | grep -E -o '^e[a-z]+[0-9]+[a-z0-9]*' | grep -v "^${target_interface}$" | tr '\n' ' ' )"
bond_interfaces_block="auto bond0\niface bond0 inet manual\n\tbond-mode 802.3ad\n\tbond-slaves ${bond_interfaces}\n\tpost-up ip link set mtu 9000 dev \$IFACE"
echo "done."
echo -n "Adding bond interface segment... "
echo -e "${bond_interfaces_block}" | tee ${target}/etc/network/interfaces.d/bond0 >&2
echo "done."
echo -n "Adding bootstrap interface post-up checkin script... "
cat <<EOF | tee ${target}/etc/network/pvcprovisionerd.checkin.sh >&2
#!/usr/bin/env bash
target_interface=\${1}
pvcbootstrapd_checkin_uri="${pvcbootstrapd_checkin_uri}"
macaddr=\$( ip -br link show \${target_interface} | awk '{ print \$3 }' )
ipaddr=\$( ip -br address show \${target_interface} | awk '{ print \$3 }' | awk -F '/' '{ print \$1 }' )
bmc_macaddr=\$( ipmitool lan print | grep 'MAC Address ' | awk '{ print $NF }' )
bmc_ipaddr=\$( ipmitool lan print | grep 'IP Address ' | awk '{ print $NF }' )
if [[ -f /etc/pvc-install.pvcbootstrapd_completed ]]; then
# The third boot, when all pvcprovisionerd plugins have been run (this script will henceforth do nothing)
action="system-boot_bootstrap-completed"
elif [[ -f /etc/pvc-install.base && -f /etc/pvc-install.pvc ]]; then
# The second boot, when Ansible has been run and plugins running
action="system-boot_ansible-completed"
touch /etc/pvc-install.pvcbootstrapd_completed
else
# The first boot, when Ansible has not been run yet
action="system-boot_initial"
fi
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"action\":\"\${action}\",\"macaddr\":\"\${macaddr}\",\"ipaddr\":\"\${ipaddr}\",\"hostname\":\"\$( hostname -s )\",\"bmc_macaddr\":\"\${bmc_macaddr}\",\"bmc_ipaddr\":\"\${bmc_ipaddr}\"}" \
\${pvcbootstrapd_checkin_uri}
if [[ -f /etc/pvc-install.pvcbootstrapd_completed ]]; then
# Clean up the bootstrap interface and this script
rm /etc/network/interfaces.d/\${target_interface}
rm \$0
exit 0
fi
EOF
chmod +x ${target}/etc/network/pvcprovisionerd.checkin.sh
echo "done."
;;
*)
# noop
true
;;
esac
echo "done."
echo -n "Setting temporary 'root' password... "
echo "root:${root_password}" | chroot ${target} chpasswd >&2
echo "done."
echo -n "Adding deployment user... "
mv ${target}/home ${target}/var/home >&2
chroot ${target} useradd -u 200 -d /var/home/${target_deploy_user} -m -s /bin/bash -g operator -G sudo ${target_deploy_user} >&2
chroot ${target} mkdir -p /var/home/${target_deploy_user}/.ssh
if [[ -n ${target_keys_path} ]]; then
case ${target_keys_method} in
wget)
wget -O ${target}/var/home/${target_deploy_user}/.ssh/authorized_keys ${target_keys_path}
;;
tftp)
tftp -m binary "${seed_host}" -c get "${target_keys_path}" ${target}/var/home/${target_deploy_user}/.ssh/authorized_keys
;;
esac
chroot ${target} chmod 0600 /var/home/${target_deploy_user}/.ssh/authorized_keys
chroot ${target} chown -R ${target_deploy_user}:operator /var/home/${target_deploy_user}
else
echo "${target_deploy_user}:${target_password}" | chroot ${target} chpasswd >&2
fi
echo "done."
echo -n "Setting NOPASSWD for sudo group... "
sed -i 's/^%sudo\tALL=(ALL:ALL) ALL/%sudo\tALL=(ALL:ALL) NOPASSWD: ALL/' ${target}/etc/sudoers
echo "done."
echo -n "Setting /etc/issue generator... "
mkdir -p ${target}/etc/network/if-up.d >&2
echo -e "#!/bin/sh
IP=\"\$( ip -4 addr show dev ${target_interface} | grep inet | awk '{ print \$2 }' | head -1 )\"
cat <<EOF >/etc/issue
Debian GNU/Linux 10 \\\\n \\\\l
Bootstrap interface IP address: \$IP
EOF" | tee ${target}/etc/network/if-up.d/issue-gen >&2
chmod +x ${target}/etc/network/if-up.d/issue-gen 1>&2
echo "done."
echo -n "Generating host rsa and ed25519 keys... "
rm ${target}/etc/ssh/ssh_host_*_key* >&2
chroot ${target} ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key >&2
chroot ${target} ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key >&2
echo "done."
echo -n "Setting hostname... "
echo "${target_hostname}" | tee ${target}/etc/hostname >&2
echo "done."
echo -n "Setting resolv.conf... "
echo "nameserver 8.8.8.8" | tee ${target}/etc/resolv.conf >&2
echo "done."
echo -n "Installing GRUB bootloader... "
mount --bind /dev ${target}/dev >&2
mount --bind /dev/pts ${target}/dev/pts >&2
mount --bind /proc ${target}/proc >&2
mount --bind /sys ${target}/sys >&2
mount --bind /run ${target}/run >&2
if [[ -d /sys/firmware/efi ]]; then
bios_target="x86_64-efi"
else
bios_target="i386-pc"
fi
cat <<EOF | tee ${target}/etc/default/grub >&2
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Parallel Virtual Cluster (PVC) - Debian"
GRUB_CMDLINE_LINUX="console=hvc0 console=tty0 console=ttyS0,115200"
GRUB_TERMINAL_INPUT="console serial"
GRUB_TERMINAL_OUTPUT="gfxterm serial"
GRUB_SERIAL_COMMAND="serial --unit=0 --unit=1 --speed=115200"
EOF
chroot ${target} grub-install --force --target=${bios_target} ${target_disk} >&2
chroot ${target} grub-mkconfig -o /boot/grub/grub.cfg >&2
echo "done."
seed_postinst() {
cleanup
echo "Temporary root password: ${root_password}"
seed_checkin end
echo -n "Rebooting in 10 seconds..."
i=10
while [[ ${i} -gt 0 ]]; do
sleep 1
i=$(( ${1} - 1 ))
echo -n "."
done
echo
reboot
}
interactive_postinst() {
set +o errexit
echo
echo -n "Edit the /etc/network/interfaces file in the target before completing setup? If you plan to use bonding, it is prudent to set this up in basic form now! [y/N] "
read edit_ifaces
if [[ ${edit_ifaces} == 'y' || ${edit_ifaces} == 'Y' ]]; then
vim ${target}/etc/network/interfaces
fi
echo
echo -n "Launch a chroot shell in the target environment? [y/N] "
read launch_chroot
if [[ ${launch_chroot} == 'y' || ${edit_ifaces} == 'Y' ]]; then
echo "Type 'exit' or Ctrl+D to exit chroot."
chroot ${target} /bin/bash
fi
cleanup
echo "-------------------------------------------------------------------------------------"
echo "| PVC node installation finished. Next steps: |"
echo "| 1. Press <enter> to reboot the system. |"
echo "| 2. Boot the system verify SSH access (IP shown on login screen). |"
echo "| 3. Proceed with system deployment via PVC Ansible. |"
echo "| |"
echo "| The INSECURE temporary root password if the system will not boot is: ${root_password} |"
echo "-------------------------------------------------------------------------------------"
echo
read
reboot
}
case ${install_option} in
on)
seed_postinst
;;
*)
interactive_postinst
;;
esac

1
install.sh Symbolic link
View File

@ -0,0 +1 @@
templates/install.sh

View File

View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "I: remove root password"
passwd --delete root

View File

@ -0,0 +1,69 @@
#!/bin/sh
#set -e
log_wait_msg ()
{
# Print a message and wait for enter
if [ -x /bin/plymouth ] && plymouth --ping
then
plymouth message --text="$@"
plymouth watch-keystroke | read nunya
fi
_log_msg "Waiting: ${@} ... \n"
}
# Override maybe_break from scripts/functions
maybe_break()
{
if [ "${break}" = "$1" ]; then
# Call original panic
. /scripts/functions
panic "Spawning shell within the initramfs"
fi
}
# Override panic from scripts/functions
panic()
{
for _PARAMETER in ${LIVE_BOOT_CMDLINE}
do
case "${_PARAMETER}" in
panic=*)
panic="${_PARAMETER#*panic=}"
;;
esac
done
DEB_1="\033[1;31m .''\`. \033[0m"
DEB_2="\033[1;31m: :' : \033[0m"
DEB_3="\033[1;31m\`. \`'\` \033[0m"
DEB_4="\033[1;31m \`- \033[0m"
LIVELOG="\033[1;37m/boot.log\033[0m"
DEBUG="\033[1;37mdebug\033[0m"
# Reset redirections to avoid buffering
exec 1>&6 6>&-
exec 2>&7 7>&-
kill ${tailpid}
printf "\n\n"
printf " \033[1;37mBOOT FAILED!\033[0m\n"
printf "\n"
printf " The PVC installer image failed to boot.\n\n"
printf "The error message was:\n\n "
printf " $@\n\n"
# Reboot system
printf "System will reboot in 30 seconds. Press any key to spawn a shell instead.\n"
if ! read -t 30; then
sleep 30
reboot -f
fi
# Call original panic
. /scripts/functions
panic "$@"
}

24
templates/boot.pxe Normal file
View File

@ -0,0 +1,24 @@
#!ipxe
# Set global variables
set root-url tftp://${next-server}
set kernel vmlinuz
set initrd initrd.img
# Set kernel command line parameters
set imgargs-base vga=normal nomodeset boot=live components ethdevice-timeout=600 timezone=America/Toronto fetch=${root-url}/filesystem.squashfs username=root
set imgargs-pvcinstall pvcinstall.preseed=on pvcinstall.seed_host=${next-server} pvcinstall.seed_file=/host/mac-${mac:hexraw}.preseed
# Load per-host kernel command line parameters (should contain ${imgargs-host} if present)
chain --autofree ${root-url}/host/mac-${mac:hexraw}.ipxe ||
# Set default menu options
set menu-default pvc-installer
set submenu-default pvc-installer
# PVC installer menu option
:pvc-installer
kernel ${root-url}/vmlinuz
initrd ${root-url}/initrd.img
imgargs vmlinuz ${imgargs-host} ${imgargs-base} ${imgargs-pvcinstall}
boot

View File

@ -0,0 +1 @@
firmware-bnx2 firmware-bnx2x

View File

@ -0,0 +1,3 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -- \\u' --autologin root --noclear %I $TERM

1
templates/hostname Normal file
View File

@ -0,0 +1 @@
pvc-live-installer

892
templates/install.sh Executable file
View File

@ -0,0 +1,892 @@
#!/usr/bin/env bash
logfile="/tmp/pvc-install.log"
lockfile="/run/pvc-install.lock"
if [[ $( whoami ) != "root" ]]; then
echo "STOP! This script is designed to run as root within the installer only!"
echo "To build a PVC installer ISO file, use './buildiso.sh'."
echo "To build a PVC installer PXE root, use './buildpxe.sh'."
exit 1
fi
echo
active_ttys=( $( w | grep "^root" | awk '{ print $2 }' ) )
echo "Active TTYs: ${active_ttys[@]}"
this_tty=$( tty | sed -e "s:/dev/::" )
echo "This TTY: ${this_tty}"
echo
if [[ ${#active_ttys} -gt 1 ]]; then
if [[ "${active_ttys[@]}" =~ "ttyS" ]]; then
if grep -q -E -o "tty[0-9]+" <<<"${this_tty}"; then
echo "Found more than one TTY and at least one serial TTY!"
echo -n "If you wish to run the installer on this graphical TTY instead of the serial TTY, press enter within 15 seconds... "
if ! read -t 15; then
echo "timeout."
exit 0
fi
else
echo "Found more than one TTY!"
echo -n "Waiting for other TTYs to time out... "
sleep $(( 16 + $( grep -E -o '[0-9]+' <<<"${this_tty}" ) ))
echo "done."
fi
else
echo "Found more than one graphical TTY!"
echo -n "If you wish to run the installer on this graphical TTY, press enter within 60 seconds... "
if ! read -t 60; then
echo "timeout."
echo "To launch the installer again on this TTY, run '/install.sh'."
exit 0
fi
fi
fi
if [[ -f ${lockfile} ]]; then
echo "Aborting installer due to lockfile presence: $( cat ${lockfile} )."
exit 0
fi
printf "PID $$ on TTY ${this_tty}" > ${lockfile}
echo
iso_name="XXDATEXX"
target_deploy_user="XXDEPLOYUSERXX"
supported_debrelease="buster bullseye"
default_debrelease="buster"
default_debmirror="http://debian.mirror.rafal.ca/debian"
inclpkglist="lvm2,parted,gdisk,grub-pc,grub-efi-amd64,linux-image-amd64,sudo,vim,gpg,gpg-agent,aptitude,openssh-server,vlan,ifenslave,python3,ca-certificates,curl"
suppkglist="firmware-linux,firmware-linux-nonfree,firmware-bnx2,firmware-bnx2x,ntp"
# DANGER - THIS PASSWORD IS PUBLIC
# It should be used ONLY immediately after booting the PVC node in a SECURE environment
# to facilitate troubleshooting of a failed boot. It should NOT be exposed to the Internet,
# and it should NOT be left in place after system configuration. The PVC Ansible deployment
# roles will overwrite it by default during configuration.
root_password="hCb1y2PF"
# Respawn function
respawn() (
echo "Respawning..."
$0 & disown
)
# Checkin function
seed_checkin() (
case ${1} in
start)
action="install-start"
;;
end)
action="install-complete"
;;
esac
macaddr=$( ip -br link show ${target_interface} | awk '{ print $3 }' )
ipaddr=$( ip -br address show ${target_interface} | awk '{ print $3 }' | awk -F '/' '{ print $1 }' )
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"action\":\"${action}\",\"macaddr\":\"${macaddr}\",\"ipaddr\":\"${ipaddr}\",\"hostname\":\"${target_hostname}\",\"bmc_macaddr\":\"${bmc_macaddr}\",\"bmc_ipaddr\":\"${bmc_ipaddr}\"}" \
${pvcbootstrapd_checkin_uri}
)
# Obtain the preseed options from the kernel command line
install_option=""
seed_host=""
seed_file=""
kernel_cmdline=( $( cat /proc/cmdline ) )
for option in ${kernel_cmdline[@]}; do
case ${option} in
pvcinstall.preseed=*)
install_option=${option#pvcinstall.preseed=}
;;
pvcinstall.seed_host=*)
seed_host=${option#pvcinstall.seed_host=}
;;
pvcinstall.seed_file=*)
seed_file=${option#pvcinstall.seed_file=}
;;
esac
done
seed_config() {
# Get IPMI BMC MAC for checkings
bmc_macaddr="$( ipmitool lan print | grep 'MAC Address ' | awk '{ print $NF }' )"
bmc_ipaddr="$( ipmitool lan print | grep 'IP Address ' | awk '{ print $NF }' )"
# Perform DHCP on all interfaces to come online
for interface in $( ip address | grep '^[0-9]' | grep 'eno\|enp\|ens\|wlp' | awk '{ print $2 }' | tr -d ':' ); do
ip link set ${interface} up
pgrep dhclient || dhclient ${interface}
done
# Fetch the seed config
tftp -m binary "${seed_host}" -c get "${seed_file}" /tmp/install.seed
. /tmp/install.seed || exit 1
# Handle the target interface
target_route="$( ip route show to match ${seed_host} | grep 'scope link' )"
target_interface="$( grep -E -o 'e[a-z]+[0-9]+[a-z0-9]*' <<<"${target_route}" )"
# Handle the target disk
if [[ -n ${target_disk_path} ]]; then
target_disk="$( realpath ${target_disk_path} )"
else
# Find the (first) disk with the given model
for disk in /dev/sd?; do
disk_model="$( fdisk -l ${disk} | grep 'Disk model:' | sed 's/Disk model: //g' )"
if [[ ${disk_model} == ${target_disk_model} ]]; then
target_disk="${disk}"
break
fi
done
fi
if [[ ! -b ${target_disk} ]]; then
echo "Invalid disk or disk not found!"
exit 1
fi
}
interactive_config() {
clear
echo "--------------------------------------------------------"
echo "| PVC Node installer (${iso_name}) |"
echo "--------------------------------------------------------"
echo
echo "This LiveCD will install a PVC node base system ready for bootstrapping with 'pvc-ansible'."
echo
echo "* NOTE: If you make a mistake and need to restart the installer while answering"
echo " the questions below, you may do so by typing ^C to cancel the script,"
echo " then re-running it by calling /install.sh in the resulting shell."
echo
echo "1) Please enter a fully-qualified hostname for the system. This should match the hostname"
echo "in the 'pvc-ansible' inventory."
while [[ -z ${target_hostname} ]]; do
echo
echo -n "> "
read target_hostname
if [[ -z ${target_hostname} ]]; then
echo
echo "Please enter a hostname."
continue
fi
echo
done
disks="$(
for disk in /dev/sd? /dev/nvme?n?; do
if [[ ! -b ${disk} ]]; then
continue
fi
disk_data="$( fdisk -l ${disk} 2>/dev/null )"
echo -n "${disk}"
echo -en "\t$( grep "^Disk model:" <<<"${disk_data}" | awk '{ $1=""; print $0 }' )"
echo -en " $( grep "^Disk ${disk}:" <<<"${disk_data}" | awk '{ $1=""; $2="size:"; print $0 }' )"
echo
done
)"
echo "2) Please enter the disk to install the PVC base system to. This disk will be"
echo "wiped, an LVM PV created on it, and the system installed to this LVM."
echo "* NOTE: PVC requires a disk of at least 30GB to be installed to, and 100GB is the"
echo "recommended minimum size for optimal production partition sizes."
echo "* NOTE: For optimal performance, this disk should be high-performance flash (SSD, etc.)."
echo "* NOTE: This disk should be a RAID-1 volume configured in hardware, or a durable storage"
echo "device, maximum redundancy and resiliency."
echo
echo "Available disks:"
echo
echo -e "$( sed 's/\(.*\)/ \1/' <<<"${disks[@]}" )"
while [[ ! -b ${target_disk} ]]; do
echo
echo -n "> "
read target_disk
if [[ ! -b ${target_disk} ]]; then
echo
echo "Please enter a valid target disk."
continue
fi
blockdev_size_gbytes="$(( $( blockdev --getsize64 ${target_disk} ) / 1024 / 1024 / 1024 - 1))"
if [[ ${blockdev_size_gbytes} -lt 30 ]]; then
target_disk=""
echo
echo "The specified disk is too small (<30 GB) to use as a PVC system disk."
echo "Please choose an alternative disk."
continue
fi
echo
done
for interface in $( ip address | grep '^[0-9]' | grep 'eno\|enp\|ens\|wlp' | awk '{ print $2 }' | tr -d ':' ); do
ip link set ${interface} up
done
sleep 2
interfaces="$(
ip address | grep '^[0-9]' | grep 'eno\|enp\|ens\|wlp' | awk '{ print $2"\t"$3 }' | tr -d ':'
)"
echo "3a) Please enter the primary network interface for external connectivity. If"
echo "no entries are shown here, ensure a cable is connected, then restart the"
echo "installer with ^C and '/install.sh'."
echo
echo "Available interfaces:"
echo
echo -e "$( sed 's/\(.*\)/ \1/' <<<"${interfaces[@]}" )"
while [[ -z ${target_interface} ]]; do
echo
echo -n "> "
read target_interface
if [[ -z ${target_interface} ]]; then
echo
echo "Please enter a valid interface."
continue
fi
if ! grep -qw "${target_interface}" <<<"${interfaces[@]}"; then
target_interface=""
echo
echo "Please enter a valid interface."
continue
fi
echo
done
echo -n "3b) Is a tagged vLAN required for the primary network interface? [y/N] "
read vlans_req
if [[ ${vlans_req} == 'y' || ${vlans_req} == 'Y' ]]; then
echo
echo "Please enter the vLAN ID for the interface."
while [[ -z ${vlan_id} ]]; do
echo
echo -n "> "
read vlan_id
if [[ -z ${vlan_id} ]]; then
echo
echo "Please enter a numeric vLAN ID."
continue
fi
done
echo
else
vlan_id=""
echo
fi
echo "3c) Please enter the IP address, in CIDR format [X.X.X.X/YY], of the primary"
echo "network interface. Leave blank for DHCP configuration of the interface on boot."
echo
echo -n "> "
read target_ipaddr
if [[ -n ${target_ipaddr} ]]; then
target_netformat="static"
echo
echo "3d) Please enter the default gateway IP address of the primary"
echo "network interface."
while [[ -z ${target_defgw} ]]; do
echo
echo -n "> "
read target_defgw
if [[ -z ${target_defgw} ]]; then
echo
echo "Please enter a default gateway; the installer requires Internet access."
continue
fi
echo
done
else
target_netformat="dhcp"
echo
fi
echo -n "Bringing up primary network interface in ${target_netformat} mode... "
case ${target_netformat} in
'static')
if [[ -n ${vlan_id} ]]; then
modprobe 8021q >&2
vconfig add ${target_interface} ${vlan_id} >&2
vlan_interface=${target_interface}.${vlan_id}
ip link set ${target_interface} up >&2 || true
ip link set ${vlan_interface} up >&2 || true
ip address add ${target_ipaddr} dev ${vlan_interface} >&2 || true
ip route add default via ${target_defgw} >&2 || true
formatted_ipaddr="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Host address/{ print $NF }' )"
formatted_netmask="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Network mask/{ print $NF }' )"
real_interface="${vlan_interface}"
target_interfaces_is="static-vlan"
else
ip link set ${target_interface} up >&2 || true
ip address add ${target_ipaddr} dev ${target_interface} >&2 || true
ip route add default via ${target_defgw} >&2 || true
formatted_ipaddr="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Host address/{ print $NF }' )"
formatted_netmask="$( sipcalc ${target_ipaddr} | grep -v '(' | awk '/Network mask/{ print $NF }' )"
real_interface="${target_interface}"
target_interfaces_is="static-raw"
fi
cat <<EOF >/etc/resolv.conf
nameserver 8.8.8.8
EOF
;;
'dhcp')
if [[ -n ${vlan_id} ]]; then
modprobe 8021q >&2
vconfig add ${target_interface} ${vlan_id} &>/dev/null
vlan_interface=${target_interface}.${vlan_id}
dhclient ${vlan_interface} >&2
real_interface="${vlan_interface}"
target_interfaces_is="dhcp-vlan"
else
dhclient ${target_interface} >&2
real_interface="${target_interface}"
target_interfaces_is="dhcp-raw"
fi
;;
esac
echo "done."
echo
echo -n "Waiting for networking to become ready... "
while ! ping -q -c 1 8.8.8.8 &>/dev/null; do
sleep 1
done
echo "done."
echo
echo "4a) Please enter an alternate Debian release codename for the system if desired."
echo " Supported: ${supported_debrelease}"
echo " Default: ${default_debrelease}"
while [[ -z ${debrelease} ]]; do
echo
echo -n "> "
read debrelease
if [[ -z ${debrelease} ]]; then
debrelease="${default_debrelease}"
fi
if ! grep -qw "${debrelease}" <<<"${supported_debrelease}"; then
debrelease=""
echo
echo "Please enter a valid release."
continue
fi
echo
done
echo "4b) Please enter an HTTP URL for an alternate Debian mirror if desired."
echo " Default: ${default_debmirror}"
while [[ -z ${debmirror} ]]; do
echo
echo -n "> "
read debmirror
if [[ -z ${debmirror} ]]; then
debmirror="${default_debmirror}"
fi
if ! wget -O /dev/null ${debmirror}/dists/${debrelease}/Release &>/dev/null; then
debmirror=""
echo
echo "Please enter a valid Debian mirror URL."
continue
fi
echo
echo "Repository mirror '${debmirror}' successfully validated."
echo
done
target_keys_method="wget"
echo "5) Please enter an HTTP URL containing a text list of SSH authorized keys to"
echo "fetch. These keys will be allowed access to the deployment user 'XXDEPLOYUSER'"
echo "via SSH."
echo ""
echo "Leave blank to bypass this and use a password instead."
echo
echo -n "> "
read target_keys_path
if [[ -z ${target_keys_path} ]]; then
echo
echo "No SSH keys URL specified. Falling back to password configuration."
echo
echo "5) Please enter a password (hidden), twice, for the deployment user '${target_deploy_user}'."
while [[ -z "${target_password}" ]]; do
echo
echo -n "> "
read -s target_password_1
echo
echo -n "> "
read -s target_password_2
echo
if [[ -n "${target_password_1}" && "${target_password_1}" -eq "${target_password_2}" ]]; then
target_password="${target_password_1}"
else
echo
echo "The specified passwords do not match or are empty."
fi
done
else
while ! wget -O /dev/null ${target_keys_path} &>/dev/null; do
echo
echo "Please enter a valid SSH keys URL."
echo
echo -n "> "
read target_keys_path
done
echo
echo "SSH key source '${target_keys_path}' successfully validated."
fi
echo
}
case ${install_option} in
on)
seed_config
;;
*)
interactive_config
;;
esac
titlestring_text="| Proceeding with installation of host '${target_hostname}'. |"
titlestring_len="$(( $( wc -c <<<"${titlestring_text}" ) - 2 ))"
for i in $( seq 0 ${titlestring_len} ); do echo -n "-"; done; echo
echo "${titlestring_text}"
for i in $( seq 0 ${titlestring_len} ); do echo -n "-"; done; echo
echo
### Script begins ###
echo "LOGFILE: ${logfile}"
echo
exec 1> >( tee -ia ${logfile} )
exec 2> >( tee -ia ${logfile} >/dev/null )
set -o errexit
set -o xtrace
case ${install_option} in
on)
seed_checkin start
;;
*)
# noop
true
;;
esac
cleanup() {
set +o errexit
echo -n "Cleaning up... "
umount ${target}/tmp >&2
umount ${target}/run >&2
umount ${target}/sys >&2
umount ${target}/proc >&2
umount ${target}/dev/pts >&2
umount ${target}/dev >&2
umount ${target}/var/lib/ceph >&2
umount ${target}/var/lib/zookeeper >&2
umount ${target}/boot/efi >&2
umount ${target}/boot >&2
umount ${target} >&2
vgchange -an >&2
rmdir ${target} >&2
rm ${lockfile}
echo "done."
echo
case ${install_option} in
on)
respawn
;;
*)
# noop
true
;;
esac
}
trap cleanup EXIT
echo -n "Determining partition sizes... "
blockdev_size_bytes="$( blockdev --getsize64 ${target_disk} )"
blockdev_size_gbytes="$(( ${blockdev_size_bytes} / 1024 / 1024 / 1024 - 1))"
if [[ ${blockdev_size_gbytes} -ge 100 ]]; then
# Optimal sized system disk (>=100GB), use large partitions
size_root_lv="32"
size_ceph_lv="8"
size_zookeeper_lv="32"
size_swap_lv="16"
echo "found large disk (${blockdev_size_gbytes} >= 100GB), using optimal partition sizes."
else
# Minimum sized disk (>=30GB), use small partitions
size_root_lv="8"
size_ceph_lv="4"
size_zookeeper_lv="8"
size_swap_lv="8"
echo "found small disk (${blockdev_size_gbytes} < 100GB), using small partition sizes."
fi
echo -n "Disabing existing volume groups... "
vgchange -an >&2 || true
echo "done."
blockcheck() {
# Use for testing only
if [[ -n ${SKIP_BLOCKCHECK} ]]; then
return
fi
# Determine optimal block size for zeroing
exponent=16
remainder=1
while [[ ${remainder} -gt 0 && ${exponent} -gt 0 ]]; do
exponent=$(( ${exponent} - 1 ))
size=$(( 2**9 * 2 ** ${exponent} ))
count=$(( ${blockdev_size_bytes} / ${size} ))
remainder=$(( ${blockdev_size_bytes} - ${count} * ${size} ))
done
if [[ ${remainder} -gt 0 ]]; then
echo "Failed to find a suitable block size for wiping... skipping."
return
fi
echo -n "Checking if block device '${target_disk}' is already wiped... "
if ! cmp --silent --bytes ${blockdev_size_bytes} /dev/zero ${target_disk}; then
echo "false."
echo "Wiping block device '${target_disk}' (${count} blocks of ${size} bytes)..."
dd if=/dev/zero of=${target_disk} bs=${size} count=${count} oflag=direct status=progress 2>&1
else
echo "done."
fi
}
blockcheck
echo -n "Preparing block device '${target_disk}'... "
# New GPT, part 1 64MB ESP, part 2 960MB BOOT, part 3 inf LVM PV
echo -e "o\ny\nn\n1\n\n64M\nEF00\nn\n2\n\n960M\n8300\nn\n3\n\n\n8E00\nw\ny\n" | gdisk ${target_disk} >&2
echo "done."
echo -n "Rescanning disks... "
partprobe >&2 || true
echo "done."
echo -n "Creating LVM PV... "
yes | pvcreate -ffy ${target_disk}3 >&2
echo "done."
echo -n "Creating LVM VG 'vgx'... "
yes | vgcreate vgx ${target_disk}3 >&2
echo "done."
echo -n "Creating root logical volume (${size_root_lv}GB)... "
yes | lvcreate -L ${size_root_lv}G -n root vgx >&2
echo "done."
echo -n "Creating filesystem on root logical volume (ext4)... "
yes | mkfs.ext4 /dev/vgx/root >&2
echo "done."
echo -n "Creating ceph logical volume (${size_ceph_lv}GB)... "
yes | lvcreate -L ${size_ceph_lv}G -n ceph vgx >&2
echo "done."
echo -n "Creating filesystem on ceph logical volume (ext4)... "
yes | mkfs.ext4 /dev/vgx/ceph >&2
echo "done."
echo -n "Creating zookeeper logical volume (${size_zookeeper_lv}GB)... "
yes | lvcreate -L ${size_zookeeper_lv}G -n zookeeper vgx >&2
echo "done."
echo -n "Creating filesystem on zookeeper logical volume (ext4)... "
yes | mkfs.ext4 /dev/vgx/zookeeper >&2
echo "done."
echo -n "Creating swap logical volume (${size_swap_lv}GB)... "
yes | lvcreate -L ${size_swap_lv}G -n swap vgx >&2
echo "done."
echo -n "Creating swap space on swap logical volume... "
yes | mkswap -f /dev/vgx/swap >&2
echo "done."
echo -n "Creating filesystem on boot partition (ext2)... "
yes | mkfs.ext2 ${target_disk}2 >&2
echo "done."
echo -n "Creating filesystem on ESP partition (vfat)... "
yes | mkdosfs -F32 ${target_disk}1 >&2
echo "done."
vgchange -ay >&2
target="/tmp/target"
mkdir -p ${target} >&2
echo -n "Mounting disks on temporary target '${target}'... "
mount /dev/vgx/root ${target} >&2
mkdir -p ${target}/boot >&2
chattr +i ${target}/boot >&2
mount ${target_disk}2 ${target}/boot >&2
mkdir -p ${target}/boot/efi >&2
chattr +i ${target}/boot/efi >&2
mount ${target_disk}1 ${target}/boot/efi >&2
mkdir -p ${target}/var/lib/ceph >&2
chattr +i ${target}/var/lib/ceph >&2
mount /dev/vgx/ceph ${target}/var/lib/ceph >&2
mkdir -p ${target}/var/lib/zookeeper >&2
chattr +i ${target}/var/lib/zookeeper >&2
mount /dev/vgx/zookeeper ${target}/var/lib/zookeeper >&2
mkdir -p ${target}/tmp >&2
chattr +i ${target}/tmp >&2
mount -t tmpfs tmpfs ${target}/tmp >&2
echo "done."
echo -n "Running debootstrap install... "
echo "Command: debootstrap --include=${inclpkglist} ${debrelease} ${target}/ ${debmirror}" >&2
debootstrap --include=${inclpkglist} ${debrelease} ${target}/ ${debmirror} >&2
echo "done."
echo -n "Adding non-free repository (firmware, etc.)... "
mkdir -p ${target}/etc/apt/sources.list.d/ >&2
echo "deb ${debmirror} ${debrelease} contrib non-free" | tee -a ${target}/etc/apt/sources.list >&2
chroot ${target} apt update >&2
echo "done."
echo -n "Installing supplemental packages... "
chroot ${target} apt install -y --no-install-recommends $( sed 's/,/ /g' <<<"${suppkglist}" ) >&2
echo "done."
# Determine the bypath name of the specified system disk
for disk in /dev/disk/by-path/*; do
bypathlink="$( realpath ${disk} | awk -F'/' '{ print $NF }' )"
enteredname="$( awk -F'/' '{ print $NF }' <<<"${target_disk}" )"
if [[ ${bypathlink} == ${enteredname} ]]; then
bypath_disk="${disk}"
fi
done
echo -n "Adding fstab entries... "
echo "/dev/mapper/vgx-root / ext4 errors=remount-ro 0 1" | tee -a ${target}/etc/fstab >&2
echo "/dev/mapper/vgx-ceph /var/lib/ceph ext4 errors=remount-ro 0 2" | tee -a ${target}/etc/fstab >&2
echo "/dev/mapper/vgx-zookeeper /var/lib/zookeeper ext4 errors=remount-ro 0 2" | tee -a ${target}/etc/fstab >&2
echo "/dev/mapper/vgx-swap none swap sw 0 0" | tee -a ${target}/etc/fstab >&2
echo "${bypath_disk}-part2 /boot ext2 defaults 0 2" | tee -a ${target}/etc/fstab >&2
echo "${bypath_disk}-part1 /boot/efi vfat umask=0077 0 2" | tee -a ${target}/etc/fstab >&2
echo "tmpfs /tmp tmpfs defaults 0 0" | tee -a ${target}/etc/fstab >&2
echo "done."
seed_interfaces_segment() {
# A seed install is always "dhcp-raw" since the provisioner is always a dedicated, access port
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet dhcp\npost-up /etc/network/pvcprovisionerd.checkin.sh \$IFACE"
}
interactive_interfaces_segment() {
case ${target_interfaces_is} in
static-vlan)
target_interfaces_block="auto ${vlan_interface}\niface ${vlan_interface} inet ${target_netformat}\n\tvlan_raw_device ${target_interface}\n\taddress ${formatted_ipaddr}\n\tnetmask ${formatted_netmask}\n\tgateway ${target_defgw}"
;;
static-raw)
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet ${target_netformat}\n\taddress ${formatted_ipaddr}\n\tnetmask ${formatted_netmask}\n\tgateway ${target_defgw}"
;;
dhcp-vlan)
target_interfaces_block="auto ${vlan_interface}\niface ${vlan_interface} inet ${target_netformat}\n\tvlan_raw_device${target_interface}"
;;
dhcp-raw)
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet ${target_netformat}"
;;
esac
}
echo -n "Creating bootstrap interface segment... "
case ${install_option} in
on)
seed_interfaces_segment
;;
*)
interactive_interfaces_segment
;;
esac
echo "done."
echo -n "Adding bootstrap interface segment... "
echo -e "${target_interfaces_block}" | tee ${target}/etc/network/interfaces.d/${target_interface} >&2
echo "done."
case ${install_option} in
on)
echo -n "Creating bond interface segment... "
bond_interfaces="$( ip -br link show | grep -E -o '^e[a-z]+[0-9]+[a-z0-9]*' | grep -v "^${target_interface}$" | tr '\n' ' ' )"
bond_interfaces_block="auto bond0\niface bond0 inet manual\n\tbond-mode 802.3ad\n\tbond-slaves ${bond_interfaces}\n\tpost-up ip link set mtu 9000 dev \$IFACE"
echo "done."
echo -n "Adding bond interface segment... "
echo -e "${bond_interfaces_block}" | tee ${target}/etc/network/interfaces.d/bond0 >&2
echo "done."
echo -n "Adding bootstrap interface post-up checkin script... "
cat <<EOF | tee ${target}/etc/network/pvcprovisionerd.checkin.sh >&2
#!/usr/bin/env bash
target_interface=\${1}
pvcbootstrapd_checkin_uri="${pvcbootstrapd_checkin_uri}"
macaddr=\$( ip -br link show \${target_interface} | awk '{ print \$3 }' )
ipaddr=\$( ip -br address show \${target_interface} | awk '{ print \$3 }' | awk -F '/' '{ print \$1 }' )
bmc_macaddr=\$( ipmitool lan print | grep 'MAC Address ' | awk '{ print $NF }' )
bmc_ipaddr=\$( ipmitool lan print | grep 'IP Address ' | awk '{ print $NF }' )
if [[ -f /etc/pvc-install.pvcbootstrapd_completed ]]; then
# The third boot, when all pvcprovisionerd plugins have been run (this script will henceforth do nothing)
action="system-boot_bootstrap-completed"
elif [[ -f /etc/pvc-install.base && -f /etc/pvc-install.pvc ]]; then
# The second boot, when Ansible has been run and plugins running
action="system-boot_ansible-completed"
touch /etc/pvc-install.pvcbootstrapd_completed
else
# The first boot, when Ansible has not been run yet
action="system-boot_initial"
fi
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"action\":\"\${action}\",\"macaddr\":\"\${macaddr}\",\"ipaddr\":\"\${ipaddr}\",\"hostname\":\"\$( hostname -s )\",\"bmc_macaddr\":\"\${bmc_macaddr}\",\"bmc_ipaddr\":\"\${bmc_ipaddr}\"}" \
\${pvcbootstrapd_checkin_uri}
if [[ -f /etc/pvc-install.pvcbootstrapd_completed ]]; then
# Clean up the bootstrap interface and this script
rm /etc/network/interfaces.d/\${target_interface}
rm \$0
exit 0
fi
EOF
chmod +x ${target}/etc/network/pvcprovisionerd.checkin.sh
echo "done."
;;
*)
# noop
true
;;
esac
echo "done."
echo -n "Setting temporary 'root' password... "
echo "root:${root_password}" | chroot ${target} chpasswd >&2
echo "done."
echo -n "Adding deployment user... "
mv ${target}/home ${target}/var/home >&2
chroot ${target} useradd -u 200 -d /var/home/${target_deploy_user} -m -s /bin/bash -g operator -G sudo ${target_deploy_user} >&2
chroot ${target} mkdir -p /var/home/${target_deploy_user}/.ssh
if [[ -n ${target_keys_path} ]]; then
case ${target_keys_method} in
wget)
wget -O ${target}/var/home/${target_deploy_user}/.ssh/authorized_keys ${target_keys_path}
;;
tftp)
tftp -m binary "${seed_host}" -c get "${target_keys_path}" ${target}/var/home/${target_deploy_user}/.ssh/authorized_keys
;;
esac
chroot ${target} chmod 0600 /var/home/${target_deploy_user}/.ssh/authorized_keys
chroot ${target} chown -R ${target_deploy_user}:operator /var/home/${target_deploy_user}
else
echo "${target_deploy_user}:${target_password}" | chroot ${target} chpasswd >&2
fi
echo "done."
echo -n "Setting NOPASSWD for sudo group... "
sed -i 's/^%sudo\tALL=(ALL:ALL) ALL/%sudo\tALL=(ALL:ALL) NOPASSWD: ALL/' ${target}/etc/sudoers
echo "done."
echo -n "Setting /etc/issue generator... "
mkdir -p ${target}/etc/network/if-up.d >&2
echo -e "#!/bin/sh
IP=\"\$( ip -4 addr show dev ${target_interface} | grep inet | awk '{ print \$2 }' | head -1 )\"
cat <<EOF >/etc/issue
Debian GNU/Linux 10 \\\\n \\\\l
Bootstrap interface IP address: \$IP
EOF" | tee ${target}/etc/network/if-up.d/issue-gen >&2
chmod +x ${target}/etc/network/if-up.d/issue-gen 1>&2
echo "done."
echo -n "Generating host rsa and ed25519 keys... "
rm ${target}/etc/ssh/ssh_host_*_key* >&2
chroot ${target} ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key >&2
chroot ${target} ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key >&2
echo "done."
echo -n "Setting hostname... "
echo "${target_hostname}" | tee ${target}/etc/hostname >&2
echo "done."
echo -n "Setting resolv.conf... "
echo "nameserver 8.8.8.8" | tee ${target}/etc/resolv.conf >&2
echo "done."
echo -n "Installing GRUB bootloader... "
mount --bind /dev ${target}/dev >&2
mount --bind /dev/pts ${target}/dev/pts >&2
mount --bind /proc ${target}/proc >&2
mount --bind /sys ${target}/sys >&2
mount --bind /run ${target}/run >&2
if [[ -d /sys/firmware/efi ]]; then
bios_target="x86_64-efi"
else
bios_target="i386-pc"
fi
cat <<EOF | tee ${target}/etc/default/grub >&2
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Parallel Virtual Cluster (PVC) - Debian"
GRUB_CMDLINE_LINUX="console=hvc0 console=tty0 console=ttyS0,115200"
GRUB_TERMINAL_INPUT="console serial"
GRUB_TERMINAL_OUTPUT="gfxterm serial"
GRUB_SERIAL_COMMAND="serial --unit=0 --unit=1 --speed=115200"
EOF
chroot ${target} grub-install --force --target=${bios_target} ${target_disk} >&2
chroot ${target} grub-mkconfig -o /boot/grub/grub.cfg >&2
echo "done."
seed_postinst() {
cleanup
echo "Temporary root password: ${root_password}"
seed_checkin end
echo -n "Rebooting in 10 seconds..."
i=10
while [[ ${i} -gt 0 ]]; do
sleep 1
i=$(( ${1} - 1 ))
echo -n "."
done
echo
reboot
}
interactive_postinst() {
set +o errexit
echo
echo -n "Edit the /etc/network/interfaces file in the target before completing setup? If you plan to use bonding, it is prudent to set this up in basic form now! [y/N] "
read edit_ifaces
if [[ ${edit_ifaces} == 'y' || ${edit_ifaces} == 'Y' ]]; then
vim ${target}/etc/network/interfaces
fi
echo
echo -n "Launch a chroot shell in the target environment? [y/N] "
read launch_chroot
if [[ ${launch_chroot} == 'y' || ${edit_ifaces} == 'Y' ]]; then
echo "Type 'exit' or Ctrl+D to exit chroot."
chroot ${target} /bin/bash
fi
cleanup
echo "-------------------------------------------------------------------------------------"
echo "| PVC node installation finished. Next steps: |"
echo "| 1. Press <enter> to reboot the system. |"
echo "| 2. Boot the system verify SSH access (IP shown on login screen). |"
echo "| 3. Proceed with system deployment via PVC Ansible. |"
echo "| |"
echo "| The INSECURE temporary root password if the system will not boot is: ${root_password} |"
echo "-------------------------------------------------------------------------------------"
echo
read
reboot
}
case ${install_option} in
on)
seed_postinst
;;
*)
interactive_postinst
;;
esac

View File

@ -0,0 +1 @@
live-task-standard live-tools live-boot live-boot-initramfs-tools linux-image-amd64 psmisc mdadm lvm2 parted gdisk dosfstools debootstrap grub-pc-bin grub-efi-amd64 sipcalc vim ca-certificates vlan tftp-hpa curl ipmitool

2
templates/logind.conf Normal file
View File

@ -0,0 +1,2 @@
[Login]
NAutoVTs=2

1
templates/resolv.conf Normal file
View File

@ -0,0 +1 @@
nameserver 8.8.8.8

1
templates/root.bashrc Normal file
View File

@ -0,0 +1 @@
/install.sh

View File

@ -0,0 +1,3 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -- \\u' --autologin root --noclear --keep-baud 115200,38400,9600 %I $TERM

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB