Bash case not properly evaluating value - bash

The Problem
I have a script that has a case statement which I'm expecting to execute based on the value of a variable. The case statement appears to either ignore the value or not properly evaluate it instead dropping to the default.
The Scenario
I pull a specific character out of our server hostnames which indicates where in our environment the server resides. We have six different locations:
Management(m): servers that are part of the infrastructure such as monitoring, email, ticketing, etc
Development(d): servers that are for developing code and application functionality
Test(t): servers that are used for initial testing of the code and application functionality
Implementation(i): servers that the code is pushed to for pre-production evaluation
Production(p): self-explanatory
Services(s): servers that the customer needs to integrate that provide functionality across their project. These are separate from the Management servers in that these are customer servers while Management servers are owned and operated by us.
After pulling the character from the hostname I pass it to a case block. I expect the case block to evaluate the character and add a couple lines of text to our rsyslog.conf file. What is happening instead is that the case block returns the default which does nothing but tell the person building the server to manually configure the entry due to an unrecognized character.
I've tested this manually against a server I recently built and verified that the character I am pulling from the hostname (an 's') is expected and accounted for in the case block.
The Code
# Determine which environment our server resides in
host=$(hostname -s)
env=${host:(-8):1}
OLDFILE=/etc/rsyslog.conf
NEWFILE=/etc/rsyslog.conf.new
# This is the configuration we need on every server regardless of environment
read -d '' common <<- EOF
...
TEXT WHICH IS ADDED TO ALL CONFIG FILES REGARDLESS OF FURTHER CODE EXECUTION
SNIPPED
....
EOF
# If a server is in the Management, Dev or Test environments send logs to lg01
read -d '' lg01conf <<- EOF
# Relay messages to lg01
*.notice ##xxx.xxx.xxx.100
#### END FORWARDING RULE ####
EOF
# If a server is in the Imp, Prod or is a non-affiliated Services zone server send logs to lg02
read -d '' lg02conf <<- EOF
# Relay messages to lg02
*.notice ##xxx.xxx.xxx.101
#### END FORWARDING RULE ####
EOF
# The general rsyslog configuration remains the same; pull it out and write it to a new file
head -n 63 $OLDFILE > $NEWFILE
# Add the common language to our config file
echo "$common" >> $NEWFILE
# Depending on which environment ($env) our server is in, add the appropriate
# remote log server to the configuration with the $common settings.
case $env in
m) echo "$lg01conf" >> $NEWFILE;;
d) echo "$lg01conf" >> $NEWFILE;;
t) echo "$lg01conf" >> $NEWFILE;;
i) echo "$lg02conf" >> $NEWFILE;;
p) echo "$lg02conf" >> $NEWFILE;;
s) echo "$lg02conf" >> $NEWFILE;;
*) echo "Unknown environment; Manually configure"
esac
# Keep a dated backup of the original rsyslog.conf file
cp $OLDFILE $OLDFILE.$(date +%Y%m%d)
# Replace the original rsyslog.conf file with the new version
mv $NEWFILE $OLDFILE
An Aside
I've already determined that I can combine the different groups of code from the case block onto single lines (a total of two) using the | operator. I've listed it in the manner above since this is how it is coded while I'm having issues with it.

I can't see what's wrong with your code. Maybe add another ;; to the default clause. To find the problem add a set -vx as a first line. Will show you lots of debug information.

Related

Changing MacOS Location based on SSID - check current location before changing

In this thread I received some assistance with getting this script to work correctly. The script essentially sets my network location according to the SSID I'm connected to. This is now working, however, it generates a lot of nuisance notifications.
Every time my laptop joins a wifi network, the script runs, sets the network location, and gives me a notification. Since power nap periodically joins the wifi to check for emails/updates and what have you, after a long weekend I'll get dozens of identical notifications.
How can I modify the script so that it only send a notification if the network location is changed to something different, not just when the script runs? Can I somehow check the existing network location and only change it/trigger a notification if the "new" location is different to the "existing" location?
Again, I'm extremely new to scripting on mac and GitHub in general; my previous experience is all on Windows and largely self taught.
Script:
#!/bin/bash
# automatically change configuration of Mac OS X based on location
# redirect all IO to a logfile
mkdir -p /usr/local/var/log
exec &>/usr/local/var/log/locationchanger.log
# get a little breather before we get data for things to settle down
sleep 2
# get SSID
SSID=$(/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -I | sed -n 's/^ *SSID: //p')
echo $(date) "New SSID found: $SSID"
# LOCATIONS
LOCATION=
Location_Automatic="Automatic"
Location_Office="Office"
Location_Site="Site"
# SSIDS
SSID_Office="My Office SSID"
SSID_Site="My Mobile SSID"
# SSID -> LOCATION mapping
case $SSID in
"$SSID_Office") LOCATION="$Location_Office";;
"$SSID_Site" ) LOCATION="$Location_Site";;
esac
REASON="SSID changed to $SSID"
# Location_Automatic
if [ -z "$LOCATION" ]; then
LOCATION="$Location_Automatic"
REASON="Automatic Fallback"
fi
# change network location
scselect "$LOCATION"
case $LOCATION in
"$Location_Automatic" )
osascript -e 'display notification "Network Location Changed to Automatic" with title "Network Location Changed"'
;;
"$Location_Office" )
osascript -e 'display notification "Network Location Changed to Office" with title "Network Location Changed"'
;;
"$Location_Site" )
osascript -e 'display notification "Network Location Changed to Site" with title "Network Location Changed"'
;;
esac
echo "--> Location Changer: $LOCATION - $REASON"
exit 0
This thread explains how to get the current network location.
I added the following code to get the current network location before making any changes:
CurrLoc=$(scselect | awk '{if ($1=="*") print $3}' | sed 's/[()]//g')
And then a simple if statement to exit the script early if the evaluated "new" network location matched the existing one:
if [ "$CurrLoc" = "$LOCATION" ]
then
exit 0
fi
# existing code to change network location and show notifications

Shell / Korn script - set variable depending on hostname in list

I need to write a korn script that depending on the host the script is running on, will set a deployment directory (so say 5 hosts deploy the software to directory one and five other hosts deploy to directory two).
How could I do this - I wanted to avoid an if condition for every host like below
IF [hostname = host1] then $INSTALL_DIR=Dir1
ELSE IF [hostname = host2] then $INSTALL_DIR=Dir1
and would prefer to have a list of say Directory1Hosts and Directory2Hosts which contains all the hosts valid for each directory, and then I would just check if the host the script is running on is in my Directory1Hosts or Directory2Hosts (so only two IF conditions instead of 10).
Thanks for your help - have been struggling to find how to do effectively a contains clause.
Use a case statement:
case $hostname in
host1) INSTALL_DIR=DIR1 ;;
host2) INSTALL_DIR=DIR2 ;;
esac
or use an associative array
install_dirs=([host1]=DIR1 [host2]=DIR2)
...
INSTALL_DIR=${install_dirs[$hostname]}
When you want to have configuration and code apart, you can make a config directory: one file with hosts for each install dir.
# cat installdirs/Dir1
host1
host2
With these files your code can be
INSTALL_DIR=$(grep -Flx "${hostname}" installdirs/* | cut -d"/" -f2)

What could prevent frequently switching default source ip of a machine with several interfaces

The goal was to frequently change default outgoing source ip on a machine with multiple interfaces and live ips.
I used ip route replace default as per its documentation and let a script run in loop for some interval. It changes source ip fine for a while but then all internet access to the machine is lost. It has to be remotely rebooted from a web interface to have any thing working
Is there any thing that could possibly prevent this from working stably. I have tried this on more than one servers?
Following is a minimum example
# extract all currently active source ips except loopback
IPs="$(ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 |
awk '{ print $1}')"
read -a ip_arr <<<$IPs
# extract all currently active mac / ethernet addresses
Int="$(ifconfig | grep 'eth'| grep -v 'lo' | awk '{print $1}')"
read -a eth_arr <<<$Int
ip_len=${#ip_arr[#]}
eth_len=${#eth_arr[#]}
i=0;
e=0;
while(true); do
#ip route replace 0.0.0.0 dev eth0:1 src 192.168.1.18
route_cmd="ip route replace 0.0.0.0 dev ${eth_arr[e]} src ${ip_arr[i]}"
echo $route_cmd
eval $route_cmd
sleep 300
(i++)
(e++)
if [ $i -eq $ip_len ]; then
i=0;
e=0;
echo "all ips exhausted - starting from first again"
# break;
fi
done
I wanted to comment, but as I'm not having enough points, it won't let me.
Consider:
Does varying the delay time before its run again change the number of iterations before it fails?
Exporting the ifconfig & routes every time you change it, to see if something is meaningfully different over time. Maybe some basic tests to it (ping, nslookup, etc) Basically, find what is exactly going wrong with it. Also exporting the commands you send to a logfile (text file per change?) to see changes in them to see if some is different after x iterations.
What connectivity is lost? Incoming? Outgoing? Specific applications?
You say you use/do this on other servers without problems?
Are the IP's: Static (/etc/network/interfaces), bootp/DHCP, semi-static (bootp/DHCP server serving, based on MAC address), and if served by bootp/DHCP, what is the lease duration?
On the last remark:
bootp/dhcp will give IP's for x duration. say its 60 minutes. After half that time it will "check" with the bootp/dhcp server if it can keep the IP, and extend the lease to 60 minutes again, this can mean a small reconfig on the ifconfig (maybe even at the same time of your script?).
hth

Why I cannot scale openshift cart having an environment variable defined?

I am trying to integrate Hazelcast in Tomcat cartridge (https://github.com/worldline/openshift-cartridge-tomcat). The problem is to retrieve ip:ports of gears of a scalable application. I looked at how vert.x does it and it is perfectly fine. It uses pub/sub mechanist to set ip and port in the future environment variable. I can see this in hook folder "set-vertex-cluster" file.
echo $list > $OPENSHIFT_VERTX_DIR/env/OPENSHIFT_VERTX_HAZELCAST_CLUSTER
I did id the same way replacing VERTX by TOMCAT (short name of cart).
But after creating the app there is no OPENSHIFT_TOMCAT_HAZELCAST_CLUSTER env variable.
I looked at how JbossEAP does it. It has
touch ${OPENSHIFT_JBOSSEAP_DIR}/env/OPENSHIFT_JBOSSEAP_CLUSTER
https://github.com/openshift/origin-server/blob/master/cartridges/openshift-origin-cartridge-jbosseap/bin/install
It worked for me, and finally I see OPENSHIFT_VERTX_HAZELCAST_CLUSTER env var and it is populated by gear_ip:gear_port.It's good. But when I scale the app I get the error:
Activation of new gears failed: 53d2ed31e0b8cd2bba00051f: Error activating gear: CLIENT_ERROR: Failed to execute: 'control start' for /var/lib/openshift/53d2ed31e0b8cd2bba00051f/tomcat
Unable to complete the requested operation due to: An invalid exit code (1) was returned from the server ex-std-node92.prod.rhcloud.com. This indicates an unexpected problem during the execution of your request.
Reference ID: 0b4e8a465d1901e8317a18739586e6d1
OPENSHIFT_VERTX_HAZELCAST_CLUSTER is populated by gear1_ip:gear1_port and gear2_ip:gear2_port but of course the second gear failed to start.
When I remove
touch ${OPENSHIFT_TOMCAT_DIR}/env/OPENSHIFT_TOMCAT_HAZELCAST_CLUSTER
from bin/install file everything is fine! Except that I don't have list of cluster members...
I am going mad, struggling with the problem all day long. Can anybody help me, please?
UPDATED:
• Customer A create an OpenShift Application A1 with the Git downloadable cartridge
• OpenShift install the downloadable cartridge into Node N1 and install it into the Application A1.
• Customer A now want to scale the application A1.
• OpenShift try to scale the application A1 by acquiring a new gear in Node N2 (notice that it is different from N1 above) and copy the content from A1 into N2 (but somehow will not copy all the environment variables and necessary settings). In .env folder of every gear
• The gear creation now fail on N2, because the downable cartridge content is not available on N2, because of following commands in bin/tomcat
# Filter user-owned configuration files through sed to replace all
# ${OPENSHIFT_*} variables with their actual values, and write the
# resulting filtered files to the live Tomcat configuration location.
sed_replace_env=$(print_sed_exp_replace_env_var)
replacement_conf_files=(
"server.xml"
"context.xml"
)
for conf_file in "${replacement_conf_files[#]}"; do
sed ${sed_replace_env} ${OPENSHIFT_REPO_DIR}/.openshift/config/${conf_file} > ${OPENSHIFT_TOMCAT_DIR}/conf/${conf_file}
done
The particular function
function print_sed_exp_replace_env_var {
sed_exp=""
for openshift_var in $(env | grep OPENSHIFT_ | awk -F '=' '{print $1}')
do
# environment variable values that contain " or / need to be escaped
# or they will cause problems in the sed command line.
variable_val=$(echo "${!openshift_var}" | sed -e "s#\/#\\\\/#g" | sed -e "s/\"/\\\\\"/g")
# the entire sed s/search/replace/g command needs to be quoted in case the variable value
# contains a space.
sed_exp="${sed_exp} -e \"s/\\\${env.${openshift_var}}/${variable_val}/g\""
done
printf "%s\n" "$sed_exp"
}
As you can see, there is a dangerous of the sed command that may replace entire file into blank file for server.xml and context.xml if the environment variables are not present.
The correct order that OpenShift should perform is:
Customer A create an OpenShift Application A1 with the Git downloadable cartridge
• OpenShift install the downloadable cartridge into Node N1 and install it into the Application A1.
• Customer A now want to scale the application A1.
• OpenShift try to scale the application A1 by acquiring a new gear in Node N2 (notice that it is different from N1 above). Copy all necessary environment variables into the new gears as well
• There is a script within your cartridge that require the server.xml and context.xml template from downloadable cartridge, it can now be successfully found and copy.

What gems do you recommend to use for this kind of automation?

I have to create a script to manage maintenance pages server for my hosting company.
I will need to do a CLI interface that would act like this (example scenario) :
(here, let's suppose that mcli is the name of the script, 1.1.1.1 the original server address (that host the website, www.exemple.com)
Here I just create the loopback interface on the maintenance server with the original ip address and create the nginx site-specific config file in sites-enabled
$ mcli register www.exemple.com 1.1.1.1
[DEBUG] Adding IP 1.1.1.1 to new loopback interface lo:001001001001
[WARNING] No root directory specified, setting default maintenance page.
[DEBUG] Registering www.exemple.com maintenance page and reloading Nginx: OK
Then when I want to enable the maintenance page and completely shutdown the website:
$ mcli maintenance www.exemple.com
[DEBUG] Connecting to router with SSH: OK
[DEBUG] Setting new route to 1.1.1.1 to maintenance server: OK
[DEBUG] Writing configuration: Ok
Then removing the maintenance page:
$ mcli nomaintenance www.exemple.com
[DEBUG] Connecting to router with SSH: OK
[DEBUG] Removing route to 1.1.1.1: Ok
[DEBUG] Writing configuration: Ok
And I would need a function to see the actual states of the websites
$ mcli list
+------------------+-----------------+------------------+
| Site Name | Server I.P | Maintenance mode |
+------------------+-----------------+------------------+
| www.example.com | 1.1.1.1 | Enabled |
| www.example.org | 1.1.1.2 | Disabled |
+------------------+-----------------+------------------+
$ mcli show www.example.org
Site Name: www.example.org
Server I.P: 1.1.1.1
Maintenance Mode: Enabled
Root Directory : /var/www/maintenance/default/
But I never did this kind of scripting with Ruby. What gems do you recommend for this kind of things ? For command line parsing ? Column/Colorized output ? SSH connection (needed to connect to cisco routers)
Do you recommend me to use a local database (sqlite) to store meta datas (Stages changes, actual states) or do you recommend me to compute on the fly by analyzing nginx/interfaces configuration files and using syslog for monitoring changes done with this script ?
This script will be used at first time for a massive datacenter physical migration, and next for standard usages for scheduled downtimes.
Thank you
First of all, I'd recommend you get a copy of Build awesome command-line applications in Ruby.
That said, you might want to check
GLI command line parsing like git
OptionParser command line parsing
Personally, I'd go for the SQLite approach for storing data, but I'm biased (having a strong SQL background).
Thor is a good gem for handling CLI options. It allows this type of organization in your script:
class Maintenance < Thor
desc "maintenance", "put up maintenance page"
method_option :switch, :aliases => '-s', :type => 'string'
#The method name is the name of the task that would be run => mcli maintenance
def maintenance
#do stuff
end
no_tasks do
#methods that you don't want cli tasks for go here
end
end
Maintenance.start
I don't really have any good suggestions for column/colorized output though.
I definitely recommend using some kind of a database to store states though. Maybe not sqlite, I would probably opt for maybe a redis database that stores key/value pairs with the information you are looking for.
We have similar task. I use next architecture
Small application (C) what generate config file
Add nginx init.d script new switch update_clusters. This script will restart nginx only if config file is changed
update_clusters() {
${CONF_GEN} --outfile=/tmp/nginx_clusters.conf
RETVAL=$?
if [[ "$RETVAL" != "0" ]]; then
return 5
fi
if ! diff ${CLUSTER_CONF_FILE} /tmp/nginx_clusters.conf > /dev/null; then
echo "Cluster configuration changed. Reload service"
mv -f /tmp/nginx_clusters.conf ${CLUSTER_CONF_FILE}
reload
fi
}
Set of bash scripts to add records to database.
Web console to add/modify/delete records in database (extjs+nginx module)

Resources