#!/bin/bash # # Copyright (c) 2011 Citrix Systems, Inc. # Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # set -eux . /etc/xensource-inventory NAME="XenServer OpenStack VPX" DATA_VDI_SIZE="500MiB" BRIDGE_M= BRIDGE_P= KERNEL_PARAMS= VPX_FILE=os-vpx.xva AS_TEMPLATE= FROM_TEMPLATE= RAM= WAIT_FOR_NETWORK= BALLOONING= usage() { cat << EOF Usage: $0 [-f FILE_PATH] [-d DISK_SIZE] [-v BRIDGE_NAME] [-m BRIDGE_NAME] [-p BRIDGE_NAME] [-k PARAMS] [-r RAM] [-i|-c] [-w] [-b] Installs XenServer OpenStack VPX. OPTIONS: -h Shows this message. -i Install OpenStack VPX as template. -c Clone from existing template. -w Wait for the network settings to show up before exiting. -b Enable memory ballooning. When set min_RAM=RAM/2 max_RAM=RAM. -f path Specifies the path to the XVA. Default to ./os-vpx.xva. -d disk-size Specifies the size in MiB for the data disk. Defaults to 500 MiB. -m bridge Specifies the bridge for the isolated management network. Defaults to xenbr0. -v bridge Specifies the bridge for the vm network -p bridge Specifies the bridge for the externally facing network. -k params Specifies kernel parameters. -r MiB Specifies RAM used by the VPX, in MiB. By default it will take the value from the XVA. EXAMPLES: Create a VPX that connects to the isolated management network using the default bridge with a data disk of 1GiB: install-os-vpx.sh -f /root/os-vpx-devel.xva -d 1024 Create a VPX that connects to the isolated management network using xenbr1 as bridge: install-os-vpx.sh -m xenbr1 Create a VPX that connects to both the management and public networks using xenbr1 and xapi4 as bridges: install-os-vpx.sh -m xenbr1 -p xapi4 Create a VPX that connects to both the management and public networks using the default for management traffic: install-os-vpx.sh -m xapi4 Create a VPX that automatically becomes the master: install-os-vpx.sh -k geppetto_master=true EOF } get_params() { while getopts "hicwbf:d:v:m:p:k:r:" OPTION; do case $OPTION in h) usage exit 1 ;; i) AS_TEMPLATE=1 ;; c) FROM_TEMPLATE=1 ;; w) WAIT_FOR_NETWORK=1 ;; b) BALLOONING=1 ;; f) VPX_FILE=$OPTARG ;; d) DATA_VDI_SIZE="${OPTARG}MiB" ;; m) BRIDGE_M=$OPTARG ;; p) BRIDGE_P=$OPTARG ;; k) KERNEL_PARAMS=$OPTARG ;; r) RAM=$OPTARG ;; v) BRIDGE_V=$OPTARG ;; ?) usage exit ;; esac done if [[ -z $BRIDGE_M ]] then BRIDGE_M=xenbr0 fi } xe_min() { local cmd="$1" shift xe "$cmd" --minimal "$@" } get_dest_sr() { IFS=, sr_uuids=$(xe sr-list --minimal other-config:i18n-key=local-storage) dest_sr="" for sr_uuid in $sr_uuids do pbd=$(xe pbd-list --minimal sr-uuid=$sr_uuid host-uuid=$INSTALLATION_UUID) if [ "$pbd" ] then echo "$sr_uuid" unset IFS return fi done unset IFS dest_sr=$(xe_min sr-list uuid=$(xe_min pool-list params=default-SR)) if [ "$dest_sr" = "" ] then echo "No local storage and no default storage; cannot import VPX." >&2 exit 1 else echo "$dest_sr" fi } find_network() { result=$(xe_min network-list bridge="$1") if [ "$result" = "" ] then result=$(xe_min network-list name-label="$1") fi echo "$result" } find_template() { xe_min template-list other-config:os-vpx=true } renumber_system_disk() { local v="$1" local vdi_uuid=$(xe_min vbd-list vm-uuid="$v" type=Disk userdevice=xvda \ params=vdi-uuid) if [ "$vdi_uuid" ] then local vbd_uuid=$(xe_min vbd-list vm-uuid="$v" vdi-uuid="$vdi_uuid") xe vbd-destroy uuid="$vbd_uuid" local new_vbd_uuid=$(xe vbd-create vm-uuid="$v" vdi-uuid="$vdi_uuid" \ device=0 bootable=true type=Disk) xe vbd-param-set other-config:owner uuid="$new_vbd_uuid" fi } create_vif() { xe vif-create vm-uuid="$1" network-uuid="$2" device="$3" } create_gi_vif() { local v="$1" # Note that we've made the outbound device eth1, so that it comes up after # the guest installer VIF, which means that the outbound one wins in terms # of gateway. local gi_network_uuid=$(xe_min network-list \ other-config:is_guest_installer_network=true) create_vif "$v" "$gi_network_uuid" "0" >/dev/null } create_vm_vif() { local v="$1" echo "Installing management interface on $BRIDGE_V." local out_network_uuid=$(find_network "$BRIDGE_V") create_vif "$v" "$out_network_uuid" "1" >/dev/null } create_management_vif() { local v="$1" echo "Installing management interface on $BRIDGE_M." local out_network_uuid=$(find_network "$BRIDGE_M") create_vif "$v" "$out_network_uuid" "2" >/dev/null } # This installs the interface for public traffic, only if a bridge is specified # The interface is not configured at this stage, but it will be, once the admin # tasks are complete for the services of this VPX create_public_vif() { local v="$1" if [[ -z $BRIDGE_P ]] then echo "Skipping installation of interface for public traffic." else echo "Installing public interface on $BRIDGE_P." pub_network_uuid=$(find_network "$BRIDGE_P") create_vif "$v" "$pub_network_uuid" "3" >/dev/null fi } label_system_disk() { local v="$1" local vdi_uuid=$(xe_min vbd-list vm-uuid="$v" type=Disk userdevice=0 \ params=vdi-uuid) xe vdi-param-set \ name-label="$NAME system disk" \ other-config:os-vpx=true \ uuid=$vdi_uuid } create_data_disk() { local v="$1" local sys_vdi_uuid=$(xe_min vbd-list vm-uuid="$v" type=Disk params=vdi-uuid) local data_vdi_uuid=$(xe_min vdi-list other-config:os-vpx-data=true) if echo "$data_vdi_uuid" | grep -q , then echo "Multiple data disks found -- assuming that you want a new one." data_vdi_uuid="" else data_in_use=$(xe_min vbd-list vdi-uuid="$data_vdi_uuid") if [ "$data_in_use" != "" ] then echo "Data disk already in use -- will create another one." data_vdi_uuid="" fi fi if [ "$data_vdi_uuid" = "" ] then echo -n "Creating new data disk ($DATA_VDI_SIZE)... " sr_uuid=$(xe_min vdi-list params=sr-uuid uuid="$sys_vdi_uuid") data_vdi_uuid=$(xe vdi-create name-label="$NAME data disk" \ sr-uuid="$sr_uuid" \ type=user \ virtual-size="$DATA_VDI_SIZE") xe vdi-param-set \ other-config:os-vpx-data=true \ uuid="$data_vdi_uuid" dom0_uuid=$(xe_min vm-list is-control-domain=true) vbd_uuid=$(xe vbd-create device=autodetect type=Disk \ vdi-uuid="$data_vdi_uuid" vm-uuid="$dom0_uuid") xe vbd-plug uuid=$vbd_uuid dev=$(xe_min vbd-list params=device uuid=$vbd_uuid) mke2fs -q -j -m0 /dev/$dev e2label /dev/$dev vpxstate xe vbd-unplug uuid=$vbd_uuid xe vbd-destroy uuid=$vbd_uuid else echo -n "Attaching old data disk... " fi vbd_uuid=$(xe vbd-create device=2 type=Disk \ vdi-uuid="$data_vdi_uuid" vm-uuid="$v") xe vbd-param-set other-config:os-vpx-data=true uuid=$vbd_uuid echo "done." } set_kernel_params() { local v="$1" local args=$KERNEL_PARAMS local cmdline=$(cat /proc/cmdline) for word in $cmdline do if echo "$word" | grep -q "geppetto" then args="$word $args" fi done if [ "$args" != "" ] then echo "Passing Geppetto args to VPX: $args." xe vm-param-set PV-args="$args" uuid="$v" fi } set_memory() { local v="$1" if [ "$RAM" != "" ] then echo "Setting RAM to $RAM MiB." [ "$BALLOONING" == 1 ] && RAM_MIN=$(($RAM / 2)) || RAM_MIN=$RAM xe vm-memory-limits-set static-min=16MiB static-max=${RAM}MiB \ dynamic-min=${RAM_MIN}MiB dynamic-max=${RAM}MiB \ uuid="$v" fi } # Make the VM auto-start on server boot. set_auto_start() { local v="$1" xe vm-param-set uuid="$v" other-config:auto_poweron=true } set_all() { local v="$1" set_kernel_params "$v" set_memory "$v" set_auto_start "$v" label_system_disk "$v" create_gi_vif "$v" create_vm_vif "$v" create_management_vif "$v" create_public_vif "$v" } log_vifs() { local v="$1" (IFS=, for vif in $(xe_min vif-list vm-uuid="$v") do dev=$(xe_min vif-list uuid="$vif" params=device) mac=$(xe_min vif-list uuid="$vif" params=MAC | sed -e 's/:/-/g') echo "eth$dev has MAC $mac." done unset IFS) | sort } destroy_vifs() { local v="$1" IFS=, for vif in $(xe_min vif-list vm-uuid="$v") do xe vif-destroy uuid="$vif" done unset IFS } get_params "$@" thisdir=$(dirname "$0") if [ "$FROM_TEMPLATE" ] then template_uuid=$(find_template) name=$(xe_min template-list params=name-label uuid="$template_uuid") echo -n "Cloning $name... " vm_uuid=$(xe vm-clone vm="$template_uuid" new-name-label="$name") xe vm-param-set is-a-template=false uuid="$vm_uuid" echo $vm_uuid. destroy_vifs "$vm_uuid" set_all "$vm_uuid" else if [ ! -f "$VPX_FILE" ] then # Search $thisdir/$VPX_FILE too. In particular, this is used when # installing the VPX from the supp-pack, because we want to be able to # invoke this script from the RPM and the firstboot script. if [ -f "$thisdir/$VPX_FILE" ] then VPX_FILE="$thisdir/$VPX_FILE" else echo "$VPX_FILE does not exist." >&2 exit 1 fi fi echo "Found OS-VPX File: $VPX_FILE. " dest_sr=$(get_dest_sr) echo -n "Installing $NAME... " vm_uuid=$(xe vm-import filename=$VPX_FILE sr-uuid="$dest_sr") echo $vm_uuid. renumber_system_disk "$vm_uuid" nl=$(xe_min vm-list params=name-label uuid=$vm_uuid) xe vm-param-set \ "name-label=${nl/ import/}" \ other-config:os-vpx=true \ uuid=$vm_uuid set_all "$vm_uuid" create_data_disk "$vm_uuid" if [ "$AS_TEMPLATE" ] then xe vm-param-set uuid="$vm_uuid" is-a-template=true \ other-config:instant=true echo -n "Installing VPX from template... " vm_uuid=$(xe vm-clone vm="$vm_uuid" new-name-label="${nl/ import/}") xe vm-param-set is-a-template=false uuid="$vm_uuid" echo "$vm_uuid." fi fi log_vifs "$vm_uuid" echo -n "Starting VM... " xe vm-start uuid=$vm_uuid echo "done." show_ip() { ip_addr=$(echo "$1" | sed -n "s,^.*"$2"/ip: \([^;]*\).*$,\1,p") echo -n "IP address for $3: " if [ "$ip_addr" = "" ] then echo "did not appear." else echo "$ip_addr." fi } if [ "$WAIT_FOR_NETWORK" ] then echo "Waiting for network configuration... " i=0 while [ $i -lt 600 ] do ip=$(xe_min vm-list params=networks uuid=$vm_uuid) if [ "$ip" != "" ] then show_ip "$ip" "1" "$BRIDGE_M" if [[ $BRIDGE_P ]] then show_ip "$ip" "2" "$BRIDGE_P" fi echo "Installation complete." exit 0 fi sleep 10 let i=i+1 done fi