diff --git a/devstack/lib/ironic b/devstack/lib/ironic index ba4458df15..614cdf868c 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -147,6 +147,7 @@ IRONIC_VM_EMULATOR=${IRONIC_VM_EMULATOR:-'/usr/bin/qemu-system-x86_64'} IRONIC_VM_ENGINE=${IRONIC_VM_ENGINE:-qemu} IRONIC_VM_NETWORK_BRIDGE=${IRONIC_VM_NETWORK_BRIDGE:-brbm} IRONIC_VM_INTERFACE_COUNT=${IRONIC_VM_INTERFACE_COUNT:-2} +IRONIC_VM_VOLUME_COUNT=${IRONIC_VM_VOLUME_COUNT:-1} IRONIC_VM_MACS_CSV_FILE=${IRONIC_VM_MACS_CSV_FILE:-$IRONIC_DATA_DIR/ironic_macs.csv} IRONIC_CLEAN_NET_NAME=${IRONIC_CLEAN_NET_NAME:-${IRONIC_PROVISION_NETWORK_NAME:-${PRIVATE_NETWORK_NAME}}} IRONIC_RESCUE_NET_NAME=${IRONIC_RESCUE_NET_NAME:-${IRONIC_CLEAN_NET_NAME}} @@ -1812,7 +1813,8 @@ function create_bridge_and_vms { $IRONIC_SCRIPTS_DIR/create-node.sh -n $vm_name \ -c $IRONIC_VM_SPECS_CPU -m $IRONIC_VM_SPECS_RAM -d $IRONIC_VM_SPECS_DISK \ -a $IRONIC_VM_SPECS_CPU_ARCH -b $IRONIC_VM_NETWORK_BRIDGE $vm_opts -p $vbmc_port -o $pdu_outlet \ - -i $IRONIC_VM_INTERFACE_COUNT -f $IRONIC_VM_SPECS_DISK_FORMAT -M $PUBLIC_BRIDGE_MTU $log_arg >> $IRONIC_VM_MACS_CSV_FILE + -i $IRONIC_VM_INTERFACE_COUNT -f $IRONIC_VM_SPECS_DISK_FORMAT -M $PUBLIC_BRIDGE_MTU $log_arg \ + -v $IRONIC_VM_VOLUME_COUNT >> $IRONIC_VM_MACS_CSV_FILE SUBSHELL if is_deployed_by_ipmi; then diff --git a/devstack/tools/ironic/scripts/configure-vm.py b/devstack/tools/ironic/scripts/configure-vm.py index b381269bac..a1b3a65df2 100755 --- a/devstack/tools/ironic/scripts/configure-vm.py +++ b/devstack/tools/ironic/scripts/configure-vm.py @@ -15,6 +15,8 @@ import argparse import os.path +import string +import sys import jinja2 import libvirt @@ -57,7 +59,7 @@ def main(): description="Configure a kvm virtual machine for the seed image.") parser.add_argument('--name', default='seed', help='the name to give the machine in libvirt.') - parser.add_argument('--image', + parser.add_argument('--image', action='append', default=[], help='Use a custom image file (must be qcow2).') parser.add_argument('--engine', default='qemu', help='The virtualization engine to use') @@ -92,9 +94,14 @@ def main(): env = jinja2.Environment(loader=jinja2.FileSystemLoader(templatedir)) template = env.get_template('vm.xml') + images = list(zip(args.image, string.ascii_lowercase)) + if not images or len(images) > 6: + # 6 is an artificial limitation because of the way we generate PCI IDs + sys.exit("Up to 6 images are required") + params = { 'name': args.name, - 'imagefile': args.image, + 'images': images, 'engine': args.engine, 'arch': args.arch, 'memory': args.memory, diff --git a/devstack/tools/ironic/scripts/create-node.sh b/devstack/tools/ironic/scripts/create-node.sh index 67d261572f..aa823aaae2 100755 --- a/devstack/tools/ironic/scripts/create-node.sh +++ b/devstack/tools/ironic/scripts/create-node.sh @@ -12,7 +12,7 @@ export PS4='+ ${BASH_SOURCE:-}:${FUNCNAME[0]:-}:L${LINENO:-}: ' # Keep track of the DevStack directory TOP_DIR=$(cd $(dirname "$0")/.. && pwd) -while getopts "n:c:i:m:M:d:a:b:e:E:p:o:f:l:L:N:A:D:" arg; do +while getopts "n:c:i:m:M:d:a:b:e:E:p:o:f:l:L:N:A:D:v:" arg; do case $arg in n) NAME=$OPTARG;; c) CPU=$OPTARG;; @@ -34,6 +34,7 @@ while getopts "n:c:i:m:M:d:a:b:e:E:p:o:f:l:L:N:A:D:" arg; do N) UEFI_NVRAM=$OPTARG;; A) MAC_ADDRESS=$OPTARG;; D) NIC_DRIVER=$OPTARG;; + v) VOLUME_COUNT=$OPTARG;; esac done @@ -76,7 +77,6 @@ if [ -n "$LOGDIR" ] ; then else VM_LOGGING="" fi -VOL_NAME="${NAME}.${DISK_FORMAT}" UEFI_OPTS="" if [ ! -z "$UEFI_LOADER" ]; then @@ -111,20 +111,31 @@ if [ -n "$MAC_ADDRESS" ] ; then MAC_ADDRESS="--mac $MAC_ADDRESS" fi +VOLUME_COUNT=${VOLUME_COUNT:-1} + if ! virsh list --all | grep -q $NAME; then - virsh vol-list --pool $LIBVIRT_STORAGE_POOL | grep -q $VOL_NAME && - virsh vol-delete $VOL_NAME --pool $LIBVIRT_STORAGE_POOL >&2 - virsh vol-create-as $LIBVIRT_STORAGE_POOL ${VOL_NAME} ${DISK}G --format $DISK_FORMAT $PREALLOC >&2 - volume_path=$(virsh vol-path --pool $LIBVIRT_STORAGE_POOL $VOL_NAME) - # Pre-touch the VM to set +C, as it can only be set on empty files. - sudo touch "$volume_path" - sudo chattr +C "$volume_path" || true vm_opts="" + for int in $(seq 1 $VOLUME_COUNT); do + if [[ "$int" == "1" ]]; then + # Compatibility with old naming + vol_name="$NAME.$DISK_FORMAT" + else + vol_name="$NAME-$int.$DISK_FORMAT" + fi + virsh vol-list --pool $LIBVIRT_STORAGE_POOL | grep -q $vol_name && + virsh vol-delete $vol_name --pool $LIBVIRT_STORAGE_POOL >&2 + virsh vol-create-as $LIBVIRT_STORAGE_POOL ${vol_name} ${DISK}G --format $DISK_FORMAT $PREALLOC >&2 + volume_path=$(virsh vol-path --pool $LIBVIRT_STORAGE_POOL $vol_name) + # Pre-touch the VM to set +C, as it can only be set on empty files. + sudo touch "$volume_path" + sudo chattr +C "$volume_path" || true + vm_opts+="--image $volume_path " + done if [[ -n "$EMULATOR" ]]; then vm_opts+="--emulator $EMULATOR " fi $PYTHON $TOP_DIR/scripts/configure-vm.py \ - --bootdev network --name $NAME --image "$volume_path" \ + --bootdev network --name $NAME \ --arch $ARCH --cpus $CPU --memory $MEM --libvirt-nic-driver $LIBVIRT_NIC_DRIVER \ --disk-format $DISK_FORMAT $VM_LOGGING --engine $ENGINE $UEFI_OPTS $vm_opts \ --interface-count $INTERFACE_COUNT $MAC_ADDRESS >&2 diff --git a/devstack/tools/ironic/templates/vm.xml b/devstack/tools/ironic/templates/vm.xml index 8a009f476f..2be30a549a 100644 --- a/devstack/tools/ironic/templates/vm.xml +++ b/devstack/tools/ironic/templates/vm.xml @@ -30,19 +30,21 @@ restart {{ emulator }} + {% for (imagefile, letter) in images %} {% if uefi_loader %} - -
+ +
{% else %} - -
+ +
{% endif %} + {% endfor %}