|
|
|
@ -1,17 +1,35 @@
|
|
|
|
|
#!/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
|
|
|
|
|
|
|
|
|
|
logfile="/tmp/pvc-install.log"
|
|
|
|
|
# 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"
|
|
|
|
|
debpkglist="lvm2,parted,gdisk,grub-pc,grub-efi-amd64,linux-image-amd64,sudo,vim,gpg,gpg-agent,aptitude,openssh-server,vlan,ifenslave,python2,python3,ca-certificates,ntp"
|
|
|
|
|
suppkglist="firmware-linux,firmware-linux-nonfree,firmware-bnx2,firmware-bnx2x"
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@ -20,10 +38,92 @@ suppkglist="firmware-linux,firmware-linux-nonfree,firmware-bnx2,firmware-bnx2x"
|
|
|
|
|
# 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 (XXDATEXX) |"
|
|
|
|
|
echo "| PVC Node installer (${iso_name}) |"
|
|
|
|
|
echo "--------------------------------------------------------"
|
|
|
|
|
echo
|
|
|
|
|
echo "This LiveCD will install a PVC node base system ready for bootstrapping with 'pvc-ansible'."
|
|
|
|
@ -80,8 +180,8 @@ while [[ ! -b ${target_disk} ]]; do
|
|
|
|
|
echo "Please enter a valid target disk."
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
blockdev_size="$(( $( blockdev --getsize64 ${target_disk} ) / 1024 / 1024 / 1024 - 1))"
|
|
|
|
|
if [[ ${blockdev_size} -lt 30 ]]; then
|
|
|
|
|
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."
|
|
|
|
@ -183,16 +283,16 @@ case ${target_netformat} in
|
|
|
|
|
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 }' )"
|
|
|
|
|
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}"
|
|
|
|
|
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 }' )"
|
|
|
|
|
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet ${target_netformat}\n\taddress ${formatted_ipaddr}\n\tnetmask ${formatted_netmask}\n\tgateway ${target_defgw}"
|
|
|
|
|
real_interface="${target_interface}"
|
|
|
|
|
target_interfaces_is="static-raw"
|
|
|
|
|
fi
|
|
|
|
|
cat <<EOF >/etc/resolv.conf
|
|
|
|
|
nameserver 8.8.8.8
|
|
|
|
@ -203,13 +303,13 @@ EOF
|
|
|
|
|
modprobe 8021q >&2
|
|
|
|
|
vconfig add ${target_interface} ${vlan_id} &>/dev/null
|
|
|
|
|
vlan_interface=${target_interface}.${vlan_id}
|
|
|
|
|
target_interfaces_block="auto ${vlan_interface}\niface ${vlan_interface} inet ${target_netformat}\n\tvlan_raw_device${target_interface}"
|
|
|
|
|
dhclient ${vlan_interface} >&2
|
|
|
|
|
real_interface="${vlan_interface}"
|
|
|
|
|
target_interfaces_is="dhcp-vlan"
|
|
|
|
|
else
|
|
|
|
|
target_interfaces_block="auto ${target_interface}\niface ${target_interface} inet ${target_netformat}"
|
|
|
|
|
dhclient ${target_interface} >&2
|
|
|
|
|
real_interface="${target_interface}"
|
|
|
|
|
target_interfaces_is="dhcp-raw"
|
|
|
|
|
fi
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
@ -262,6 +362,7 @@ while [[ -z ${debmirror} ]]; do
|
|
|
|
|
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."
|
|
|
|
@ -269,12 +370,12 @@ echo ""
|
|
|
|
|
echo "Leave blank to bypass this and use a password instead."
|
|
|
|
|
echo
|
|
|
|
|
echo -n "> "
|
|
|
|
|
read target_keys_url
|
|
|
|
|
if [[ -z ${target_keys_url} ]]; then
|
|
|
|
|
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 'XXDEPLOYUSERXX'."
|
|
|
|
|
echo "5) Please enter a password (hidden), twice, for the deployment user '${target_deploy_user}'."
|
|
|
|
|
while [[ -z "${target_password}" ]]; do
|
|
|
|
|
echo
|
|
|
|
|
echo -n "> "
|
|
|
|
@ -291,17 +392,27 @@ if [[ -z ${target_keys_url} ]]; then
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
else
|
|
|
|
|
while ! wget -O /dev/null ${target_keys_url} &>/dev/null; do
|
|
|
|
|
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_url
|
|
|
|
|
read target_keys_path
|
|
|
|
|
done
|
|
|
|
|
echo
|
|
|
|
|
echo "SSH key source '${target_keys_url}' successfully validated."
|
|
|
|
|
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 ))"
|
|
|
|
@ -314,13 +425,25 @@ echo
|
|
|
|
|
echo "LOGFILE: ${logfile}"
|
|
|
|
|
echo
|
|
|
|
|
|
|
|
|
|
set -o errexit
|
|
|
|
|
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
|
|
|
|
@ -333,36 +456,74 @@ cleanup() {
|
|
|
|
|
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 block device and partition sizing... "
|
|
|
|
|
blockdev_size="$(( $( blockdev --getsize64 ${target_disk} ) / 1024 / 1024 / 1024 - 1))"
|
|
|
|
|
if [[ ${blockdev_size} -ge 100 ]]; then
|
|
|
|
|
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 (>=100GB), using optimal partition sizes."
|
|
|
|
|
elif [[ ${blockdev_size} -ge 30 ]]; then
|
|
|
|
|
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 (>=30GB), using small partition sizes."
|
|
|
|
|
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."
|
|
|
|
|
|
|
|
|
|
echo -n "Zeroing block device '${target_disk}'... "
|
|
|
|
|
dd if=/dev/zero of=${target_disk} bs=4M >&2 || true
|
|
|
|
|
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 -n "Wiping block device '${target_disk}' (${count} blocks of ${size} bytes)... "
|
|
|
|
|
dd if=/dev/zero of=${target_disk} bs=${size} count=${count} oflag=direct &>/dev/null
|
|
|
|
|
fi
|
|
|
|
|
echo "done."
|
|
|
|
|
}
|
|
|
|
|
blockcheck
|
|
|
|
|
|
|
|
|
|
echo -n "Preparing block device '${target_disk}'... "
|
|
|
|
|
# New GPT, part 1 64MB ESP, part 2 960MB BOOT, part 3 inf LVM PV
|
|
|
|
@ -377,12 +538,12 @@ echo -n "Creating LVM PV... "
|
|
|
|
|
yes | pvcreate -ffy ${target_disk}3 >&2
|
|
|
|
|
echo "done."
|
|
|
|
|
|
|
|
|
|
echo -n "Creating LVM VG named 'vgx'... "
|
|
|
|
|
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)... "
|
|
|
|
|
lvcreate -L ${size_root_lv}G -n root vgx >&2
|
|
|
|
|
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
|
|
|
|
@ -392,18 +553,18 @@ 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)... "
|
|
|
|
|
mkfs.ext4 /dev/vgx/ceph >&2
|
|
|
|
|
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)... "
|
|
|
|
|
mkfs.ext4 /dev/vgx/zookeeper >&2
|
|
|
|
|
yes | mkfs.ext4 /dev/vgx/zookeeper >&2
|
|
|
|
|
echo "done."
|
|
|
|
|
|
|
|
|
|
echo -n "Creating swap logical volume (${size_swap_lv}GB)... "
|
|
|
|
|
lvcreate -L ${size_swap_lv}G -n swap vgx >&2
|
|
|
|
|
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
|
|
|
|
@ -417,8 +578,10 @@ echo -n "Creating filesystem on ESP partition (vfat)... "
|
|
|
|
|
yes | mkdosfs -F32 ${target_disk}1 >&2
|
|
|
|
|
echo "done."
|
|
|
|
|
|
|
|
|
|
echo -n "Mounting disks on temporary target... "
|
|
|
|
|
target=$( mktemp -d )
|
|
|
|
|
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
|
|
|
|
@ -438,7 +601,8 @@ mount -t tmpfs tmpfs ${target}/tmp >&2
|
|
|
|
|
echo "done."
|
|
|
|
|
|
|
|
|
|
echo -n "Running debootstrap install... "
|
|
|
|
|
debootstrap --include=${debpkglist} ${debrelease} ${target}/ ${debmirror} >&2
|
|
|
|
|
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.)... "
|
|
|
|
@ -453,7 +617,7 @@ echo "done."
|
|
|
|
|
|
|
|
|
|
# Determine the bypath name of the specified system disk
|
|
|
|
|
for disk in /dev/disk/by-path/*; do
|
|
|
|
|
bypathlink="$( readlink ${disk} | awk -F'/' '{ print $NF }' )"
|
|
|
|
|
bypathlink="$( realpath ${disk} | awk -F'/' '{ print $NF }' )"
|
|
|
|
|
enteredname="$( awk -F'/' '{ print $NF }' <<<"${target_disk}" )"
|
|
|
|
|
if [[ ${bypathlink} == ${enteredname} ]]; then
|
|
|
|
|
bypath_disk="${disk}"
|
|
|
|
@ -470,8 +634,94 @@ echo "${bypath_disk}-part1 /boot/efi vfat umask=0077 0 2" | tee -a ${target}/etc
|
|
|
|
|
echo "tmpfs /tmp tmpfs defaults 0 0" | tee -a ${target}/etc/fstab >&2
|
|
|
|
|
echo "done."
|
|
|
|
|
|
|
|
|
|
echo -n "Adding interface segment... "
|
|
|
|
|
echo -e "${target_interfaces_block}" | tee -a ${target}/etc/network/interfaces >&2
|
|
|
|
|
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... "
|
|
|
|
@ -480,14 +730,21 @@ echo "done."
|
|
|
|
|
|
|
|
|
|
echo -n "Adding deployment user... "
|
|
|
|
|
mv ${target}/home ${target}/var/home >&2
|
|
|
|
|
chroot ${target} useradd -u 200 -d /var/home/XXDEPLOYUSERXX -m -s /bin/bash -g operator -G sudo XXDEPLOYUSERXX >&2
|
|
|
|
|
chroot ${target} mkdir -p /var/home/XXDEPLOYUSERXX/.ssh
|
|
|
|
|
if [[ -n ${target_keys_url} ]]; then
|
|
|
|
|
wget -O ${target}/var/home/XXDEPLOYUSERXX/.ssh/authorized_keys ${target_keys_url}
|
|
|
|
|
chroot ${target} chmod 0600 /var/home/XXDEPLOYUSERXX/.ssh/authorized_keys
|
|
|
|
|
chroot ${target} chown -R XXDEPLOYUSERXX:operator /var/home/XXDEPLOYUSERXX
|
|
|
|
|
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 "XXDEPLOYUSERXX:${target_password}" | chroot ${target} chpasswd >&2
|
|
|
|
|
echo "${target_deploy_user}:${target_password}" | chroot ${target} chpasswd >&2
|
|
|
|
|
fi
|
|
|
|
|
echo "done."
|
|
|
|
|
|
|
|
|
@ -498,11 +755,11 @@ 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 ${real_interface} | grep inet | awk '{ print \$2 }' | head -1 )\"
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
Primary interface IP address: \$IP
|
|
|
|
|
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
|
|
|
|
@ -533,10 +790,36 @@ if [[ -d /sys/firmware/efi ]]; then
|
|
|
|
|
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] "
|
|
|
|
@ -567,3 +850,13 @@ echo
|
|
|
|
|
read
|
|
|
|
|
|
|
|
|
|
reboot
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ${install_option} in
|
|
|
|
|
on)
|
|
|
|
|
seed_postinst
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
interactive_postinst
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|