16dd8b3ed9
we mostly have a consistent style on if/then & for/do in devstack, except when we don't. This attempts to build a set of rules to enforce this. Because there are times when lines are legitimately long, and there is a continuation, this starts off ignoring if and for loops with continuations. But for short versions, we should enforce this. Changes to make devstack pass are included. The fact that the cleanup patch was so small is pretty solid reason that this is actually the style we've all agreed to. Part of a git stash from hong kong that I finally cleaned up. Change-Id: I6376d7afd59cc5ebba9ed69e5ee784a3d5934a10
472 lines
16 KiB
Plaintext
472 lines
16 KiB
Plaintext
## vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
## Copyright (c) 2012 Hewlett-Packard Development Company, L.P.
|
|
## 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.
|
|
|
|
|
|
# This file provides devstack with the environment and utilities to
|
|
# control nova-compute's baremetal driver.
|
|
# It sets reasonable defaults to run within a single host,
|
|
# using virtual machines in place of physical hardware.
|
|
# However, by changing just a few options, devstack+baremetal can in fact
|
|
# control physical hardware resources on the same network, if you know
|
|
# the MAC address(es) and IPMI credentials.
|
|
#
|
|
# At a minimum, to enable the baremetal driver, you must set these in localrc:
|
|
#
|
|
# VIRT_DRIVER=baremetal
|
|
# ENABLED_SERVICES="$ENABLED_SERVICES,baremetal"
|
|
#
|
|
#
|
|
# We utilize diskimage-builder to create a ramdisk, and then
|
|
# baremetal driver uses that to push a disk image onto the node(s).
|
|
#
|
|
# Below we define various defaults which control the behavior of the
|
|
# baremetal compute service, and inform it of the hardware it will control.
|
|
#
|
|
# Below that, various functions are defined, which are called by devstack
|
|
# in the following order:
|
|
#
|
|
# before nova-cpu starts:
|
|
#
|
|
# - prepare_baremetal_toolchain
|
|
# - configure_baremetal_nova_dirs
|
|
#
|
|
# after nova and glance have started:
|
|
#
|
|
# - build_and_upload_baremetal_deploy_k_and_r $token
|
|
# - create_baremetal_flavor $BM_DEPLOY_KERNEL_ID $BM_DEPLOY_RAMDISK_ID
|
|
# - upload_baremetal_image $url $token
|
|
# - add_baremetal_node <first_mac> <second_mac>
|
|
|
|
|
|
# Save trace setting
|
|
XTRACE=$(set +o | grep xtrace)
|
|
set +o xtrace
|
|
|
|
|
|
# Sub-driver settings
|
|
# -------------------
|
|
|
|
# sub-driver to use for kernel deployment
|
|
#
|
|
# - nova.virt.baremetal.pxe.PXE
|
|
# - nova.virt.baremetal.tilera.TILERA
|
|
BM_DRIVER=${BM_DRIVER:-nova.virt.baremetal.pxe.PXE}
|
|
|
|
# sub-driver to use for remote power management
|
|
#
|
|
# - nova.virt.baremetal.fake.FakePowerManager, for manual power control
|
|
# - nova.virt.baremetal.ipmi.IPMI, for remote IPMI
|
|
# - nova.virt.baremetal.tilera_pdu.Pdu, for TilePro hardware
|
|
BM_POWER_MANAGER=${BM_POWER_MANAGER:-nova.virt.baremetal.fake.FakePowerManager}
|
|
|
|
|
|
# These should be customized to your environment and hardware
|
|
# -----------------------------------------------------------
|
|
|
|
# whether to create a fake environment, eg. for devstack-gate
|
|
BM_USE_FAKE_ENV=`trueorfalse False $BM_USE_FAKE_ENV`
|
|
|
|
# Extra options to pass to bm_poseur
|
|
# change the bridge name or IP: --bridge br99 --bridge-ip 192.0.2.1
|
|
# change the virtualization type: --engine qemu
|
|
BM_POSEUR_EXTRA_OPTS=${BM_POSEUR_EXTRA_OPTS:-}
|
|
|
|
# To provide PXE, configure nova-network's dnsmasq rather than run the one
|
|
# dedicated to baremetal. When enable this, make sure these conditions are
|
|
# fulfilled:
|
|
#
|
|
# 1) nova-compute and nova-network runs on the same host
|
|
# 2) nova-network uses FlatDHCPManager
|
|
#
|
|
# NOTE: the other BM_DNSMASQ_* have no effect on the behavior if this option
|
|
# is enabled.
|
|
BM_DNSMASQ_FROM_NOVA_NETWORK=`trueorfalse False $BM_DNSMASQ_FROM_NOVA_NETWORK`
|
|
|
|
# BM_DNSMASQ_IFACE should match FLAT_NETWORK_BRIDGE
|
|
if [ "$BM_USE_FAKE_ENV" ]; then
|
|
BM_DNSMASQ_IFACE=${BM_DNSMASQ_IFACE:-br99}
|
|
BM_DNSMASQ_RANGE=${BM_DNSMASQ_RANGE:-192.0.2.32,192.0.2.48}
|
|
else
|
|
BM_DNSMASQ_IFACE=${BM_DNSMASQ_IFACE:-eth0}
|
|
# if testing on a physical network,
|
|
# BM_DNSMASQ_RANGE must be changed to suit your network
|
|
BM_DNSMASQ_RANGE=${BM_DNSMASQ_RANGE:-}
|
|
fi
|
|
|
|
# BM_DNSMASQ_DNS provide dns server to bootstrap clients
|
|
BM_DNSMASQ_DNS=${BM_DNSMASQ_DNS:-}
|
|
|
|
# BM_FIRST_MAC *must* be set to the MAC address of the node you will
|
|
# boot. This is passed to dnsmasq along with the kernel/ramdisk to
|
|
# deploy via PXE.
|
|
BM_FIRST_MAC=${BM_FIRST_MAC:-}
|
|
|
|
# BM_SECOND_MAC is only important if the host has >1 NIC.
|
|
BM_SECOND_MAC=${BM_SECOND_MAC:-}
|
|
|
|
# Hostname for the baremetal nova-compute node, if not run on this host
|
|
BM_HOSTNAME=${BM_HOSTNAME:-$(hostname -f)}
|
|
|
|
# BM_PM_* options are only necessary if BM_POWER_MANAGER=...IPMI
|
|
BM_PM_ADDR=${BM_PM_ADDR:-0.0.0.0}
|
|
BM_PM_USER=${BM_PM_USER:-user}
|
|
BM_PM_PASS=${BM_PM_PASS:-pass}
|
|
|
|
# BM_FLAVOR_* options are arbitrary and not necessarily related to
|
|
# physical hardware capacity. These can be changed if you are testing
|
|
# BaremetalHostManager with multiple nodes and different flavors.
|
|
BM_CPU_ARCH=${BM_CPU_ARCH:-x86_64}
|
|
BM_FLAVOR_CPU=${BM_FLAVOR_CPU:-1}
|
|
BM_FLAVOR_RAM=${BM_FLAVOR_RAM:-1024}
|
|
BM_FLAVOR_ROOT_DISK=${BM_FLAVOR_ROOT_DISK:-10}
|
|
BM_FLAVOR_EPHEMERAL_DISK=${BM_FLAVOR_EPHEMERAL_DISK:-0}
|
|
BM_FLAVOR_SWAP=${BM_FLAVOR_SWAP:-1}
|
|
BM_FLAVOR_NAME=${BM_FLAVOR_NAME:-bm.small}
|
|
BM_FLAVOR_ID=${BM_FLAVOR_ID:-11}
|
|
BM_FLAVOR_ARCH=${BM_FLAVOR_ARCH:-$BM_CPU_ARCH}
|
|
|
|
|
|
# Below this, we set some path and filenames.
|
|
# Defaults are probably sufficient.
|
|
BM_IMAGE_BUILD_DIR=${BM_IMAGE_BUILD_DIR:-$DEST/diskimage-builder}
|
|
BM_POSEUR_DIR=${BM_POSEUR_DIR:-$DEST/bm_poseur}
|
|
|
|
# Use DIB to create deploy ramdisk and kernel.
|
|
BM_BUILD_DEPLOY_RAMDISK=`trueorfalse True $BM_BUILD_DEPLOY_RAMDISK`
|
|
# If not use DIB, these files are used as deploy ramdisk/kernel.
|
|
# (The value must be a relative path from $TOP_DIR/files/)
|
|
BM_DEPLOY_RAMDISK=${BM_DEPLOY_RAMDISK:-}
|
|
BM_DEPLOY_KERNEL=${BM_DEPLOY_KERNEL:-}
|
|
|
|
# If you need to add any extra flavors to the deploy ramdisk image
|
|
# eg, specific network drivers, specify them here
|
|
BM_DEPLOY_FLAVOR=${BM_DEPLOY_FLAVOR:-}
|
|
|
|
# set URL and version for google shell-in-a-box
|
|
BM_SHELL_IN_A_BOX=${BM_SHELL_IN_A_BOX:-http://shellinabox.googlecode.com/files/shellinabox-2.14.tar.gz}
|
|
|
|
|
|
# Functions
|
|
# ---------
|
|
|
|
# Check if baremetal is properly enabled
|
|
# Returns false if VIRT_DRIVER is not baremetal, or if ENABLED_SERVICES
|
|
# does not contain "baremetal"
|
|
function is_baremetal() {
|
|
if [[ "$ENABLED_SERVICES" =~ 'baremetal' && "$VIRT_DRIVER" = 'baremetal' ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Install diskimage-builder and shell-in-a-box
|
|
# so that we can build the deployment kernel & ramdisk
|
|
function prepare_baremetal_toolchain() {
|
|
git_clone $BM_IMAGE_BUILD_REPO $BM_IMAGE_BUILD_DIR $BM_IMAGE_BUILD_BRANCH
|
|
git_clone $BM_POSEUR_REPO $BM_POSEUR_DIR $BM_POSEUR_BRANCH
|
|
|
|
local shellinabox_basename=$(basename $BM_SHELL_IN_A_BOX)
|
|
if [[ ! -e $DEST/$shellinabox_basename ]]; then
|
|
cd $DEST
|
|
wget $BM_SHELL_IN_A_BOX
|
|
fi
|
|
if [[ ! -d $DEST/${shellinabox_basename%%.tar.gz} ]]; then
|
|
cd $DEST
|
|
tar xzf $shellinabox_basename
|
|
fi
|
|
if [[ ! $(which shellinaboxd) ]]; then
|
|
cd $DEST/${shellinabox_basename%%.tar.gz}
|
|
./configure
|
|
make
|
|
sudo make install
|
|
fi
|
|
}
|
|
|
|
# set up virtualized environment for devstack-gate testing
|
|
function create_fake_baremetal_env() {
|
|
local bm_poseur="$BM_POSEUR_DIR/bm_poseur"
|
|
# TODO(deva): add support for >1 VM
|
|
sudo $bm_poseur $BM_POSEUR_EXTRA_OPTS create-bridge
|
|
sudo $bm_poseur $BM_POSEUR_EXTRA_OPTS create-vm
|
|
BM_FIRST_MAC=$(sudo $bm_poseur get-macs)
|
|
|
|
# NOTE: there is currently a limitation in baremetal driver
|
|
# that requires second MAC even if it is not used.
|
|
# Passing a fake value allows this to work.
|
|
# TODO(deva): remove this after driver issue is fixed.
|
|
BM_SECOND_MAC='12:34:56:78:90:12'
|
|
}
|
|
|
|
function cleanup_fake_baremetal_env() {
|
|
local bm_poseur="$BM_POSEUR_DIR/bm_poseur"
|
|
sudo $bm_poseur $BM_POSEUR_EXTRA_OPTS destroy-vm
|
|
sudo $bm_poseur $BM_POSEUR_EXTRA_OPTS destroy-bridge
|
|
}
|
|
|
|
# prepare various directories needed by baremetal hypervisor
|
|
function configure_baremetal_nova_dirs() {
|
|
# ensure /tftpboot is prepared
|
|
sudo mkdir -p /tftpboot
|
|
sudo mkdir -p /tftpboot/pxelinux.cfg
|
|
|
|
PXEBIN=/usr/share/syslinux/pxelinux.0
|
|
if [ ! -f $PXEBIN ]; then
|
|
PXEBIN=/usr/lib/syslinux/pxelinux.0
|
|
if [ ! -f $PXEBIN ]; then
|
|
die $LINENO "pxelinux.0 (from SYSLINUX) not found."
|
|
fi
|
|
fi
|
|
|
|
sudo cp $PXEBIN /tftpboot/
|
|
sudo chown -R $STACK_USER:$LIBVIRT_GROUP /tftpboot
|
|
|
|
# ensure $NOVA_STATE_PATH/baremetal is prepared
|
|
sudo mkdir -p $NOVA_STATE_PATH/baremetal
|
|
sudo mkdir -p $NOVA_STATE_PATH/baremetal/console
|
|
sudo mkdir -p $NOVA_STATE_PATH/baremetal/dnsmasq
|
|
sudo touch $NOVA_STATE_PATH/baremetal/dnsmasq/dnsmasq-dhcp.host
|
|
sudo chown -R $STACK_USER $NOVA_STATE_PATH/baremetal
|
|
|
|
# ensure dnsmasq is installed but not running
|
|
# because baremetal driver will reconfigure and restart this as needed
|
|
is_package_installed dnsmasq || install_package dnsmasq
|
|
stop_service dnsmasq
|
|
}
|
|
|
|
# build deploy kernel+ramdisk, then upload them to glance
|
|
# this function sets BM_DEPLOY_KERNEL_ID and BM_DEPLOY_RAMDISK_ID
|
|
function upload_baremetal_deploy() {
|
|
token=$1
|
|
|
|
if [ "$BM_BUILD_DEPLOY_RAMDISK" = "True" ]; then
|
|
BM_DEPLOY_KERNEL=bm-deploy.kernel
|
|
BM_DEPLOY_RAMDISK=bm-deploy.initramfs
|
|
if [ ! -e "$TOP_DIR/files/$BM_DEPLOY_KERNEL" -o ! -e "$TOP_DIR/files/$BM_DEPLOY_RAMDISK" ]; then
|
|
$BM_IMAGE_BUILD_DIR/bin/ramdisk-image-create $BM_DEPLOY_FLAVOR deploy \
|
|
-o $TOP_DIR/files/bm-deploy
|
|
fi
|
|
fi
|
|
|
|
# load them into glance
|
|
BM_DEPLOY_KERNEL_ID=$(glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name $BM_DEPLOY_KERNEL \
|
|
--is-public True --disk-format=aki \
|
|
< $TOP_DIR/files/$BM_DEPLOY_KERNEL | grep ' id ' | get_field 2)
|
|
BM_DEPLOY_RAMDISK_ID=$(glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name $BM_DEPLOY_RAMDISK \
|
|
--is-public True --disk-format=ari \
|
|
< $TOP_DIR/files/$BM_DEPLOY_RAMDISK | grep ' id ' | get_field 2)
|
|
}
|
|
|
|
# create a basic baremetal flavor, associated with deploy kernel & ramdisk
|
|
#
|
|
# Usage: create_baremetal_flavor <aki_uuid> <ari_uuid>
|
|
function create_baremetal_flavor() {
|
|
aki=$1
|
|
ari=$2
|
|
nova flavor-create $BM_FLAVOR_NAME $BM_FLAVOR_ID \
|
|
$BM_FLAVOR_RAM $BM_FLAVOR_ROOT_DISK $BM_FLAVOR_CPU
|
|
nova flavor-key $BM_FLAVOR_NAME set \
|
|
"cpu_arch"="$BM_FLAVOR_ARCH" \
|
|
"baremetal:deploy_kernel_id"="$aki" \
|
|
"baremetal:deploy_ramdisk_id"="$ari"
|
|
|
|
}
|
|
|
|
# Pull run-time kernel/ramdisk out of disk image and load into glance.
|
|
# Note that $file is currently expected to be in qcow2 format.
|
|
# Sets KERNEL_ID and RAMDISK_ID
|
|
#
|
|
# Usage: extract_and_upload_k_and_r_from_image $token $file
|
|
function extract_and_upload_k_and_r_from_image() {
|
|
token=$1
|
|
file=$2
|
|
image_name=$(basename "$file" ".qcow2")
|
|
|
|
# this call returns the file names as "$kernel,$ramdisk"
|
|
out=$($BM_IMAGE_BUILD_DIR/bin/disk-image-get-kernel \
|
|
-x -d $TOP_DIR/files -o bm-deploy -i $file)
|
|
if [ $? -ne 0 ]; then
|
|
die $LINENO "Failed to get kernel and ramdisk from $file"
|
|
fi
|
|
XTRACE=$(set +o | grep xtrace)
|
|
set +o xtrace
|
|
out=$(echo "$out" | tail -1)
|
|
$XTRACE
|
|
OUT_KERNEL=${out%%,*}
|
|
OUT_RAMDISK=${out##*,}
|
|
|
|
# load them into glance
|
|
KERNEL_ID=$(glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name $image_name-kernel \
|
|
--is-public True --disk-format=aki \
|
|
< $TOP_DIR/files/$OUT_KERNEL | grep ' id ' | get_field 2)
|
|
RAMDISK_ID=$(glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name $image_name-initrd \
|
|
--is-public True --disk-format=ari \
|
|
< $TOP_DIR/files/$OUT_RAMDISK | grep ' id ' | get_field 2)
|
|
}
|
|
|
|
|
|
# Re-implementation of devstack's "upload_image" function
|
|
#
|
|
# Takes the same parameters, but has some peculiarities which made it
|
|
# easier to create a separate method, rather than complicate the logic
|
|
# of the existing function.
|
|
function upload_baremetal_image() {
|
|
local image_url=$1
|
|
local token=$2
|
|
|
|
# Create a directory for the downloaded image tarballs.
|
|
mkdir -p $FILES/images
|
|
|
|
# Downloads the image (uec ami+aki style), then extracts it.
|
|
IMAGE_FNAME=`basename "$image_url"`
|
|
if [[ ! -f $FILES/$IMAGE_FNAME || \
|
|
"$(stat -c "%s" $FILES/$IMAGE_FNAME)" = "0" ]]; then
|
|
wget -c $image_url -O $FILES/$IMAGE_FNAME
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Not found: $image_url"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
local KERNEL=""
|
|
local RAMDISK=""
|
|
local DISK_FORMAT=""
|
|
local CONTAINER_FORMAT=""
|
|
case "$IMAGE_FNAME" in
|
|
*.tar.gz|*.tgz)
|
|
# Extract ami and aki files
|
|
[ "${IMAGE_FNAME%.tar.gz}" != "$IMAGE_FNAME" ] &&
|
|
IMAGE_NAME="${IMAGE_FNAME%.tar.gz}" ||
|
|
IMAGE_NAME="${IMAGE_FNAME%.tgz}"
|
|
xdir="$FILES/images/$IMAGE_NAME"
|
|
rm -Rf "$xdir";
|
|
mkdir "$xdir"
|
|
tar -zxf $FILES/$IMAGE_FNAME -C "$xdir"
|
|
KERNEL=$(for f in "$xdir/"*-vmlinuz* "$xdir/"aki-*/image; do
|
|
[ -f "$f" ] && echo "$f" && break; done; true)
|
|
RAMDISK=$(for f in "$xdir/"*-initrd* "$xdir/"ari-*/image; do
|
|
[ -f "$f" ] && echo "$f" && break; done; true)
|
|
IMAGE=$(for f in "$xdir/"*.img "$xdir/"ami-*/image; do
|
|
[ -f "$f" ] && echo "$f" && break; done; true)
|
|
if [[ -z "$IMAGE_NAME" ]]; then
|
|
IMAGE_NAME=$(basename "$IMAGE" ".img")
|
|
fi
|
|
DISK_FORMAT=ami
|
|
CONTAINER_FORMAT=ami
|
|
;;
|
|
*.qcow2)
|
|
IMAGE="$FILES/${IMAGE_FNAME}"
|
|
IMAGE_NAME=$(basename "$IMAGE" ".qcow2")
|
|
DISK_FORMAT=qcow2
|
|
CONTAINER_FORMAT=bare
|
|
;;
|
|
*) echo "Do not know what to do with $IMAGE_FNAME"; false;;
|
|
esac
|
|
|
|
if [ "$CONTAINER_FORMAT" = "bare" ]; then
|
|
extract_and_upload_k_and_r_from_image $token $IMAGE
|
|
elif [ "$CONTAINER_FORMAT" = "ami" ]; then
|
|
KERNEL_ID=$(glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name "$IMAGE_NAME-kernel" --is-public True \
|
|
--container-format aki \
|
|
--disk-format aki < "$KERNEL" | grep ' id ' | get_field 2)
|
|
RAMDISK_ID=$(glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name "$IMAGE_NAME-ramdisk" --is-public True \
|
|
--container-format ari \
|
|
--disk-format ari < "$RAMDISK" | grep ' id ' | get_field 2)
|
|
else
|
|
# TODO(deva): add support for other image types
|
|
return
|
|
fi
|
|
|
|
glance \
|
|
--os-auth-token $token \
|
|
--os-image-url http://$GLANCE_HOSTPORT \
|
|
image-create \
|
|
--name "${IMAGE_NAME%.img}" --is-public True \
|
|
--container-format $CONTAINER_FORMAT \
|
|
--disk-format $DISK_FORMAT \
|
|
${KERNEL_ID:+--property kernel_id=$KERNEL_ID} \
|
|
${RAMDISK_ID:+--property ramdisk_id=$RAMDISK_ID} < "${IMAGE}"
|
|
|
|
# override DEFAULT_IMAGE_NAME so that tempest can find the image
|
|
# that we just uploaded in glance
|
|
DEFAULT_IMAGE_NAME="${IMAGE_NAME%.img}"
|
|
}
|
|
|
|
function clear_baremetal_of_all_nodes() {
|
|
list=$(nova baremetal-node-list | awk -F '| ' 'NR>3 {print $2}' )
|
|
for node in $list; do
|
|
nova baremetal-node-delete $node
|
|
done
|
|
}
|
|
|
|
# Inform nova-baremetal about nodes, MACs, etc.
|
|
# Defaults to using BM_FIRST_MAC and BM_SECOND_MAC if parameters not specified
|
|
#
|
|
# Usage: add_baremetal_node <first_mac> <second_mac>
|
|
function add_baremetal_node() {
|
|
mac_1=${1:-$BM_FIRST_MAC}
|
|
mac_2=${2:-$BM_SECOND_MAC}
|
|
|
|
id=$(nova baremetal-node-create \
|
|
--pm_address="$BM_PM_ADDR" \
|
|
--pm_user="$BM_PM_USER" \
|
|
--pm_password="$BM_PM_PASS" \
|
|
"$BM_HOSTNAME" \
|
|
"$BM_FLAVOR_CPU" \
|
|
"$BM_FLAVOR_RAM" \
|
|
"$BM_FLAVOR_ROOT_DISK" \
|
|
"$mac_1" \
|
|
| grep ' id ' | get_field 2 )
|
|
[ $? -eq 0 ] || [ "$id" ] || die $LINENO "Error adding baremetal node"
|
|
if [ -n "$mac_2" ]; then
|
|
id2=$(nova baremetal-interface-add "$id" "$mac_2" )
|
|
[ $? -eq 0 ] || [ "$id2" ] || die $LINENO "Error adding interface to barmetal node $id"
|
|
fi
|
|
}
|
|
|
|
|
|
# Restore xtrace
|
|
$XTRACE
|
|
|
|
# Tell emacs to use shell-script-mode
|
|
## Local variables:
|
|
## mode: shell-script
|
|
## End:
|