terom@0: #!/bin/bash terom@0: terom@7: ### Initialize terom@7: set -u terom@7: set -e terom@0: terom@7: TESTING= terom@7: terom@9: DO_SHOWSPEC=y terom@7: DO_VIRTINSTALL= terom@7: terom@7: scripts=$(dirname $0)/scripts terom@7: . $scripts/lib.sh terom@7: terom@7: ### Command-line input terom@7: ## Command-line options terom@7: function _help () { terom@7: cat < [param=value [...]] terom@7: terom@7: Options: terom@7: -h Show this help text terom@7: terom@7: -D Debug terom@7: -v Verbose terom@7: -q Quiet terom@7: terom@7: -N Mock command execution terom@7: -P Skip command prompts terom@7: terom@7: -I Do a virt-install terom@7: terom@7: -T Just testing.. terom@7: terom@7: Parameters are given as: terom@7: terom@14: name=value-{FOO} terom@7: terom@7: END terom@7: } terom@7: terom@7: while getopts "hDvqNPTI" opt; do terom@7: case "$opt" in terom@7: h) terom@7: _help $0 terom@7: terom@7: exit 0 terom@7: ;; terom@7: terom@7: D) terom@7: LOG_DEBUG=y terom@7: LOG_CMD=y terom@7: terom@7: ;; terom@7: terom@7: v) terom@7: LOG_CMD=y terom@7: terom@7: log_debug "log: Commands" terom@7: ;; terom@7: terom@7: q) terom@7: LOG_CMD= terom@7: LOG_INFO= terom@9: DO_SHOWSPEC= terom@7: terom@7: log_debug "log: Quiet" terom@7: ;; terom@7: terom@7: N) terom@7: log_info "Mock-executing commands (this will break functionality..)" terom@7: terom@7: CMD_MOCK=y terom@7: ;; terom@7: terom@7: P) terom@7: log_info "Skipping command confirmations" terom@7: terom@7: CMD_PROMPT= terom@7: ;; terom@7: terom@7: T) terom@7: log_info "Just testing.." terom@7: terom@7: TESTING=y terom@7: ;; terom@7: terom@7: I) terom@7: log_info "Do virt-install" terom@7: terom@7: DO_VIRTINSTALL=y terom@7: ;; terom@7: ?) terom@7: _help $0 terom@7: terom@7: exit 1 terom@7: ;; terom@7: esac terom@7: done terom@7: terom@9: terom@7: # forget them terom@9: [ $OPTIND -gt 1 ] && shift $(( $OPTIND - 1 )) terom@7: terom@7: ## Command-line arguments terom@8: # Name terom@8: [ -z $1 ] && die "Machine name must be given as first argument" terom@8: terom@8: opt_name="$1"; shift terom@8: terom@8: # Defaults terom@8: function define_opt () { terom@8: local name=$1 terom@8: local value=${2:-} terom@8: terom@8: log_debugf "%-20s = %s" $name "$value" terom@8: eval "opt_${name}=${value}" terom@8: } terom@8: terom@8: function resolve_name () { terom@8: local name=$1 terom@8: local out=$(dig +short $name) terom@8: terom@8: [ -z "$out" ] && die "Hostname lookup failed: $name" terom@8: terom@8: echo $out terom@8: } terom@8: terom@8: define_opt ram 1G terom@8: define_opt cpus 2 terom@8: define_opt os debiansqueeze terom@8: define_opt disk_size 10G terom@8: define_opt disk_vg pvl terom@8: define_opt disk_lv $opt_name terom@8: define_opt disk_bus virtio terom@8: define_opt guest_disk /dev/vda terom@8: define_opt hostname $opt_name terom@8: define_opt bridge br-lan terom@18: define_opt domain paivola.fi terom@9: define_opt ip terom@14: define_opt puppet terom@15: define_opt puppet_master puppet terom@16: define_opt serial_console terom@8: terom@8: log_info "Processing ${#@} parameters:" terom@7: for param in "$@"; do terom@7: name=${param%=*} terom@7: value=${param##*=} terom@7: terom@7: # evaluate terom@7: value=$(expand_line "$value") terom@7: terom@8: define_opt ${name} "${value}" terom@7: done terom@7: terom@9: # resolve defaults terom@9: if [ -z $opt_ip ]; then terom@18: define_opt ip $(resolve_name ${opt_name}.${opt_domain}) terom@9: fi terom@9: terom@14: ### Virtual machine config terom@18: ## General terom@18: NAME=$opt_name terom@18: DOMAIN=$opt_domain terom@18: FQDN=${NAME}.${DOMAIN} terom@18: terom@14: ## libvirt guest info terom@14: # Name terom@18: GUEST_NAME=$NAME terom@1: terom@1: # Basic params terom@8: GUEST_RAM=$opt_ram terom@8: GUEST_VCPUS=$opt_cpus terom@1: terom@1: # OS variant (for virt-install) terom@8: GUEST_OS_VARIANT=$opt_os terom@1: terom@1: ## Disk terom@1: # Size of LV to create terom@8: DISK_SIZE=$opt_disk_size terom@0: terom@1: # LVM vg to use terom@8: DISK_VG=$opt_disk_vg terom@0: terom@1: # LVM lv to use terom@8: DISK_NAME=$opt_disk_lv terom@0: terom@1: # Path to disk block device terom@1: DISK_PATH=/dev/mapper/${DISK_VG}-${DISK_NAME} terom@14: GUEST_DISK_BUS=$opt_disk_bus terom@14: terom@16: ## Serial terom@16: # Serial console? terom@16: # XXX: hardcoded as ttyS0 terom@16: case x"$opt_serial_console" in terom@16: # xtty*) SERIAL_CONSOLE="$opt_serial_console" ;; terom@16: x) SERIAL_CONSOLE= ;; terom@16: x*) SERIAL_CONSOLE="ttyS0" ;; terom@16: esac terom@16: terom@14: ### Preseed content terom@8: GUEST_DISK=$opt_guest_disk terom@0: terom@1: ## Network terom@13: # Network configuration, for /etc/network/interfaces terom@18: NET_DOMAIN=${DOMAIN} terom@18: NET_HOSTNAME=${NAME} terom@8: NET_BRIDGE=$opt_bridge terom@8: NET_IPADDR=$opt_ip terom@13: NET_NETMASK=255.255.255.0 terom@13: NET_GATEWAY=194.197.235.1 terom@13: NET_NAMESERVERS=( 194.197.235.210 194.197.235.252 ) terom@1: terom@13: ## Clock/time terom@13: TIME_ZONE='Europe/Helsinki' terom@13: terom@13: # only used during install, not stored in target terom@13: TIME_NTP_SERVER=ntp.paivola.fi # XXX: harcoded terom@13: terom@13: ## User account terom@13: function getent_user_attr () { terom@13: local user=$1 terom@13: local db=$2 terom@13: local attr=$3 terom@13: terom@13: line=$(getent $db $user) || die "Unable to read $db database for $user" terom@13: terom@13: echo "$line" | cut -d ':' -f $attr terom@13: } terom@13: function user_fullname () { terom@13: local user=$1 terom@13: terom@13: getent_user_attr $user passwd 5 terom@13: } terom@13: function user_shadow () { terom@13: local user=$1 terom@13: terom@13: if [ $UID -eq 0 ]; then terom@13: log_debug "Get user password from shadow: $user" terom@13: getent_user_attr $user shadow 2 terom@13: else terom@14: echo -n "Install target login ($USER_NAME) " >&2 terom@13: mkpasswd -m sha-512 terom@13: fi terom@13: } terom@13: terom@13: # XXX: hardcoded terom@13: USER_CREATE='true' terom@13: USER_NAME=$USER terom@14: USER_FULLNAME=$(user_fullname $USER) terom@14: USER_SHADOW=$(user_shadow $USER) terom@14: USER_GROUPS=( cdrom sudo adm ) terom@13: terom@18: ### Installer setup terom@18: ## Installation image terom@18: # Original Debian Installer image (iso) terom@18: INSTALLER_NAME="debian-6.0.3-amd64" terom@18: INSTALLER_ISO="iso-in/${INSTALLER_NAME}-netinst.iso" terom@18: INSTALLER_TREE="iso-in/$INSTALLER_NAME" terom@18: INSTALLER_FLAG="${INSTALLER_TREE}.unpacked" terom@18: terom@18: # Customized preseed image name terom@18: INSTALL_NAME="debian-6.0.3-amd64_${GUEST_NAME}" terom@18: terom@18: # Customized image content terom@18: INSTALL_TREE="images/${INSTALL_NAME}" terom@18: INSTALL_ISO="iso-out/${INSTALL_NAME}.iso" terom@18: terom@18: terom@18: ### Preseed setup terom@18: ## preseed.cfg templating terom@20: PRESEED_DIR="preseed" terom@18: terom@18: # Preseed output file in install tree terom@18: PRESEED_NAME="preseed.cfg" terom@18: terom@18: # Mount path of preseed target in installer terom@18: PRESEED_MOUNT="/cdrom" terom@18: terom@18: # Prefix for target files in install tree terom@18: # XXX: not implemented terom@18: #PRESEED_TARGET_PREFIX="" terom@18: terom@18: # Main preseed source template terom@20: PRESEED_TEMPLATE="${PRESEED_DIR}/${PRESEED_NAME}" terom@18: terom@18: # Target path for preseed in install tree terom@18: PRESEED_FILE="${INSTALL_TREE}/${PRESEED_NAME}" terom@18: terom@18: # Checksum of target preseed.cfg terom@18: PRESEED_CHECKSUM= # set later terom@18: terom@18: terom@18: ## preseed.cfg contents terom@18: # List of additional packages to install terom@18: PRESEED_PACKAGES=( sudo screen vim ) terom@18: terom@18: # Script commands to execute terom@14: PRESEED_LATE_COMMANDS=( ) terom@16: PRESEED_LATE_COMMANDS_END=( ) terom@18: terom@18: # Chainload preseed files terom@14: PRESEED_INCLUDES=( 'passwords.cfg' 'host.cfg' ) terom@13: terom@18: # Add packages to preseed install terom@18: function preseed_packages () { terom@18: PRESEED_PACKAGES=( ${PRESEED_PACKAGES[@]} "$@" ) terom@18: } terom@18: terom@18: # Add command to execute terom@16: function preseed_late_commands () { terom@16: PRESEED_LATE_COMMANDS=( "${PRESEED_LATE_COMMANDS[@]:-}" "$@" ) terom@16: } terom@16: terom@18: # Add command to execute at end terom@16: # XXX: ordering? terom@16: function preseed_late_commands_end () { terom@16: PRESEED_LATE_COMMANDS_END=( "${PRESEED_LATE_COMMANDS_END[@]:-}" "$@" ) terom@16: } terom@16: terom@15: terom@18: ## preseed-files terom@18: # template source terom@20: PRESEED_FILES_SOURCES=( 'preseed/files' ) terom@18: terom@18: # template output into install tree terom@20: PRESEED_FILES_TARGET_NAME='preseed-files' terom@20: PRESEED_FILES_TARGET="${INSTALL_TREE}/${PRESEED_FILES_TARGET_NAME}" terom@18: terom@18: # paths within installer runtime terom@20: PRESEED_FILES_INSTALLER_SOURCE="${PRESEED_MOUNT}/${PRESEED_FILES_TARGET_NAME}" terom@20: PRESEED_FILES_INSTALLER_TARGET='/target' terom@20: terom@20: # Add a tree of configuration files to template into the installer terom@20: function preseed_conf_files () { terom@20: PRESEED_FILES_SOURCES=( "${PRESEED_FILES_SOURCES[@]:-}" "$@" ) terom@20: } terom@18: terom@18: # Add a file to install in preseed, without templating terom@18: # preseed_file / terom@18: # if dst is a dir, it must end in / terom@18: function preseed_file () { terom@18: local src=$1 terom@18: local dst=$2 terom@18: local dir=$(dirname $dst) terom@20: local tgt=${PRESEED_FILES_TARGET} terom@18: terom@18: local tgt_dir="$tgt/$dir" terom@18: terom@18: if [ ! -d "$tgt_dir" ]; then terom@18: cmd mkdir -p "$tgt_dir" terom@18: fi terom@18: terom@18: cmd cp "$src" "$tgt/$dst" terom@18: } terom@18: terom@15: ### Extra terom@15: ## Puppet terom@18: PUPPET= terom@18: terom@14: if [ $opt_puppet ]; then terom@14: log_info "Puppetizing preseed" terom@18: PUPPET=yes terom@20: terom@20: PUPPET_PRESEED_DIR="${PRESEED_DIR}/puppet" terom@14: terom@20: ## Packages terom@18: preseed_packages puppet terom@14: terom@18: ## Vars for preseed-files terom@18: # hostname for puppetmaster (server) terom@15: PUPPET_MASTER="${opt_puppet_master}" terom@15: terom@18: # path ssl data (ssldir) terom@18: PUPPET_SSLDIR=/etc/puppet/ssl terom@20: terom@20: ## Preseed files terom@20: # add to list of conf files to copy terom@20: preseed_conf_files "${PUPPET_PRESEED_DIR}/files" terom@20: terom@18: fi terom@16: terom@18: # Invoked during image-customizing process terom@18: function puppet_config () { terom@18: ## Preseed ssl certs? terom@20: PUPPET_SOURCE_SSLDIR="${PUPPET_PRESEED_DIR}/ssl" terom@18: terom@18: # copy file to preseed if exists terom@18: function puppet_preseed_ssl_file () { terom@18: local name=$1 terom@18: terom@18: local src=${PUPPET_SOURCE_SSLDIR}/$name terom@18: local dst=${PUPPET_SSLDIR}/$name terom@18: terom@18: if [ -f $src ]; then terom@18: log_info "puppet: preseed ssl data: $name" terom@18: terom@18: cmd preseed_file $src $dst terom@18: else terom@18: log_debug "puppet: skip ssl preseed: $name" terom@18: fi terom@18: } terom@18: terom@18: # ca.pem terom@18: puppet_preseed_ssl_file certs/ca.pem terom@18: terom@18: # guest cert/pkey terom@18: puppet_preseed_ssl_file certs/${FQDN}.pem terom@18: puppet_preseed_ssl_file private_keys/${FQDN}.pem terom@18: } terom@14: terom@16: ## Configure GRUB, via preseed/files: /etc/default/grub terom@16: # Kernel commandline/grub terminal terom@16: # The last console=... for kernel is used as /dev/console, i.e. init output terom@16: terom@16: if [ $SERIAL_CONSOLE ]; then terom@16: BOOT_KERNEL_CONSOLE="console=${SERIAL_CONSOLE} console=tty0" terom@16: BOOT_GRUB_TERMINAL="console serial" terom@16: terom@16: # re-generate grub.cfg terom@16: preseed_late_commands_end "in-target update-grub" terom@16: terom@16: # Configure /etc/inittab for serial console terom@16: preseed_late_commands "in-target sed -i 's/#T0/T0/' /etc/inittab" terom@16: else terom@16: BOOT_KERNEL_CONSOLE="" terom@16: BOOT_GRUB_TERMINAL="console" terom@16: fi terom@16: terom@16: # Kernel boot args (overrides those generated by installer) - default boot option uses args + args_default terom@16: BOOT_KERNEL_ARGS_DEFAULT="quiet" terom@16: BOOT_KERNEL_ARGS="${BOOT_KERNEL_CONSOLE}" terom@16: terom@18: ### Postprocess preseed terom@13: ## Preseed files terom@18: ## Preseed / config files terom@14: terom@14: terom@15: terom@16: # copy at end of install terom@20: preseed_late_commands "cp -rd -- ${PRESEED_FILES_INSTALLER_SOURCE}/* ${PRESEED_FILES_INSTALLER_TARGET}" terom@15: terom@14: # Additional files to copy terom@20: PRESEED_INCLUDE_FILES=( $(for preseed in ${PRESEED_INCLUDES[@]}; do echo "${PRESEED_DIR}/${preseed}"; done) ) terom@14: #PRESEED_INCLUDE_FILES=("preseed/passwords.cfg" "preseed/host.cfg") terom@14: terom@14: # preseed.cfg 'includes' line terom@14: PRESEED_INCLUDE=${PRESEED_INCLUDES[@]} terom@14: terom@15: # preseed command execution terom@16: PRESEED_LATE_COMMAND=$(for cmd in "${PRESEED_LATE_COMMANDS[@]}" "${PRESEED_LATE_COMMANDS_END[@]:-}"; do if [ "$cmd" ]; then echo -n "$cmd;" $'\\\n '; fi; done; echo true) terom@15: terom@14: terom@14: ## Isolinux terom@14: # Source for install tree isolinux file terom@20: PRESEED_ISOLINUX="${PRESEED_DIR}/isolinux.cfg" terom@14: terom@16: # Automagics for serial boot :) terom@14: # virt-install assumes we have an X $DISPLAY if given --vnc terom@14: # but we also want to enable the serial console... terom@14: # d-i automagically configures the serial boot/login console, but only if the installer is run under serial... terom@14: # we want to have both :( terom@16: if [ ${DISPLAY:-} ]; then terom@14: INSTALL_BOOT_CONSOLE="" terom@14: else terom@14: INSTALL_BOOT_CONSOLE="console=ttyS0" terom@14: fi terom@14: terom@16: # isolinux installer boot args terom@16: INSTALL_BOOT_ARGS="auto=true priority=critical preseed/file=${PRESEED_MOUNT}/${PRESEED_NAME} preseed/file/checksum=${PRESEED_CHECKSUM} -- ${INSTALL_BOOT_CONSOLE} quiet" terom@13: terom@13: ### External progs terom@13: ## Bootable .iso for Debian isolinux-based installer CDs terom@1: GENISOIMAGE=/usr/bin/genisoimage terom@1: GENISOIMAGE_OPTS="-r -J -no-emul-boot -boot-load-size 4 -boot-info-table -b isolinux/isolinux.bin -c isolinux/boot.cat" terom@1: terom@13: ## LVM terom@1: LVM=/sbin/lvm terom@1: terom@13: ## Libvirt terom@13: # --connect URL terom@1: LIBVIRT=qemu:///system terom@1: VIRSH=/usr/bin/virsh terom@1: terom@1: function virsh () { terom@1: $VIRSH --connect $LIBVIRT "$@" terom@1: } terom@1: terom@1: # type of guest to create terom@1: LIBVIRT_TYPE=kvm terom@1: terom@13: ## virt-install terom@1: VIRT_INSTALL="/usr/bin/virt-install" terom@1: terom@1: ## SELinux? terom@0: #SEMANAGE=/usr/sbin/semanage terom@0: #RESTORECON=/sbin/restorecon terom@0: terom@13: terom@13: terom@7: ### Prepare terom@9: if [ $DO_SHOWSPEC ]; then terom@9: cat < /dev/null 2> /dev/null; then terom@13: die "Virtual machine already exists: ${GUEST_NAME}" terom@13: fi terom@13: terom@7: ## Disk terom@7: # Create LV (unless it already exists) terom@7: [ -e $DISK_PATH ] || cmd_confirm sudo $LVM lvcreate -L $DISK_SIZE -n $DISK_NAME $DISK_VG terom@7: terom@7: ## SELinux? terom@7: #$SEMANAGE fcontext -a -t virt_image_t $DISK terom@7: #$RESTORECON -v $DISK terom@7: terom@7: ## virt-install terom@7: cmd_confirm $VIRT_INSTALL \ terom@7: --connect $LIBVIRT \ terom@7: --name $GUEST_NAME \ terom@7: --ram $(expand_MB $GUEST_RAM) --vcpus $GUEST_VCPUS \ terom@7: --cdrom "$INSTALL_ISO" \ terom@7: --os-variant $GUEST_OS_VARIANT \ terom@14: --disk path=$DISK_PATH,bus=$GUEST_DISK_BUS \ terom@7: --network bridge:$NET_BRIDGE \ terom@7: --virt-type $LIBVIRT_TYPE \ terom@7: --accelerate --hvm \ terom@16: --vnc \ terom@7: --serial pty terom@7: fi terom@7: