So I wrote a script to setup some networks. The script is not that important for this article; in fact I do not like said script anymore and will replace it with some wholesome ansible playbooks. What matters in it is this bit of code:
So the script is called nething.sh and kinda looks like this:
#! /bin/bash # Populate openstack networks HEAD_NODE_IPADDRESS="10.0.0.30" COMPUTE_NOTE=$HEAD_NODE_IP_ADDRESS SEC_GROUP="custom-1" NET_TYPE="flat" PHYSICAL_NETWORK_DEFAULT="extnet" NET_NAME="public" SUBNET_NAME="subnet-${NET_NAME}" CIDR="10.0.0.0/24" DNS_NAMESERVER1="10.0.0.10" GATEWAY_IP="10.0.0.2" DHCP_ALLOCATION_START="10.0.0.249" DHCP_ALLOCATION_END="10.0.0.254" # Boring things here function CreateNetwork { net_name=$1 net_id=$2 # vlan tag, segmentation ID net_type=$3 subnet_name=$4 subnet_range=$5 # Subnet is not necessarily as wide as its parent network gateway=$6 subnet_dhcp_start=$7 subnet_dhcp_end=$8 nameserver=$9 physical_net=$10 if [[ ${net_id} == "EXT" ]]; then openstack network create --provider-network-type ${net_type} \ [...] } # Public Network CreateNetwork \ $NET_NAME \ "EXT" \ $NET_TYPE \ $SUBNET_NAME \ $CIDR \ $GATEWAY_IP \ $DHCP_ALLOCATION_START \ $DHCP_ALLOCATION_END \ $DNS_NAMESERVER1 \ $PHYSICAL_NETWORK_DEFAULT
which defines a few constants on the top and then pass them to a function in the end of the script.
Every time I ran the script, it kept crashing and burning, and I did not know why. So I ran the script as bash -x nething.sh so it would spit out each line/variable as it passed through them. Here is how the output looks like:
+ HEAD_NODE_IPADDRESS=10.0.0.30 + COMPUTE_NOTE= + SEC_GROUP=custom-1 + NET_TYPE=flat + PHYSICAL_NETWORK_DEFAULT=extnet + NET_NAME=public + SUBNET_NAME=public_subnet + CIDR=10.0.0.0/24 + DNS_NAMESERVER1=10.0.0.10 + GATEWAY_IP=10.0.0.2 + DHCP_ALLOCATION_START=10.0.0.249 + DHCP_ALLOCATION_END=10.0.0.254 + CreateNetwork public BLANK flat public_subnet 10.0.0.0/24 ' ' + net_name=public + net_id=BLANK + net_type=flat + subnet_name=public_subnet + subnet_range=10.0.0.0/24 + gateway=' ' + subnet_dhcp_start= + subnet_dhcp_end= + nameserver= + physical_net=public0 + [[ BLANK == \E\X\T ]] + [[ BLANK == \B\L\A\N\K ]]
The line starting with CreateNetwork public BLANK means we are going to the function CreateNetwork(); what is come after is a list of the local variables inside the function which were populated by the function call. The [[ BLANK == \E\X\T ]] is how an if test looks like under the -x option, specifically
if [[ ${net_id} == "EXT" ]]; then
To save time I will spoil the fun: take a look at the gateway variable. Why is it blank? And why every other variable after it is blank? Note that the line
+ CreateNetwork public BLANK flat public_subnet 10.0.0.0/24 ' 'tells us that
- gateway is being passed as ' ' right from the function call.
- No other argument after gateway is being passed.
Why?
The problem is CIDR="10.0.0.0/24", specifically the double quotes. They are expanding the string 10.0.0.0/24 and interpreting the /24 as a character. The solution is to use single quotes, as in
HEAD_NODE_IPADDRESS="10.0.0.30" COMPUTE_NOTE=$HEAD_NODE_IP_ADDRESS SEC_GROUP="custom-1" NET_TYPE="flat" PHYSICAL_NETWORK_DEFAULT="extnet" NET_NAME="public" SUBNET_NAME="${NET_NAME}_subnet" CIDR='10.0.0.0/24' DNS_NAMESERVER1="10.0.0.10" GATEWAY_IP="10.0.0.2" DHCP_ALLOCATION_START="10.0.0.249" DHCP_ALLOCATION_END="10.0.0.254"
If we then run that, the strings now behave as expected:
[root@stakola ~(keystone_admin)]# bash -x nething.sh + HEAD_NODE_IPADDRESS=10.0.0.30 + COMPUTE_NOTE= + SEC_GROUP=custom-1 + NET_TYPE=flat + PHYSICAL_NETWORK_DEFAULT=extnet + NET_NAME=public + SUBNET_NAME=public_subnet + CIDR=10.0.0.0/24 + DNS_NAMESERVER1=10.0.0.10 + GATEWAY_IP=10.0.0.2 + DHCP_ALLOCATION_START=10.0.0.249 + DHCP_ALLOCATION_END=10.0.0.254 + CreateNetwork private 6855 vlan private_subnet 192.168.55.0/24 192.168.55.1 192. 168.55.100 192.168.55.200 10.0.0.10 physnet1 + net_name=private + net_id=6855 + net_type=vlan + subnet_name=private_subnet + subnet_range=192.168.55.0/24 + gateway=192.168.55.1 + subnet_dhcp_start=192.168.55.100 + subnet_dhcp_end=192.168.55.200 + nameserver=10.0.0.10 + physical_net=private0 + [[ 6855 == \E\X\T ]] + [[ 6855 == \B\L\A\N\K ]] [...]
Moral of the story: know when to use single and double quotes!