functions - DevStack-specific functions

The following variables are assumed to be defined by certain functions:

# DIVIDER #!/bin/bash # DIVIDER [[ -z "$_DEVSTACK_FUNCTIONS" ]] || return 0 declare -r _DEVSTACK_FUNCTIONS=1 # DIVIDER FUNC_DIR=$(cd $(dirname "${BASH_SOURCE:-$0}") && pwd) source ${FUNC_DIR}/functions-common source ${FUNC_DIR}/inc/ini-config source ${FUNC_DIR}/inc/python source ${FUNC_DIR}/inc/rootwrap # DIVIDER _XTRACE_FUNCTIONS=$(set +o | grep xtrace) set +o xtrace # DIVIDER function function_exists { declare -f -F $1 > /dev/null } # DIVIDER function upload_image { local image_url=$1 local image image_fname image_name # DIVIDER mkdir -p $FILES/images image_fname=`basename "$image_url"` if [[ $image_url != file* ]]; then # DIVIDER if [[ ! -f $FILES/$image_fname || "$(stat -c "%s" $FILES/$image_fname)" = "0" ]]; then wget --progress=dot:giga -c $image_url -O $FILES/$image_fname if [[ $? -ne 0 ]]; then echo "Not found: $image_url" return fi fi image="$FILES/${image_fname}" else # DIVIDER image=$(echo $image_url | sed "s/^file:\/\///g") if [[ ! -f $image || "$(stat -c "%s" $image)" == "0" ]]; then echo "Not found: $image_url" return fi fi # DIVIDER if [[ "$image_url" =~ 'openvz' ]]; then image_name="${image_fname%.tar.gz}" openstack --os-cloud=devstack-admin image create "$image_name" --public --container-format ami --disk-format ami < "${image}" return fi # DIVIDER if [[ "$image_url" =~ '.vmdk' ]]; then image_name="${image_fname%.vmdk}" # DIVIDER local vmdk_disktype="" local vmdk_net_adapter="e1000" local path_len # DIVIDER local vmdk_adapter_type="$(head -25 $image | { grep -a -F -m 1 'ddb.adapterType =' $image || true; })" vmdk_adapter_type="${vmdk_adapter_type#*\"}" vmdk_adapter_type="${vmdk_adapter_type%?}" # DIVIDER local vmdk_create_type="$(head -25 $image | { grep -a -F -m 1 'createType=' $image || true; })" vmdk_create_type="${vmdk_create_type#*\"}" vmdk_create_type="${vmdk_create_type%\"*}" descriptor_data_pair_msg="Monolithic flat and VMFS disks "` `"should use a descriptor-data pair." if [[ "$vmdk_create_type" = "monolithicSparse" ]]; then vmdk_disktype="sparse" elif [[ "$vmdk_create_type" = "monolithicFlat" || "$vmdk_create_type" = "vmfs" ]]; then # DIVIDER local flat_fname="$(head -25 $image | { grep -G 'RW\|RDONLY [0-9]+ FLAT\|VMFS' $image || true; })" flat_fname="${flat_fname#*\"}" flat_fname="${flat_fname%?}" if [[ -z "$flat_fname" ]]; then flat_fname="$image_name-flat.vmdk" fi path_len=`expr ${#image_url} - ${#image_fname}` local flat_url="${image_url:0:$path_len}$flat_fname" warn $LINENO "$descriptor_data_pair_msg"` `" Attempt to retrieve the *-flat.vmdk: $flat_url" if [[ $flat_url != file* ]]; then if [[ ! -f $FILES/$flat_fname || \ "$(stat -c "%s" $FILES/$flat_fname)" = "0" ]]; then wget --progress=dot:giga -c $flat_url -O $FILES/$flat_fname fi image="$FILES/${flat_fname}" else image=$(echo $flat_url | sed "s/^file:\/\///g") if [[ ! -f $image || "$(stat -c "%s" $image)" == "0" ]]; then echo "Flat disk not found: $flat_url" return 1 fi fi image_name="${flat_fname}" vmdk_disktype="preallocated" elif [[ "$vmdk_create_type" = "streamOptimized" ]]; then vmdk_disktype="streamOptimized" elif [[ -z "$vmdk_create_type" ]]; then # DIVIDER if [[ ${image_name: -5} != "-flat" ]]; then warn $LINENO "Expected filename suffix: '-flat'."` `" Filename provided: ${image_name}" else descriptor_fname="${image_name:0:${#image_name} - 5}.vmdk" path_len=`expr ${#image_url} - ${#image_fname}` local flat_path="${image_url:0:$path_len}" local descriptor_url=$flat_path$descriptor_fname warn $LINENO "$descriptor_data_pair_msg"` `" Attempt to retrieve the descriptor *.vmdk: $descriptor_url" if [[ $flat_path != file* ]]; then if [[ ! -f $FILES/$descriptor_fname || \ "$(stat -c "%s" $FILES/$descriptor_fname)" = "0" ]]; then wget -c $descriptor_url -O $FILES/$descriptor_fname fi descriptor_url="$FILES/$descriptor_fname" else descriptor_url=$(echo $descriptor_url | sed "s/^file:\/\///g") if [[ ! -f $descriptor_url || \ "$(stat -c "%s" $descriptor_url)" == "0" ]]; then echo "Descriptor not found: $descriptor_url" return 1 fi fi vmdk_adapter_type="$(head -25 $descriptor_url | { grep -a -F -m 1 'ddb.adapterType =' $descriptor_url || true; })" vmdk_adapter_type="${vmdk_adapter_type#*\"}" vmdk_adapter_type="${vmdk_adapter_type%?}" fi vmdk_disktype="preallocated" else vmdk_disktype="preallocated" fi # DIVIDER property_string=`echo "$image_name" | { grep -oP '(?<=-)(?!.*-).*[:;].*[:;].*$' || true; }` IFS=':;' read -a props <<< "$property_string" vmdk_disktype="${props[0]:-$vmdk_disktype}" vmdk_adapter_type="${props[1]:-$vmdk_adapter_type}" vmdk_net_adapter="${props[2]:-$vmdk_net_adapter}" openstack --os-cloud=devstack-admin image create "$image_name" --public --container-format bare --disk-format vmdk --property vmware_disktype="$vmdk_disktype" --property vmware_adaptertype="$vmdk_adapter_type" --property hw_vif_model="$vmdk_net_adapter" < "${image}" return fi # DIVIDER if [[ "$image_url" =~ '.vhd.tgz' ]]; then image_name="${image_fname%.vhd.tgz}" local force_vm_mode="" if [[ "$image_name" =~ 'cirros' ]]; then # DIVIDER force_vm_mode="--property vm_mode=xen" fi openstack \ --os-cloud=devstack-admin \ image create \ "$image_name" --public \ --container-format=ovf --disk-format=vhd \ $force_vm_mode < "${image}" return fi # DIVIDER if [[ "$image_url" =~ '.xen-raw.tgz' ]]; then image_name="${image_fname%.xen-raw.tgz}" openstack \ --os-cloud=devstack-admin \ image create \ "$image_name" --public \ --container-format=tgz --disk-format=raw \ --property vm_mode=xen < "${image}" return fi if [[ "$image_url" =~ '.hds' ]]; then image_name="${image_fname%.hds}" vm_mode=${image_name##*-} if [[ $vm_mode != 'exe' && $vm_mode != 'hvm' ]]; then die $LINENO "Unknown vm_mode=${vm_mode} for Virtuozzo image" fi openstack \ --os-cloud=devstack-admin \ image create \ "$image_name" --public \ --container-format=bare --disk-format=ploop \ --property vm_mode=$vm_mode < "${image}" return fi local kernel="" local ramdisk="" local disk_format="" local container_format="" local unpack="" local img_property="" case "$image_fname" in *.tar.gz|*.tgz) # DIVIDER [ "${image_fname%.tar.gz}" != "$image_fname" ] && image_name="${image_fname%.tar.gz}" || image_name="${image_fname%.tgz}" local xdir="$FILES/images/$image_name" rm -Rf "$xdir"; mkdir "$xdir" tar -zxf $image -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 ;; *.img) image_name=$(basename "$image" ".img") local format format=$(qemu-img info ${image} | awk '/^file format/ { print $3; exit }') if [[ ",qcow2,raw,vdi,vmdk,vpc," =~ ",$format," ]]; then disk_format=$format else disk_format=raw fi container_format=bare ;; *.img.gz) image_name=$(basename "$image" ".img.gz") disk_format=raw container_format=bare unpack=zcat ;; *.qcow2) image_name=$(basename "$image" ".qcow2") disk_format=qcow2 container_format=bare ;; *.iso) image_name=$(basename "$image" ".iso") disk_format=iso container_format=bare ;; *.vhd|*.vhdx|*.vhd.gz|*.vhdx.gz) local extension="${image_fname#*.}" image_name=$(basename "$image" ".$extension") disk_format=vhd container_format=bare if [ "${image_fname##*.}" == "gz" ]; then unpack=zcat fi ;; *) echo "Do not know what to do with $image_fname"; false;; esac if is_arch "ppc64"; then img_property="--property hw_cdrom_bus=scsi" fi if is_arch "aarch64"; then img_property="--property hw_machine_type=virt --property hw_cdrom_bus=virtio --property os_command_line='console=ttyAMA0'" fi if [ "$container_format" = "bare" ]; then if [ "$unpack" = "zcat" ]; then openstack --os-cloud=devstack-admin image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < <(zcat --force "${image}") else openstack --os-cloud=devstack-admin image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < "${image}" fi else # DIVIDER local kernel_id="" ramdisk_id=""; if [ -n "$kernel" ]; then kernel_id=$(openstack --os-cloud=devstack-admin image create "$image_name-kernel" $img_property --public --container-format aki --disk-format aki < "$kernel" | grep ' id ' | get_field 2) fi if [ -n "$ramdisk" ]; then ramdisk_id=$(openstack --os-cloud=devstack-admin image create "$image_name-ramdisk" $img_property --public --container-format ari --disk-format ari < "$ramdisk" | grep ' id ' | get_field 2) fi openstack --os-cloud=devstack-admin image create "${image_name%.img}" $img_property --public --container-format ami --disk-format ami ${kernel_id:+--property kernel_id=$kernel_id} ${ramdisk_id:+--property ramdisk_id=$ramdisk_id} < "${image}" fi } # DIVIDER function use_database { if [[ -z "$DATABASE_BACKENDS" ]]; then # DIVIDER DATABASE_TYPE=$1 deprecated "The database backend needs to be properly set in ENABLED_SERVICES; use_database is deprecated localrc" else # DIVIDER use_exclusive_service DATABASE_BACKENDS DATABASE_TYPE $1 fi } #Macro for curl statements. curl requires -g option for literal IPv6 addresses. CURL_GET="${CURL_GET:-curl -g}" # DIVIDER function wait_for_service { local timeout=$1 local url=$2 time_start "wait_for_service" timeout $timeout sh -c "while ! $CURL_GET -k --noproxy '*' -s $url >/dev/null; do sleep 1; done" time_stop "wait_for_service" } # DIVIDER function ping_check { local ip=$1 local timeout=${2:-30} local from_net=${3:-""} local expected=${4:-True} local op="!" local failmsg="[Fail] Couldn't ping server" local ping_cmd="ping" # DIVIDER if [[ -n "$from_net" ]]; then if is_service_enabled neutron; then ping_cmd="$TOP_DIR/tools/ $from_net" elif [[ "$MULTI_HOST" = "True" && "$from_net" = "$PRIVATE_NETWORK_NAME" ]]; then # DIVIDER return fi fi # DIVIDER if [[ "$expected" != "True" ]]; then op="" failmsg="[Fail] Could ping server" fi # DIVIDER local check_command="while $op $ping_cmd -c1 -w1 $ip; do sleep 1; done" echo "Checking connectivity with $check_command" if ! timeout $timeout sh -c "$check_command"; then die $LINENO $failmsg fi } # DIVIDER function get_instance_ip { local vm_id=$1 local network_name=$2 local nova_result="$(nova show $vm_id)" local ip ip=$(echo "$nova_result" | grep "$network_name" | get_field 2) if [[ $ip = "" ]];then echo "$nova_result" die $LINENO "[Fail] Couldn't get ipaddress of VM" fi echo $ip } # DIVIDER # DIVIDER function ssh_check { if is_service_enabled neutron; then _ssh_check_neutron "$1" $2 $3 $4 $5 return fi _ssh_check_novanet "$1" $2 $3 $4 $5 } function _ssh_check_novanet { local NET_NAME=$1 local KEY_FILE=$2 local FLOATING_IP=$3 local DEFAULT_INSTANCE_USER=$4 local ACTIVE_TIMEOUT=$5 local probe_cmd="" if ! timeout $ACTIVE_TIMEOUT sh -c "while ! ssh -o StrictHostKeyChecking=no -i $KEY_FILE ${DEFAULT_INSTANCE_USER}@$FLOATING_IP echo success; do sleep 1; done"; then die $LINENO "server didn't become ssh-able!" fi } # DIVIDER function get_rootwrap_location { local module=$1 echo "$(get_python_exec_prefix)/$module-rootwrap" } # DIVIDER function check_path_perm_sanity { # DIVIDER local real_path real_path=$(readlink -f $1) local rebuilt_path="" for i in $(echo ${real_path} | tr "/" " "); do rebuilt_path=$rebuilt_path"/"$i if [[ $(stat -c '%a' ${rebuilt_path}) = 700 ]]; then echo "*** DEST path element" echo "*** ${rebuilt_path}" echo "*** appears to have 0700 permissions." echo "*** This is very likely to cause fatal issues for DevStack daemons." if [[ -n "$SKIP_PATH_SANITY" ]]; then return else echo "*** Set SKIP_PATH_SANITY to skip this check" die $LINENO "Invalid path permissions" fi fi done } # DIVIDER function _vercmp_r { typeset sep typeset -a ver1=() ver2=() sep=$1; shift ver1=("${@:1:sep}") ver2=("${@:sep+1}") if ((ver1 > ver2)); then echo 1; return 0 elif ((ver2 > ver1)); then echo -1; return 0 fi if ((sep <= 1)); then echo 0; return 0 fi _vercmp_r $((sep-1)) "${ver1[@]:1}" "${ver2[@]:1}" } # DIVIDER function vercmp_numbers { typeset v1=$1 v2=$2 sep typeset -a ver1 ver2 deprecated "vercmp_numbers is deprecated for more generic vercmp" IFS=. read -ra ver1 <<< "$v1" IFS=. read -ra ver2 <<< "$v2" _vercmp_r "${#ver1[@]}" "${ver1[@]}" "${ver2[@]}" } # DIVIDER function vercmp { local v1=$1 local op=$2 local v2=$3 local result # DIVIDER result=$(echo -e "$v1\n$v2" | sort -V | head -1) case $op in "==") [ "$v1" = "$v2" ] return ;; ">") [ "$v1" != "$v2" ] && [ "$result" = "$v2" ] return ;; "<") [ "$v1" != "$v2" ] && [ "$result" = "$v1" ] return ;; ">=") [ "$result" = "$v2" ] return ;; "<=") [ "$result" = "$v1" ] return ;; *) die $LINENO "unrecognised op: $op" ;; esac } # DIVIDER function setup_colorized_logging { local conf_file=$1 local conf_section=$2 local project_var=${3:-"project_name"} local user_var=${4:-"user_name"} # DIVIDER iniset $conf_file $conf_section logging_context_format_string "%(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [%(request_id)s %("$user_var")s %("$project_var")s%(color)s] %(instance)s%(color)s%(message)s" iniset $conf_file $conf_section logging_default_format_string "%(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [-%(color)s] %(instance)s%(color)s%(message)s" iniset $conf_file $conf_section logging_debug_format_suffix "from (pid=%(process)d) %(funcName)s %(pathname)s:%(lineno)d" iniset $conf_file $conf_section logging_exception_prefix "%(color)s%(asctime)s.%(msecs)03d TRACE %(name)s %(instance)s" } # DIVIDER if ! function_exists echo_summary; then function echo_summary { echo $@ } fi if ! function_exists echo_nolog; then function echo_nolog { echo $@ } fi # DIVIDER function create_disk { local node_number local disk_image=${1} local storage_data_dir=${2} local loopback_disk_size=${3} # DIVIDER if [[ -e ${disk_image} ]]; then if egrep -q ${storage_data_dir} /proc/mounts; then sudo umount ${storage_data_dir}/drives/sdb1 sudo rm -f ${disk_image} fi fi sudo mkdir -p ${storage_data_dir}/drives/images sudo truncate -s ${loopback_disk_size} ${disk_image} # DIVIDER sudo mkfs.xfs -f -i size=1024 ${disk_image} # DIVIDER if ! egrep -q ${storage_data_dir} /proc/mounts; then sudo mount -t xfs -o loop,noatime,nodiratime,nobarrier,logbufs=8 \ ${disk_image} ${storage_data_dir} fi } # DIVIDER $_XTRACE_FUNCTIONS # DIVIDER # DIVIDER

ensure we don't re-source this in the same environment

Include the common functions

Save trace setting

Check if a function already exists

Retrieve an image from a URL and upload into Glance. Uses the following variables:

  • FILES must be set to the cache dir

upload_image image-url

Create a directory for the downloaded image tarballs.

Downloads the image (uec ami+akistyle), then extracts it.

File based URL (RFC 1738): file://host/path Remote files are not considered here. unix: file:///home/user/path/file windows: file:///C:/Documents%20and%20Settings/user/path/file

OpenVZ-format images are provided as .tar.gz, but not decompressed prior to loading

vmdk format images

Before we can upload vmdk type images to glance, we need to know it's disk type, storage adapter, and networking adapter. These values are passed to glance as custom properties. We take these values from the vmdk file if populated. Otherwise, we use vmdk filename, which is expected in the following format:

<name>-<disk type>;<storage adapter>;<network adapter>

If the filename does not follow the above format then the vsphere driver will supply default values.

vmdk adapter type

vmdk disk type

Attempt to retrieve the *-flat.vmdk

-flat.vmdk provided: attempt to retrieve the descriptor (.vmdk) to retrieve appropriate metadata

NOTE: For backwards compatibility reasons, colons may be used in place of semi-colons for property delimiters but they are not permitted characters in NTFS filesystems.

XenServer-vhd-ovf-format images are provided as .vhd.tgz and should not be decompressed prior to loading

Cirros VHD image currently only boots in PV mode. Nova defaults to PV for all VHD images, but the glance setting is needed for booting directly from volume.

.xen-raw.tgz suggests a Xen capable raw image inside a tgz. and should not be decompressed prior to loading. Setting metadata, so PV mode is used.

Extract ami and aki files

Use glance client to add the kernel the root filesystem. We parse the results of the first upload to get the glance ID of the kernel for use when uploading the root filesystem.

Set the database backend to use When called from stackrc/localrc DATABASE_BACKENDS has not been initialized yet, just save the configuration selection and call back later to validate it.

$1 - the name of the database backend to use (mysql, postgresql, ...)

No backends registered means this is likely called from localrc This is now deprecated usage

This should no longer get for posterity

Wait for an HTTP server to start answering requests wait_for_service timeout url

ping check Uses globals ENABLED_SERVICES, TOP_DIR, MULTI_HOST, PRIVATE_NETWORK ping_check <ip> [boot-timeout] [from_net] [expected]

if we don't specify a from_net we're expecting things to work fine from our local box.

there is no way to address the multihost / private case, bail here for compatibility. TODO: remove this cruft and redo code to handle this at the caller level.

inverse the logic if we're testing no connectivity

Because we've transformed this command so many times, print it out at the end.

Get ip of instance

ssh check

ssh_check net-name key-file floating-ip default-user active-timeout

Get the location of the $module-rootwrap executables, where module is cinder or nova. get_rootwrap_location module

Path permissions sanity check check_path_perm_sanity path

Ensure no element of the path has 0700 permissions, which is very likely to cause issues for daemons. Inspired by default 0700 homedir permissions on RHEL and common practice of making DEST in the stack user's homedir.

This function recursively compares versions, and is not meant to be called by anything other than vercmp_numbers below. This function does not work with alphabetic versions.

_vercmp_r sep ver1 ver2

This function compares two versions and is meant to be called by external callers. Please note the function assumes non-alphabetic versions. For example, this will work:

vercmp_numbers 1.10 1.4

The above will return "1", as 1.10 is greater than 1.4.

vercmp_numbers 5.2 6.4

The above will return "-1", as 5.2 is less than 6.4.

vercmp_numbers 4.0 4.0

The above will return "0", as the versions are equal.

vercmp_numbers ver1 ver2

vercmp ver1 op ver2
Compare VER1 to VER2
  • op is one of < <= == >= >
  • returns true if satisified

System Message: WARNING/2 (<stdin>, line 237)

Definition list ends without a blank line; unexpected unindent.

e.g. if vercmp 1.0 "<" 2.0; then

System Message: ERROR/3 (<stdin>, line 239)

Unexpected indentation.

System Message: WARNING/2 (<stdin>, line 240)

Block quote ends without a blank line; unexpected unindent.


sort the two numbers with sort's "-V" argument. Based on if v2 swapped places with v1, we can determine ordering.

This function sets log formatting options for colorizing log output to stdout. It is meant to be called by lib modules. The last two parameters are optional and can be used to specify non-default value for project and user format variables. Defaults are respectively 'project_name' and 'user_name'

setup_colorized_logging something.conf SOMESECTION

Add color to logging output

These functions are provided for basic fall-back functionality for projects that include parts of DevStack (Grenade). will override these with more specific versions for DevStack (with fancy spinners, etc). We never override an existing version

create_disk - Create backing disk

Create a loopback disk and format it to XFS.

Make a fresh XFS filesystem. Use bigger inodes so xattr can fit in a single inode. Keeping the default inode size (256) will result in multiple inodes being used to store xattr. Retrieving the xattr will be slower since we have to read multiple inodes. This statement is true for both Swift and Ceph.

Mount the disk with mount options to make it as efficient as possible

Restore xtrace

Local variables: mode: shell-script End: