#!/usr/bin/bash

CONFIG_DIR="/etc/vstorage"
CLUSTERS_DIR="${CONFIG_DIR}/clusters"
SYSTEMD_DIR="/usr/lib/systemd/system"
SYSTEMD_TOOL="/usr/bin/systemctl"
LOCK_FILE=".lock"

mdsd_target_file() {
cat  <<EOFT
[Unit]
Description=Metadata server(MDS) target for $CLUSTER_NAME cluster
PartOf=vstorage-mdsd.target

[Install]
WantedBy=vstorage-mdsd.target
EOFT
}

csd_target_file() {
cat  <<EOFT
[Unit]
Description=Chunk server(CS) target for $CLUSTER_NAME cluster
PartOf=vstorage-csd.target

[Install]
WantedBy=vstorage-csd.target
EOFT
}

create_cluster_target() {
	local OP="$1"
	local CLUSTER_NAME="$2"
	
	#example: vstorage-csd.my_pcs4.target
	local TARGET_NAME="vstorage-${DAEMON_NAME}.${CLUSTER_NAME}.target"
	[ -f $TARGET_NAME ] && return 0

	$OP > ${SYSTEMD_DIR}/${TARGET_NAME}
	$SYSTEMD_TOOL --quiet enable ${TARGET_NAME}
}

service_unit_file() {
cat  <<EOFT
[Unit]
Description=vstorage-$DAEMON_NAME($DAEMON_DIR)
PartOf=vstorage-$DAEMON_NAME.$CLUSTER_NAME.target
Before=remote-fs-pre.target
After=vstorage-$DAEMON_NAME-pre.target

[Service]
Type=forking
Slice=vstorage-services.slice
Environment="DAEMON=$DAEMON_NAME" "CLUSTER=$CLUSTER_NAME" "DAEMON_DIR=$DAEMON_DIR"
Environment="PIDFILE=$PIDFILE"
ExecStart=/usr/libexec/vstorage/vstorage-service start \$CLUSTER \$DAEMON \$DAEMON_DIR \$PIDFILE
ExecStartPost=/usr/libexec/vstorage/vstorage-service report \$CLUSTER \$DAEMON \$DAEMON_DIR -i "was started"
ExecStopPost=/usr/libexec/vstorage/vstorage-service report \$CLUSTER \$DAEMON \$DAEMON_DIR -i "was stopped"
PIDFile=$PIDFILE
Restart=on-failure
RestartPreventExitStatus=222
RestartSec=1s
RestartForceExitStatus=0
StartLimitInterval=5min
StartLimitBurst=10
TimeoutStopSec=60s
LimitMEMLOCK=infinity
LimitRTPRIO=infinity

[Install]
WantedBy=vstorage-$DAEMON_NAME.$CLUSTER_NAME.target
EOFT
}

create_daemon_service() {
	local CLUSTER_NAME="$1"
	local DAEMON_DIR="$2"

	[ ! -d "$DAEMON_DIR" ] && return 0

	[ -f "$DAEMON_DIR/control/id" ] && SERVICEID=$(cat "$DAEMON_DIR/control/id")
	[ -f "$DAEMON_DIR/id" ] && SERVICEID=$(cat "$DAEMON_DIR/id")
	[ -z "$SERVICEID" ] && return 0

	#unit file example vstorage-mdsd.my_pcs4.1.service
	local UNIT_NAME="vstorage-${DAEMON_NAME}.${CLUSTER_NAME}.${SERVICEID}.service"
	local UNIT_FILE="${SYSTEMD_DIR}/${UNIT_NAME}"
	if [ -f $UNIT_FILE ] ; then
		# check if unit file includes common stub which is being upgraded separately from the unit file
		grep "\.include" $UNIT_FILE >/dev/null
		if [ $? -ne 0 ]; then
			return 0
		fi
		# if not replace it with new version without .include directive
	fi

	PIDFILE="/run/vstorage/$DAEMON_NAME.$CLUSTER_NAME.$SERVICEID.pid"
	service_unit_file > ${UNIT_FILE}
	$SYSTEMD_TOOL --quiet enable ${UNIT_NAME}
}

for_cluster() {
	local OP="$1"
	local CLUSTER_NAME="$2"
	local CLUSTER_DIR="${CLUSTERS_DIR}/$CLUSTER_NAME"
	# lock per-cluster lock before accessing lists
	( flock -x -w 10 100 || exit 1
		cat "${CLUSTER_DIR}/$LIST_FILE" 2>/dev/null | grep -v '^#' | grep -v '^\s*$' | while read dir; do
			# '100<&-' explicitely closes fd 100 for childs, especially monitor
			("$OP" "${CLUSTER_NAME}" "${dir}") 100<&-
		done
	) 100>"${CLUSTER_DIR}/$LOCK_FILE" || echo "Failed to obtain lock: ${CLUSTER_DIR}/$LOCK_FILE" 1>&2
}

for_all() {
	local OP1="$1"
	local OP2="$2"
	ls -d "${CLUSTERS_DIR}"/* 2>/dev/null | while read cluster; do
		local CLUSTER_NAME=$(basename "${cluster}")
		create_cluster_target "${OP2}" "${CLUSTER_NAME}"
		for_cluster "${OP1}" "${CLUSTER_NAME}"
	done
}

new_target() {
	local cluster_name="$1"
	if [ -z "$cluster_name" ]; then
		echo "new-target: Cluster name not specified" 1>&2
		exit 1
	fi

	if [ ! -d "${CLUSTERS_DIR}/$cluster_name" ]; then
		echo "new-service: this host is not authenticated in cluster '$cluster_name'" 1>&2
		exit 2
	fi

	case "$SERVICE" in
		mds)
			create_cluster_target mdsd_target_file "$cluster_name"
			;;
		cs)
			create_cluster_target csd_target_file "$cluster_name"
			;;
	esac
}

new_service() {
	local cluster_name="$1"
	local dir="$2"
	
	if [ -z "$cluster_name" ] ; then
		echo "new-service: Cluster name not specified" 1>&2
		print_usage
	fi
	
	if [ -z "$dir" ]; then
		echo "new-service: Path to service repo not specified" 1>&2
		print_usage
	fi

	if [ ! -d "${CLUSTERS_DIR}/$cluster_name" ]; then
		echo "new-service: this host is not authenticated in cluster '$cluster_name'" 1>&2
		exit 2
	fi

	create_daemon_service "$cluster_name" "$dir"
}

do_restore() {
	case "$SERVICE" in
		mds)
			for_all create_daemon_service mdsd_target_file
			;;
		cs)
			for_all create_daemon_service csd_target_file
			;;
	esac
}

print_usage() {
cat 1>&2 <<EOFT 

vstorage-gen CMD [OPTS]
Commands:
  new-target   - create new target unit
     vstorage-gen new-target cs|mds \$cluster-name
  new-service   - create new service unit for csd or mdsd
     vstorage-gen new-service cs|mds \$cluster-name \$service-dir
  restore-units - restore unit files for all mdsd and csd serices on this host
     vstorage-gen restore-units cs|mds"

EOFT
	exit 1
}

handle_gen()
{
	local cmd="$1"
	shift

	SERVICE="$1"
	if [ -z "$SERVICE" ] ; then
		echo "Service type (cs|mds) must be specified" 1>&2
		print_usage
	fi

	DAEMON_NAME="${SERVICE}d"
	SUBSYS="vstorage-${DAEMON_NAME}"

	if [ "x$SERVICE" != "xcs" -a "x$SERVICE" != "xmds" ] ;  then
		echo "Unknown service type, expected 'mds' or 'cs'" 1>&2
		print_usage
	fi

	shift

	case "$cmd" in
		new-target)
			new_target "$1"
			;;
		new-service)
			new_service "$1" "$2"
			;;
		restore-units)
			LIST_FILE="${SERVICE}.list"
			do_restore
			;;
		*)
			echo "Unknown command '$cmd'" 1>&2
			print_usage
	esac
}

handle_gen "$@"
