8

I have a set of servers mentioned in a text file called network_list.txt . How can i run through the servers and run command and display the result ? I have tried the follows :

filename="network_list.txt"
service=httpd
while read -r line
do
    name="$line"
    ping -c 3 $name > /dev/null 2>&1;
    RETVAL=$?
        if [ $RETVAL -ne 0 ]
        then 
           echo "$name is down"
        else 
            echo "$name is up and running"
        fi
    http_status=$(ssh $name ps -ef | grep -v grep | grep $service | wc -l)
        if [ $http_status -ne 0 ]
        then
            echo "$service is up and running in $name"
        else
            echo "$service is down in $name"
        fi
done < "$filename"

But it stops after first server .

Thanks :) .

Jaroslav Kucera
  • 1,545
  • 11
  • 18
KTM
  • 213
  • 2
  • 6
  • What is the format of your input file? – Jaroslav Kucera Dec 05 '17 at 10:54
  • its like 192.168.1.101 192.168.1.102 192.168.1.103 - - - - - - line by line – KTM Dec 05 '17 at 10:55
  • 6
    This seems to me like the perfect use case for Ansible or some other Configuration Management tool. – GregL Dec 05 '17 at 11:37
  • OP - maybe try to use hostnames in your file and add in a "$serverIP" that nslookup's and awks the line? hostnames are more memorable for us humans and it allows for more documentation when you 'echo "= $server $serverIP =" before every command output when you log with ./scriptname.sh > mm.dd.yy.scriptname.log ... Just some food for thought! :) – kilrainebc Dec 05 '17 at 16:25
  • 1
    I second Greg's advice to use Ansible. I used to have scripts like this with a `for` loop. Now I put those host names into an Ansible configuration file and tell Ansible to run a script on each of them. Ansible gives you the ability to write and test the script locally, then to run it remotely on each server. Half the stuff that my scripts used to do is handled by Ansible modules, so I have a lot less to maintain. A+, will never go back. – Stephen Ostermiller Dec 05 '17 at 19:49
  • 2
    salt would work well for this too. It would basically be: `salt \* cmd.run "systemctl is-active httpd"` – Michael Hampton Dec 05 '17 at 23:22

4 Answers4

8

There is solution with -n -x parameters:

http_status=$(ssh -nx name ps -ef | grep -v grep | grep $service | wc -l)

The -x disables X11 forwarding to get rid of possible X11 forwarding request failed messages.

The -n redirects stdin from /dev/null (actually, prevents reading from stdin).

Jaroslav Kucera
  • 1,545
  • 11
  • 18
7

You need to redirect the stdin of SSH command in your loop to nowhere to prevent reading all your lines. This should do it:

http_status=$(ssh $name "ps -ef | grep -v grep | grep $service | wc -l" < /dev/null)
Khaled
  • 36,533
  • 8
  • 72
  • 99
  • ping_stat=$(ping -c 4 -q $name 2>&1) , Hi why couldn't i use $name after this command ? – KTM Dec 05 '17 at 11:44
1

While the answer that is accepted totally works, I'd like to just offer this script that might be useful should someone come across it.

Usage

So you would obviously need to change the lines under the "Echo $server $serverIP" lines to reflect the actual commands you'd like to use. $1 is the first thing you type after "./scriptname.sh" - e.g. ./scriptname.sh $1 $2 $3... - and here is how this breaks down:

$1 should be the file with your hostnames. $2 is the positional parameter you pass down to FunctionC (which always gets called in this example script). $2 and $3 can be used to call the other Functions (A/B) - alternatively you could replace $3 and $4 at the EOF with "FunctionA" and "FunctionB" if you always want to run them.

Hope this helps someone, it's been INVALUABLE to me whilst deploying packages or testing things across the domain.

#! /bin/bash
SVRList=`ls ./$1`

#========[ FUNCTIONS ]===============

FunctionA () {
  for server in `cat $SVRList | grep -v ^#`  # Makes variable "server" from each line in $SVRlist that is not commented
do
  serverIP=$(nslookup $server | tail -2 | awk -F ":" '{print $2}')
  echo "============= $server | $serverIP ============="
  #command that you'd like to run locally
done
}

FunctionB () {
      for server in `cat $SVRList | grep -v ^#`
    do
      passedvar="192.16.${1}"
      serverIP=$(nslookup $server | tail -2 | awk -F ":" '{print $2}')
      echo "============= $server | $serverIP ============="
      ssh -q $serverIP '#command that you'd like to run remotely'
      ssh -q $serverIP '#second command, if you so fancy'
      ssh -q $serverIP "ping $passedvar"
      #^ is done just to show an example of what can be done
    done
    }

FunctionC () {
  for server in `cat $SVRList | grep -v ^#`
do
  passedvar="192.16.${1}"
  serverIP=$(nslookup $server | tail -2 | awk -F ":" '{print $2}')
  echo "============= $server | $serverIP ============="
  ssh -q $serverIP "ping $passedvar"
  #^ is done just to show the power of positional parameters.
done
}

#========[ SCRIPT ]==========

if [ $# -lt 2 ] 
  then
  printf "Usage: %s <file containing list of server hostnames> <passed variable> <functions> \n" "$(basename "$0")" >&2
  exit 64
  else 
  echo "Starting Script"
fi

FunctionC $2
$3
$4
exit

Also OP:

Why set service=httpd? It may be nicer to use [ testing and "service=$1" where $1 could be httpd, splunkd, or any other service. Additionally, the variable http_status should be changed to something like "service_status" to make the script better documented.

kilrainebc
  • 93
  • 7
1

Why not use pssh?

https://github.com/lilydjwg/pssh

It's a parallel SSH tool which has built in support for obtaining a list of hosts from a file:

pssh -h network_list.txt ps -ef | grep -v grep | grep $service | wc -l

Then you can parse the output as you like

Daniel Scott
  • 430
  • 3
  • 11