#!/bin/bash

# This script generates a set of random data that follows a similar pattern to the data used in our
# live application.
#
# The result should be a directory containing a number of directories, files and a large number of
# symlinks.
#
# A subsequent script will emulate the updates that generally hit the directory.
#
# This script has been written so that a problem we are experiencing with tmpfs can be sucessfully
# reproduced and debugged.
#
# Pass in the path to the tmpfs mount as the script parameter. The mount should be writable. Create
# it as root using a command like this:
# $ mount -t tmpfs -o size=2G,nr_inodes=5M none /tmp/tmpfs
#

##
## General constants
##

# For development only. Switch on to greatly reduce the number of files and directories generated.
# This makes the script run a lot quicker when developing
FAST_MODE=0

##
## Input parameter checks
##

if [ ! "$1" ]; then
	echo "Please supply the tmpfs mount path as the first parameter"
	exit 1
fi;

MOUNT=$1

# Check that the path given exists
if [ -d "$MOUNT" ]; then
	# Check that it is a tmpfs mount
	M=$(mount | grep 'type tmpfs' | grep " $MOUNT " )

	if [ ! "$M" ]; then
		echo "Path given is not a tmpfs mount"
		exit 1
	fi
else
	echo "Path given does not exist or is not a directory"
	exit 1
fi

# Check that the directory is writable
if [ ! -w "$MOUNT" ]; then
	echo "Path given is not writable. Please fix permissions"
	exit 1
fi

# OK, that's all we're going to check.

##
## Function declarations
##

# Get a random number between $1 and $2
function random
{
	FLOOR=$1
	CEIL=$2

	RANGE=$[$CEIL - $FLOOR + 1]

	NUM=$[$RANDOM % $RANGE]

	echo $[$NUM + $FLOOR]
}


# Repeat string $1 $2 times
function str_repeat
{
	s=$(printf "%$2s" );
	echo "${s// /$1}"
}

# Make a file with random content with name $1 and size between $2 and $3 in bytes
function mkfile
{
	head -c $(random $2 $3) /dev/urandom > $1
}

# Make in directory $1 between $2 and $3 files sized between $4 and $5 named sequentially with
# prefix $6 and postfix $7
function mkfiles
{
	if [ $FAST_MODE = 1 ]; then
		num=4
	else
		if [ $2 = $3 ]; then
			num=$2
		else
			num=`random $2 $3`
		fi
	fi

	# Create files
	for ((f=0; f<$num; f+=1)); do
		mkfile $1/$6$f$7 $4 $5
	done

}

# Create a nested directory structure $1 deep with $2 directories in each with the leaves containing
# $3 files of size between $4 and $5
function create_nested_dirs
{
	local DEPTH=$1
	local DIRCOUNT=$2
	local FILECOUNT=$3
	local MINSIZE=$4
	local MAXSIZE=$5

	if [ $FAST_MODE = 1 ]; then
		DIRCOUNT=3
		FILECOUNT=4
	fi

	for ((d=0; d<$DEPTH; d+=1)); do
		mkdir $d
		cd $d
		for ((d2=0; d2<$DIRCOUNT; d2+=1)); do
			mkdir $d2

			# Create files
			mkfiles $d2 $FILECOUNT $FILECOUNT $MINSIZE $MAXSIZE
		done

		cd ..
	done
}

# Create a nested directory structure $1 deep with $2 directories in each with the leaves containing
# $3 files of size between $4 and $5 with a limiting element. $6 is the current depth: always pass
# 1.
function create_nested_dirs2
{
	local DEPTH=$1
	local DIRCOUNT=$2
	local FILECOUNT=$3
	local MINSIZE=$4
	local MAXSIZE=$5
	local CURDEPTH=$6

	if [ $FAST_MODE = 1 ]; then
		DIRCOUNT=3
		FILECOUNT=4
	fi

	tput el1
	tput rc
	echo -n `realpath .` "                         "

	local d2=0

	while [ $d2 -lt $DIRCOUNT ]; do

		dirname=$(printf "%02d" $d2)
		mkdir $dirname

		# Slight limiting element, as we don't need 1,000,000 files!
		# This filter creates files equating to <200MB of files which is roughly
		# similar to live
		modulo=$(($d2 % $(($CURDEPTH * 2))))

		if [ $modulo -gt 0 ]; then
			d2=$(($d2 + 1))
			continue
		fi

		cd $dirname

		# Create files
		mkfiles '.' $FILECOUNT $FILECOUNT $MINSIZE $MAXSIZE 'f'

		# recurse in and redo if we're not exceeding
		# $DEPTH
		if [ $DEPTH -gt $CURDEPTH ]
		then
			NEWDEPTH=$(($CURDEPTH + 1))
			create_nested_dirs2 $DEPTH $DIRCOUNT $FILECOUNT $MINSIZE $MAXSIZE $NEWDEPTH
		fi

		cd ..
		d2=$(($d2 + 1))
	done
}

# Create between $2 and $3 symlinks in $1 to files in x/e/bl via $4 levels of directory
function create_symlinks
{
	if [ $FAST_MODE = 1 ]; then
		links=4
	else
		links=$(random $2 $3)
	fi

	backpath=$(str_repeat '../' $4)/x/e/bl/

	for ((f=0; f<$links; f+=1)); do
		ln -s $backpath/$f $1/$f
	done
}

##
## Main script execution
##

cd $MOUNT

echo "Making top level directories"

# Top-level directories
mkdir x s l t


#
# The l directory
#

echo "processing 'l' directory"

mkdir l/l
cd l/l

mkfile d 2000 4000
mkfile r 2000 4000
mkfile p 2000 4000

#
# The x directory
#

echo "processing 'x' directory"

cd $MOUNT
cd x
mkdir e
cd e

mkdir bl ca di fe fen fi fr gl la mf pa pr ps st su

# x/e/bl

echo "x/e/bl...."

# This value will be used later on
BL_FILES=$(random 3000 4000)
mkfiles 'bl' $BL_FILES $BL_FILES 300 3000
echo "Created bl files"

# x/e/pr

echo "x/e/pr...."

cd $MOUNT/x/e/pr

tput sc
create_nested_dirs2 3 100 2 500 2000 1

# x/s/fe - simple nesting
echo "Making x/e/fe"

cd $MOUNT/x/e/fe

mkdir st mf ho ca su sp

mkfiles 'su' 5 10 2000 15000
mkfiles 'sp' 800 2000 80000

# x/s/fen - simple nesting
echo "Making x/e/fen"

cd $MOUNT/x/e/fen

mkdir st ma ho ca su sp

mkfiles 'ca' 90 100 500 8000
mkfiles 'ho' 50 80 8000 90000
mkfiles 'ma' 1000 1200 40 6000
mkfiles 'sp' 800 1000 400 9000
mkfiles 'st' 10 20 40 13000
mkfiles 'su' 600 700 40 7000

# x/s/ps - simple nesting
echo "Making x/e/ps"

cd $MOUNT/x/e/ps

create_nested_dirs2 3 100 1 2000 3000 1

# x/e/* (remainder directories - a litle simpler as they don't nest)
cd $MOUNT/x/e

echo "Making simple directories in x/e"

mkfiles 'ca' 80 100 500 4500
mkfiles 'di' 130 150 130000 320000
mkfiles 'fi' 1 1 9000 10000
mkfiles 'fr' 1 1 11000 13000
mkfiles 'gl' 1 1 200 300
mkfiles 'la' 5 10 300 800
mkfiles 'mf' 1000 1200 150 300
mkfiles 'pa' 4000 5000 400 30000
mkfiles 'st' 10 15 500 5000
mkfiles 'su' 500 600 600 1000

#
# The s directory
#

# This is the really fun part, involving many nested symlinks

cd $MOUNT/s

mkdir ca ho ma pr sp st su

# s/ho
cd $MOUNT/s/ho

mkdir Fe

cd Fe

tput sc

for ((fe=1; fe<=4; fe+=1)); do
	mkdir -p $fe/St/1/s/b

	mkfile $fe/St/1/s/s 200 300

	create_symlinks $fe/St/1/s/b 50 110 8
done

# s/sp
echo "Creating s/sp"
cd $MOUNT/s/sp

mkdir Sp

cd Sp

for ((d=1; d<=40; d+=1)); do
	for ((d2 = 1; d2<=2; d2+=1)); do
		mkdir -p $d/$d2/s/b

		mkfile $d/$d2/s/s 200 300

		create_symlinks $d/$d2/s/b 50 110 7
	done
done

# s/pr
echo "Creating s/pr"
cd $MOUNT/s/pr

mkdir Pr

cd Pr

# Crude. Just three deep: 100 x 100 x 6 to give us about 60k of them
d1_size=100
d2_size=100
d3_size=6

if [ $FAST_MODE = 1 ]; then
	d1_size=4
	d2_size=4
	d3_size=4
fi

for ((d=0; d<$d1_size; d+=1)); do
	for ((d2 = 0; d2<$d2_size; d2+=1)); do
		for ((d3 = 0; d3<$d3_size; d3+=1)); do
		        tput el1
	        	tput rc
		        echo -n "$d/$d2/$d3                         "

			mkdir -p $d/$d2/$d3/s/b

			mkfile $d/$d2/$d3/s/s 200 300

			create_symlinks $d/$d2/$d3/s/b 50 110 8
		done
	done
done

# That will do for now. The rest can be handled via the update emulation script
