CouchDB Replication verify - couchdb

I am searching easy way to verify replicated documents, that all of them were transferred.
Also, when some documents have error during replication process, how to check, which one and/or why ?
Now, I have an idea, that I could check current checkpointed sequence on the source. Then list all ids from source/_changes and then HEADing (or GETing) documents from target database and verify their presence (or values).

Simple nagios script to check two servers dbs and doc counts match - could be improved to check that _rev for each between dbs match.
#!/usr/local/bin/bash
# nagios check script to make sure 2 couch servers are the same
#
# ie: have same dbs
# have same number of documents in each db
#
# ./check_couch_dbs 192.168.1.2 192.168.1.3
#
# Needs fdescfs for bash redirects
#
# Needs npm - as root: 'pkg install npm'
# Needs json - as root: 'npm install -g json'
# https://github.com/zpoley/json-command
#
#
# check_couch_dbs command definition example
#
# define command{
# command_name check_couch_dbs
# command_line $USER1$/check_couch_dbs $ARG1$ $ARG2$
# }
#
host1=$1
host2=$2
difference_threashold=10
dbs1=`curl -s http://$host1:5984/_all_dbs | json -ga | grep -v -E '_replicator|_users' | sort`
dbs2=`curl -s http://$host2:5984/_all_dbs | json -ga | grep -v -E '_replicator|_users' | sort`
dif=`diff -y --suppress-common-lines -b -s <(echo "$dbs1") <(echo "$dbs2")`
err=""
msg=""
exitcode=0
if [[ "$dif" == *identical* ]]; then
msg+="Couchdbs lists match"
fi
if [[ "$dif" == *\<* ]]; then
err+="ERROR - db missing from $host2 \n"
exitcode=2
fi
if [[ "$dif" == *\>* ]]; then
err+="ERROR - db missing from $host1 \n"
exitcode=2
fi
if [[ $exitcode -gt 0 ]]; then
echo -e -n $err
echo "$dif"
err=""
# exit $exitcode
fi
dbs=`echo -e "$dbs1\n$dbs2" | sort | uniq`
#echo "$dbs"
for db in $dbs; do
count1=`curl -s http://$host1:5984/$db | json doc_count`
if [ -z "$count1" ]; then continue; fi #no db
count2=`curl -s http://$host2:5984/$db | json doc_count`
if [ -z "$count2" ]; then continue; fi #no db
if [ "$count1" -ne "$count2" ]; then
if [ "$count1" -gt "$count2" ]; then
difference=$(($count1 - $count2))
else
difference=$(($count2 - $count1))
fi
if [[ $difference -gt $difference_threashold ]]; then
err+="ERROR - $db count difference $host1: $count1 != $host2: $count2 - difference: $difference\n"
exitcode=2
else
err+="WARNING - $db count difference $host1: $count1 != $host2: $count2 - difference: $difference\n"
if [[ $exitcode -lt 1 ]]; then
exitcode=1
fi
fi
fi
done
if [[ $exitcode -gt 0 ]]; then
echo -e -n $err $msg
exit $exitcode
else
echo -e "OK - $msg - doc_count match"
exit $exitcode
fi

Having slept on it i think the below would cover the comparing of all docs and _revs
#!/usr/local/bin/bash
db1=`curl -s http://192.168.1.2:5984/mydb/_all_docs`
db2=`curl -s http://192.168.1.3:5984/mydb/_all_docs`
dif=`diff -y --suppress-common-lines -b -s <(echo "$db1") <(echo "$db2")`
echo $dif

Related

Check if user exists and belong to group

I want to check three conditions before I run the command: If string (username) match to regexp, if user exists and if user belongs to certain group:
PASSWORD=`/usr/bin/pwgen -sync -1 15`
CHPASSWD=/usr/sbin/chpasswd
FILE=/tmp/password
USER_GROUP=somegroup
while true
do
echo -ne "username: "; read USER_NAME
if [[ "$USER_NAME" =~ ^[a-z_][a-z0-9_]*[$]?$ ]] &&
[[ `getent group $USER_GROUP | grep -o $USER_NAME` -eq 0 ]] &&
[[ `getent passwd $USER_NAME | cut -d ':' -f -1` -eq 0 ]]
then
break
else
echo "error"
fi
done
echo $USER_NAME:$PASSWORD > $FILE
sudo $CHPASSWD < $FILE
rm -f $FILE
The above code works partially only. It checks for regexp and group but it is trying to change password for even if user doesn't exist.
The right solution is:
while true
do
echo -ne "username: "; read USER_NAME
if [[ "$USER_NAME" =~ ^[a-z_][a-z0-9_]*[$]?$ ]]
then
if [[ `getent group $USER_GROUP | grep -w -c "$USER_NAME"` -eq 1 ]]
then
break
else
echo "User not in the right group"
fi
else
echo "Invalid characters"
fi
done

Can't parse a string with brace expansion operations into a command

have some problem with shell script.
In our office we set up only few commands, that available for devs when they are trying ssh to server. It is configured with help of .ssh/authorized_keys file and available command for user there is bash script:
#!/bin/sh
if [[ $1 == "--help" ]]; then
cat <<"EOF"
This script has the purpose to let people remote execute certain commands without logging into the system.
For this they NEED to have a homedir on this system and uploaded their RSA public key to .ssh/authorized_keys (via ssh-copy-id)
Then you can alter that file and add some commands in front of their key eg :
command="/usr/bin/dev.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
The user will do the following : ssh testuser#server tail testserver.example.com/2017/01/01/user.log
EOF
exit 0;
fi
# set global variable
set $SSH_ORIGINAL_COMMAND
# set the syslog path where the files can be found
PATH="/opt/syslog/logs"
# strip ; or any other unwanted signs out of the command, this prevents them from breaking out of the setup command
if [[ $1 != "" ]]; then
COMMAND=$1
COMMAND=${COMMAND//[;\`]/}
fi
if [[ $2 != "" ]]; then
ARGU1=$2
ARGU1=${ARGU1//[;\`]/}
fi
if [[ $3 != "" ]]; then
ARGU2=$3
ARGU2=${ARGU2//[;\`]/}
fi
if [[ $4 != "" ]]; then
ARGU3=$4
ARGU3=${ARGU3//[;\`]/}
fi
# checking for the commands
case "$COMMAND" in
less)
ARGU2=${ARGU1//\.\./}
FILE=$PATH/$ARGU1
if [ ! -f $FILE ]; then
echo "File doesn't exist"
exit 1;
fi
#echo " --------------------------------- LESS $FILE"
/usr/bin/less $FILE
;;
grep)
if [[ $ARGU2 == "" ]]; then
echo "Pls give a filename"
exit 1
fi
if [[ $ARGU1 == "" ]]; then
echo "Pls give a string to search for"
exit 1
fi
ARGU2=${ARGU2//\.\./}
FILE=$PATH/$ARGU2
/usr/bin/logger -t restricted-command -- "------- $USER Executing grep $ARGU1 \"$ARGU2\" $FILE"
if [ ! -f $FILE ]; then
echo "File doesn't exist"
/usr/bin/logger -t restricted-command -- "$USER Executing $#"
exit 1;
fi
/bin/grep $ARGU1 $FILE
;;
tail)
if [[ $ARGU1 == "" ]]; then
echo "Pls give a filename"
exit 1
fi
ARGU1=${ARGU1//\.\./}
FILE=$PATH/$ARGU1
if [ ! -f $FILE ]; then
echo "File doesn't exist"
/usr/bin/logger -t restricted-command -- "$USER Executing $# ($FILE)"
exit 1;
fi
/usr/bin/tail -f $FILE
;;
cat)
ARGU2=${ARGU1//\.\./}
FILE=$PATH/$ARGU1
if [ ! -f $FILE ]; then
echo "File doesn't exist"
exit 1;
fi
/bin/cat $FILE
;;
help)
/bin/cat <<"EOF"
# less LOGNAME (eg less testserver.example.com/YYYY/MM/DD/logfile.log)
# grep [ARGUMENT] LOGNAME
# tail LOGNAME (eg tail testserver.example.com/YYYY/MM/DD/logfile.log)
# cat LOGNAME (eg cat testserver.example.com/YYYY/MM/DD/logfile.log)
In total the command looks like this : ssh user#testserver.example.com COMMAND [ARGUMENT] LOGFILE
EOF
/usr/bin/logger -t restricted-command -- "$USER HELP requested $#"
exit 1
;;
*)
/usr/bin/logger -s -t restricted-command -- "$USER Invalid command $#"
exit 1
;;
esac
/usr/bin/logger -t restricted-command -- "$USER Executing $#"
The problem is next:
when i try to exec some command, it takes only first argument, if i do recursion in files by using {n,n1,n2} - it doesn't work:
[testuser#local ~]$ ssh testuser#syslog.server less srv1838.example.com/2017/02/10/local1.log |grep 'srv2010' | wc -l
0
[testuser#local ~]$ ssh testuser#syslog.server less srv2010.example.com/2017/02/10/local1.log |grep 'srv2010' | wc -l
11591
[testuser#local ~]$ ssh testuser#syslog.server less srv{1838,2010}.example.com/2017/02/10/local1.log |grep 'srv2010' | wc -l
0
[testuser#local ~]$ ssh testuser#syslog.server less srv{2010,1838}.example.com/2017/02/21/local1.log |grep 'srv2010' | wc -l
11591
Could someone help me, how can i parse\count command arguments to make it work?
Thank you and have a nice day!
The number of arguments for a bash script would be $#. As a quick example:
#!/bin/bash
narg=$#
typeset -i i
i=1
while [ $i -le $narg ] ; do
echo " $# $i: $1"
shift
i=$i+1
done
gives, for bash tst.sh a b {c,d}
4 1: a
3 2: b
2 3: c
1 4: d
In your script, the command to execute (cat, less, ...) gets explicitly only the second argument to the script. If you want to read all arguments, you should do something like this (note: only a hint, removed all sorts of checks etc..)
command="$1"
shift
case $command in
(grep) pattern="$1"
shift
while [ $# -gt 0 ] ; do
grep "$pattern" "$1"
shift
done
;;
esac
note: added some quotes as comment suggested, but, being only a hint, you should carefully look at quoting and your checks in your own script.
Less command working now:
case "$COMMAND" in
less)
if [[ $ARGU1 == "" ]]; then
echo "Pls give a filename"
exit 1
fi
FILES_LIST=${#:2}
FILE=(${FILES_LIST//\.\./})
for v in "${FILE[#]}";do
v=${v//[;\']/}
if [ ! -f $v ]; then
echo "File doesn't exist"
fi
/usr/bin/less $PATH/$v
done;;
tail command works too with 2 and more files, but i can't execute tail -f command on two files unfortunately.

Displayining multiple bash output horizontally

I have 4 important services running on my machine which I want to see them all the time. I have this simple bash script running as bash profile.
echo
PROC="nginx mysql php-fpm pptpd"
for p in $PROC
do
ps cax | grep $p > /dev/null
if [ $? -eq 0 ]; then
echo -e "\e[92m$p running\e[0m"
else
echo -e "\e[101m$p IS NOT RUNNING \e[0m"
fi
done
echo
The out put of this script is:
nginx running
mysql running
php-fpm running
pptpd running
How can I make it like this?
nginx running - mysql running - php-fpm running - pptpd running
Build the status lines first into an array, and then print the array:
status=()
for p in $PROC
do
if ps cax | grep -q $p; then
status+=( " \e[92m$p running\e[0m " )
else
status+=( " \e[101m$p IS NOT RUNNING \e[0m " )
fi
done
(IFS=-; echo -e "${status[*]}")
${status[*]} expands to every element in the array joined by the first character of IFS, which I set to - earlier. Note that I used a subshell (IFS=-; echo ...), so that changing IFS doesn't affect the rest of the script.
Other notes:
ps cax | grep $p > /dev/null
if [ $? -eq 0 ]; then
Can be combined to:
if ps cax | grep -q $p; then
Which is much more concise and readable. You could also consider using pgrep instead.
Use printf or add the -n flag to echo.
POSIX Compliant Refactor without ProcTools
#!/bin/sh
showstatus() {
echo
while [ "$1" ]; do
if ps cax | grep -qF "$1"; then
msg='\e[92m%s running\e[0m'
else
msg='\e[101m%s IS NOT RUNNING \e[0m'
fi
printf "$msg" "$1"
shift
[ "$1" ] && printf ' - '
done
echo
}
showstatus nginx mysql php-fpm pptpd
POSIX Compliant Refactor with ProcTools
#!/bin/sh
showstatus() {
echo
while [ "$1" ]; do
if pkill -0 "$1"; then
msg='\e[92m%s running\e[0m'
else
msg='\e[101m%s IS NOT RUNNING \e[0m'
fi
printf "$msg" "$1"
shift
[ "$1" ] && printf ' - '
done
echo
}
showstatus nginx mysql php-fpm pptpd

Error in shell script for detecting packages on system

I wrote a shell script for detecting whether a package is installed or not. My script should write its name and status if it's installed. I can't figure out any problem with my code but when I run it, it doesn't execute the commands under if [ $? == 0 ] condition.
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
fi
But the most weird thing to me is that it works if I add an echo after if statement. Looks like that:
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
echo hi
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
fi
So if I add an echo -n to right position in my code it will work as I want. But I just want to know what is wrong with first one?
I think in general you could be more deliberate about your return code handling. You are making assumptions about what $? is referring to that may not be valid depending on your program flow, and regardless, make the program harder to read and understand.
#!/bin/bash
dpkg -s $# &> /dev/null
installed=$?
if [ $installed -eq 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
else
echo -e "Package \033[0;31mNOT\033[0m found." >&2
fi
$? is the return status of the last executed command. 0 is successful, 1 or anything else is an error. Note:
dpkg -s python &> /dev/null # returns 0 (OK, true)
# $? equals 0 now
[ #? == 1 ] # false # returns 1 (error)
# $? equals 1 now
[ #? == 0 ] # false # returns 1 (error)
When you put echo, it works:
dpkg -s python &> /dev/null # returns 0 (OK, true)
# $? equals 0 now
[ #? == 1 ] # false # returns 1 (error)
# $? equals 1 now
echo hi # returns 0 (OK)
# $? equals 0 now
[ #? == 0 ] # true # returns 0 (OK)
You could save $? to a variable, but you don't really need the if inside the else since you already checked if #? == 1 so just put your code inside else:
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
If you are worried of other possible return statuses of $? (greater than one). You could rewrite your script to
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
else
echo -e "Package \033[0;31mNOT\033[0m found." >&2
fi

Ash MATCH operator (=~)

I'm trying to fit a script for linux onto my WD world edition drive.
The script is written for Bash (debian) but my WD only runs busybox (with ash). Despite this, I have gotten most functionality in there just from using Google. There is only one operator i have not found a counterpart to, the =~ operator
How can i port the functionality of the =~ operator from the old script to ash?
Script:
#! /bin/bash
# posttorrent.sh by Killemov
{
# Log file, file where we tell what events have been processed.
LOG_FILE=/var/log/posttorrent.log
# Username for transmission remote.
TR_USERNAME="username"
# Password for transmission remote.
TR_PASSWORD="password"
# Get current time.
NOW=$(date +%Y-%m-%d\ %H:%M:%S)
# Source directory, should not be changed.
SRC_DIR="${TR_TORRENT_DIR}/${TR_TORRENT_NAME}"
# Directory to store the un-compressed files in..
DEST_DIR="${TR_TORRENT_DIR}/${TR_TORRENT_NAME}/"
# This parameter string could be passed from Transmission in the future.
TR_TORRENT_PARAMETER="EXTRACT SLEEP1h"
echo "text"
if [ -e "$SRC_DIR/keep" ]; then
TR_TORRENT_PARAMETER="$TR_TORRENT_PARAMETER KEEP"
fi
if [ -e "$SRC_DIR/exit" ]; then
TR_TORRENT_PARAMETER="EXIT"
fi
# Actual processing starts here.
if [[ "$TR_TORRENT_PARAMETER" =~ "EXIT" ]]; then
echo $NOW "Exiting $TR_TORRENT_NAME" >> $LOG_FILE
exit 0
fi
echo "text2"
if [[ "$TR_TORRENT_PARAMETER" =~ "EXTRACT" ]]; then
cd $TR_TORRENT_DIR
if [ -d "$SRC_DIR" ]; then
IFS=$'\n'
unset RAR_FILES i
for RAR_FILE in $( find "$SRC_DIR" -iname "*.rar" ); do
if [[ $RAR_FILE =~ .*part.*.rar ]]; then
if [[ $RAR_FILE =~ .*part0*1.rar ]]; then
RAR_FILES[i++]=$RAR_FILE
fi
else
RAR_FILES[i++]=$RAR_FILE
fi
done
unset IFS
if [ ${#RAR_FILES} -gt 0 ]; then
for RAR_FILE in "$(eval \$$RAR_FILES[#])"; do
unrar x -inul "$RAR_FILE" "$DEST_DIR"
if [ $? -gt 0 ]; then
echo $NOW "Error unrarring $TR_TORRENT_NAME" >> $LOG_FILE
transmission-remote -n $TR_USERNAME:$TR_PASSWORD -t$TR_TORRENT_ID --verify --start
exit 0
fi
done
if [[ ! "$TR_TORRENT_PARAMETER" =~ "KEEP" ]]; then
SLEEP=$(expr match "$TR_TORRENT_PARAMETER" '.*SLEEP\([0-9a-zA-Z]*\)')
if [ ${#SLEEP} -gt 0 ]; then
sleep $SLEEP
fi
transmission-remote -n $TR_USERNAME:$TR_PASSWORD -t$TR_TORRENT_ID --remove-and-delete
fi
echo $NOW "Unrarred $TR_TORRENT_NAME" >> $LOG_FILE
fi
fi
fi
} &
(i had some trouble with indirect references, i hoped i fixed that correctly)
Well for the $VARIABLE =~ PATERN you should be able to use the:
echo "$VARIABLE" | grep -E PATTERN
But I think you will have a little bit of trouble with the arithmetical expressions i++ as well - if it's implemented, then you still need to use the i=$(($i + 1)) syntax, if it's not implemented, then the i=$(expr $i + 1) syntax.
I presume you're reason for the IFS=$'\n' is to split the find on newlines, but you're probably better off with issuing the find into a temporary file, and then doing a while read line; do ... done <$tmpfile,
Additionally, I'm not certain if all versions of busybox ash support arrays, so you may have a problem there as well.

Resources