#!/bin/sh -efu
#
# Copyright (C) 2010  Paul Wolneykien <manowar@altlinux.org>
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#

. shell-error
. shell-quote
. shell-args
. shell-config

. zabbix-sh-functions

show_help()
{
	cat <<EOF
Usage: $PROG [options] -b base-dump configuration-source

$PROG produces a set of configuration files complementing the given installation base of a Zabbix node.

* Main options *

  Use DB dump DUMP as the configuration base:

  -b DUMP, --base-dump=DUMP

  The resulting configuration files for Zabbix server and frontend are produced by applying options to the default or specifued reference files. Use the following options to specify these reference files.

  Zabbix server reference configuration file:

  -z SRVCONF, --server-reference-config=SRVCONF

  If not specified the default reference file is used.

  Zabbix frontend reference configuration file:

  -f FNTCONF, --frontend-reference-config=FNTCONF

  If not specified the default reference file is used.


* Agent configuration options *


  The resulting agent configuration file is produced by applying options to the default or specified reference configuration file.

  Zabbix agent reference cofiguration file:

  -a ACONF, --agent-reference-config=ACONF

  If not specified the default reference file is used.

  The agent hostname, server IP address and other options are taken from the main source configuration file of the node.


* Database options *

  -D TYPE, --database-type=TYPE    selects a type TYPE of the target database (use \`list' as TYPE to see what DB engines are supported).

  The following options is used to set the basic DB access parameters to be used by Zabbix server and frontend:

  -d NAME, --database=NAME	the database name;
  -u USER, --user=USER		the name of the database user;
  -p PASSWD, --password=PASSWD	the password of the database user.
 
  If not specified, the database is named after the base name of the input file. For user name and password the defaults are: \`$ZBX_DBUSER' and \`$ZBX_DBPASS' corrrespondingly.

  If Zabbix server and database service are running on the separate hosts use the following option to specify the IP or DNS name of the database service:

  --db-host=ADDR	the database server IP or DNS address.


* Output options *

  Resulting output files are placed into a directory which then is compressed by default.

  Specify the name of the resulting archive/directory:

  -o NAME, --output=NAME

  By default the input configuration file name with suffix discared is used for output file base name. The suffix \`.tar.gz' is used in the case of default packaging.
  
  Pack the resulting fileset with the use of CMD:

  -P CMD, --pack-with=CMD
  
  Use \`%o', \`%d' and \`%F' in the CMD for output file, input directory and file list correspondingly. Default is to use \`tar czf %o.tar.gz %F' to pack the files. Use empty CMD to avoid packaging. All commands are run in the input directory (%d) as current.
  To (over-)write resulting files directly to the system use the option

  --direct-write
  
  This option is a synonym for -P '' and additionaly sets the output path to \`/' by default. Use with caution!

  The layout of the resulting fileset can be controlled with the set of following options.

  Path of the home directory:

  -H HOME, --home-dir=HOME
  
  Default: root

  Path of the resulting SQL-scripts:
  
  -S PATH, --sql-path=PATH
  
  Default: HOME/<source-name>-[<type>-]<DB-engine-name>.sql

  With script naming presented above the suffix is discarded from the source name and type is \`update' for the local node update script.

  Path of the resulting Zabbix server configuration file:
  
  -Z PATH, --server-conf-path=PATH
  
  Default: $ZBX_SRVCONF

  Path of the resulting Zabbix PHP frontend configuration file:
  
  -W PATH, --frontend-conf-path=PATH
  
  Default: $ZBX_FRONTCONF

  Path of the resulting Zabbix agent configuration:
  
  -A PATH, --agent-conf-path=PATH
  
  Default: $ZBX_AGENTCONF

  Path to the DB chroot:
  
  --db-chroot=DB-CHROOT
  
  Default: /var/lib/<DB-engine-name>

  Path to place the included image files by:
  
  -I PATH, --images-path=IPATH
  
  Default: /tmp

  Actually images would be placed at the DB-CHROOT/IPATH.

  All paths are relative to the output directory path (-o option).


* Configuration options *

  The resulting node configuration is controlled by the following options.

  Options for map objects:

  --link-color=RGB		link color (default: 0000cc);
  --link-drawtype=NUM		link line type (default: 2);
  --broken-link-color=RGB	broken link color (default: dd0000);
  --broken-link-drawtype=NUM	broken link line type (default: 4).

  The list of item names can be specified to set the corresponding set of triggers as link indicators for each node on the map:

  --link-trigger-items=ITEM-1[,ITEM-2...]

  By default, all triggers based on simple checks for a host connection availability (ICMP ping) are used as link indicators.

  Alternatively the list of trigger names can be specified explicitly:

  --link-triggers=TRIGGER-1[,TRIGGER-2...]

  The following set of options can be used to specify the names, descriptions and other project/locale dependent information.

  Each sub-region represents a remote node. To represent particular parameters of the remote node in the local configuration the mirror items are add to the representative node (a mirror node).
  
  The keys of the items to mirror can be specified with the use of the following option:

  --mirror-items=[KEY[,KEY,...]]

  If no argument is specified for the option then no items are mirrored. By default an atempt is made to mirror the following items: \`net.status', \`net.status.monitored' and \`net.count'.


* Usage options *

  The following options control the program execution:

  -h, --help		print this help information;
  --usage		print short program usage information;
  -V, --version		print the program version and license terms;
  -v, --verbose		print information messages while processing the input;
  -q, --quiet		do not print any information messages.


  Please, report bugs to http://bugs.altlinux.ru/

EOF
	exit
}

print_version()
{
	cat <<EOF
$PROG version 1.0
Written by Paul Wolneykien <manowar@altlinux.org>

Copyright (C) 2010 Paul Wolneykien <manowar@altlinux.org>
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
EOF
	exit
}

DATADIR="${DATADIR:-/usr/share}"
PROG_DATADIR="${PROG_DATADIR:-${DATADIR%/}/$PROG}"

OPTS=`getopt -n $PROG \
              -o b:,z:,f:,a:,D:,d:,u:,p:,o:,P:,H:,S:,Z:,W:,A:,I:,h,V,v,q \
              -l base-dump:,server-reference-config:,frontend-reference-config:,agent-reference-config:,database-type:,database:,user:,password:,db-host:,output:,pack-with:,direct-write,home-dir:,sql-path:,server-conf-path:,frontend-conf-path:,agent-conf-path:,db-chroot:,images-path:,link-color:,link-drawtype:,broken-link-color:,broken-link-drawtype:,link-trigger-items:,link-triggers:,mirror-items::,help,usage,version,verbose,quiet -- "$@"` \
	|| show_usage
eval set -- "$OPTS"

BASE_DUMP=;SERVER_REFERENCE_CONFIG=;FRONTEND_REFERENCE_CONFIG=;AGENT_REFERENCE_CONFIG=;DATABASE_TYPE=;DATABASE=;USER=;PASSWORD=;DB_HOST=;OUTPUT=;PACK_WITH=;DIRECT_WRITE=;HOME_DIR=;SQL_PATH=;SERVER_CONF_PATH=;FRONTEND_CONF_PATH=;AGENT_CONF_PATH=;DB_CHROOT=;IMAGES_PATH=;LINK_COLOR=;LINK_DRAWTYPE=;BROKEN_LINK_COLOR=;BROKEN_LINK_DRAWTYPE=;LINK_TRIGGER_ITEMS=;LINK_TRIGGERS=;VERBOSE=;QUIET=

MIRROR_ITEMS='net.status, net.status.monitored, net.count'
PACK_WITH='tar czf %o.tar.gz %F'

while :; do
	case "$1" in
		-b|--base-dump) BASE_DUMP="$2"; shift;;
		-z|--server-reference-config) SERVER_REFERENCE_CONFIG="$2"; shift;;
		-f|--frontend-reference-config) FRONTEND_REFERENCE_CONFIG="$2"; shift;;
		-a|--agent-reference-config) AGENT_REFERENCE_CONFIG="$2"; shift;;
		-D|--database-type) DATABASE_TYPE="$2"; shift;;
		-d|--database) DATABASE="$2"; shift;;
		-u|--user) USER="$2"; shift;;
		-p|--password) PASSWORD="$2"; shift;;
		--db-host) DB_HOST="$2"; shift;;
		-o|--output) OUTPUT="$2"; shift;;
		-P|--pack-with) PACK_WITH="$2"; shift;;
		--direct-write) DIRECT_WRITE="$1";;
		-H|--home-dir) HOME_DIR="$2"; shift;;
		-S|--sql-path) SQL_PATH="$2"; shift;;
		-Z|--server-conf-path) SERVER_CONF_PATH="$2"; shift;;
		-W|--frontend-conf-path) FRONTEND_CONF_PATH="$2"; shift;;
		-A|--agent-conf-path) AGENT_CONF_PATH="$2"; shift;;
		--db-chroot) DB_CHROOT="$2"; shift;;
		-I|--images-path) IMAGES_PATH="$2"; shift;;
		--link-color) LINK_COLOR="$2"; shift;;
		--link-drawtype) LINK_DRAWTYPE="$2"; shift;;
		--broken-link-color) BROKEN_LINK_COLOR="$2"; shift;;
		--broken-link-drawtype) BROKEN_LINK_DRAWTYPE="$2"; shift;;
		--link-trigger-items) LINK_TRIGGER_ITEMS="$2"; shift;;
		--link-triggers) LINK_TRIGGERS="$2"; shift;;
		--mirror-items) MIRROR_ITEMS="$2"; shift;;
		-h|--help) show_help;;
		--usage) show_usage;;
		-V|--version) print_version;;
		-v|--verbose) VERBOSE="$1";;
		-q|--quiet) QUIET="$1";;
		--) shift; break;;
		*) fatal "unrecognized option: $1";;
	esac
	shift
done

[ -n "$QUIET" ] || verbose=$VERBOSE

xsltpath="${PROG_DATADIR%/}/xslt"
reset_basename=-reset.xslt
insert_basename=-insert.xslt
update_basename=-update-node.xslt

list_dbtypes() {
	OIFS=$IFS
	IFS=:
	for p in $xsltpath; do
		[ -d "$p" ] && \
		ls -1 "$p" | while read f; do
			local t="${f%$reset_basename}"
			[ "$t" != "$f" ] && echo $t
			done
	done
	IFS=$OIFS
}

[ -n "$DATABASE_TYPE" ] || DATABASE_TYPE=mysql
if [ "$DATABASE_TYPE" = "list" ]; then
	list_dbtypes
	exit 0
fi

SOURCE="${1:-}"
[ -n "$SOURCE" ] || fatal "Specify an input configuration file"
[ -r "$SOURCE" ] || fatal "The input file '$SOURCE' is not readable"
[ -n "$BASE_DUMP" ] || fatal "Specify a base DB dump"
[ -r "$BASE_DUMP" ] || fatal "The base DB dump file '$BASE_DUMP' is not readable"

remove_subdir() {
	if [ -n "${1:-}" ] && [ "$1" != "/" ]; then
		rm -rf -- "$1"
	fi
}

initial_dir=$PWD
cleanup_handler()
{
	remove_subdir "${workdir:-}"
	if [ -z "$DIRECT_WRITE" ]; then
		remove_subdir "${targetdir:-}"
	fi
	[ "$PWD" = "$initial_dir" ] || cd "$initial_dir"
}
trap cleanup_handler EXIT
trap cleanup_handler HUP PIPE INT QUIT TERM

workdir="$(mktemp -dt "$PROG.XXXXXXXXXX")"
[ -d "$workdir" ] || fatal "Unable to create the work directory"

if [ -z "$DIRECT_WRITE" ]; then
	targetdir="$(mktemp -dt "$PROG.XXXXXXXXXX")"
	if [ -z "$OUTPUT" ]; then
		OUTPUT="${SOURCE%.*}"
	fi
else
	[ -n "$OUTPUT" ] || OUTPUT="/"
	if [ "$OUTPUT" != "/" ]; then
		targetdir="${OUTPUT%/}"
		[ -d "$targetdir" ] || mkdir -p "$targetdir"
	else
		targetdir="/"
	fi
fi
[ -d "$targetdir" ] || fatal "Unable to create the target directory"

if [ "${OUTPUT#/}" == "$OUTPUT" ]; then
	OUTPUT="$PWD/$OUTPUT"
fi

[ -n "$DB_CHROOT" ] || DB_CHROOT="/var/lib/$DATABASE_TYPE"
[ -n "$IMAGES_PATH" ] || IMAGES_PATH="/tmp"

[ -n "$LINK_COLOR" ] || LINK_COLOR='0000cc'
[ -n "$BROKEN_LINK_COLOR" ] || BROKEN_LINK_COLOR='dd0000'
[ -n "$LINK_DRAWTYPE" ] || LINK_DRAWTYPE=2
[ -n "$LINK_TRIGGER_ITEMS" ] || LINK_TRIGGER_ITEMS='icmpping%'
write_insert() {
	xsltproc --path "$xsltpath" \
		 --stringparam "image-prefix" "$IMAGES_PATH" \
		 --stringparam "link-color"  "$LINK_COLOR" \
		 --stringparam "broken-link-color" "$BROKEN_LINK_COLOR" \
		 --stringparam "link-drawtype" "$LINK_DRAWTYPE" \
		 --stringparam "mirror-items" "$MIRROR_ITEMS" \
		 --stringparam "link-triggers" "$LINK_TRIGGERS" \
		 --stringparam "link-trigger-items" "$LINK_TRIGGER_ITEMS" \
		 "${DATABASE_TYPE}${insert_basename}" \
		 "$SOURCE"
}

source_basename="$(basename "${SOURCE%.*}")"
[ -n "$DATABASE" ] || DATABASE="$source_basename"
[ -n "$USER" ] || USER='zabbix'
[ -n "$PASSWORD" ] || PASSWORD='zabbix'
[ -n "$DB_HOST" ] || DB_HOST='localhost'
write_reset() {
	xsltproc --path "$xsltpath" \
		 --stringparam "dbname" "$DATABASE" \
		 --stringparam "user" "$USER" \
		 --stringparam "dbpass" "$PASSWORD" \
		  "${DATABASE_TYPE}${reset_basename}" \
		  "$SOURCE"
}

write_update() {
	xsltproc --path "$xsltpath" \
		 --stringparam "dbname" "$DATABASE" \
		 "${DATABASE_TYPE}${update_basename}" \
		 "$SOURCE"
}

[ -n "$HOME_DIR" ] || HOME_DIR='root'
mkdir -p "$targetdir/${HOME_DIR%/}" || fatal "Unable to create target home directory for SQL scripts"

configuration_sql="${targetdir%/}/${HOME_DIR%/}/${source_basename}-${DATABASE_TYPE}.sql"
verbose "Output the DB reset script"
write_reset > "$configuration_sql"

verbose "Append the DB base dump"
echo >> "$configuration_sql"
cat "$BASE_DUMP" >> "$configuration_sql"

verbose "Append the node configuration script"
echo >> "$configuration_sql"
write_insert >> "$configuration_sql"

update_sql="${targetdir%/}/${HOME_DIR%/}/${source_basename}-update-${DATABASE_TYPE}.sql"
verbose "Output the local node update script"
write_update > "$update_sql"

xsltproc --path "$xsltpath" extract-map-filenames.xslt "$SOURCE" \
| while read i; do
	verbose "Copy '$i' map image"
	install -m 0644 -D "$(dirname "$SOURCE")/$i" "${targetdir%/}/${DB_CHROOT%/}/${IMAGES_PATH%/}/$(basename "$i")"
  done

[ -n "$SERVER_CONF_PATH" ] || SERVER_CONF_PATH="$ZBX_SRVCONF"

zbx_srvconf_dir="${targetdir%/}$(dirname "$SERVER_CONF_PATH")"
mkdir -p "$zbx_srvconf_dir"
metadata_file="$zbx_srvconf_dir/NODE"
verbose "Output the node metatada file"
xsltproc --path "$xsltpath" extract-metadata.xslt "$SOURCE" > "$metadata_file"
echo "DBTYPE: $DATABASE_TYPE" >> "$metadata_file"
echo "DBNAME: $DATABASE" >> "$metadata_file"
cat "$metadata_file" > "${workdir%/}/refconf"

[ -n "$SERVER_REFERENCE_CONFIG" ] || SERVER_REFERENCE_CONFIG="${PROG_DATADIR%/}/refconf/zabbix_server.conf"
install -m 0644 -D "$SERVER_REFERENCE_CONFIG" "${targetdir%/}/${SERVER_CONF_PATH#/}"
zbxsrv_set "${targetdir%/}/${SERVER_CONF_PATH#/}" 'DBName' "$DATABASE"
zbxsrv_set "${targetdir%/}/${SERVER_CONF_PATH#/}" 'DBUser' "$USER"
zbxsrv_set "${targetdir%/}/${SERVER_CONF_PATH#/}" 'DBPassword' "$PASSWORD"
nodeid="$(shell_config_get "${workdir%/}/refconf" 'ID' ': ')"
[ -n "$nodeid" ] || nodeid=0
zbxsrv_set "${targetdir%/}/${SERVER_CONF_PATH#/}" 'NodeID' "$nodeid"
downlink="$(shell_config_get "${workdir%/}/refconf" 'DOWNLINK' ': ')"
if [ -n "$downlink" ]; then
	zbxsrv_set "${targetdir%/}/${SERVER_CONF_PATH#/}" 'ListenIP' \
		   "127.0.0.1,$downlink"
else
	zbxsrv_set "${targetdir%/}/${SERVER_CONF_PATH#/}" 'ListenIP' \
		   "127.0.0.1"
fi

[ -n "$FRONTEND_CONF_PATH" ] || FRONTEND_CONF_PATH="$ZBX_FRONTCONF"
[ -n "$FRONTEND_REFERENCE_CONFIG" ] || FRONTEND_REFERENCE_CONFIG="${PROG_DATADIR%/}/refconf/zabbix.conf.php"
install -m 0644 -D "$FRONTEND_REFERENCE_CONFIG" \
		   "${targetdir%/}/${FRONTEND_CONF_PATH#/}"
zbxfront_set "${targetdir%/}/${FRONTEND_CONF_PATH#/}" \
	  '$DB["TYPE"]' \
	  "'$(echo "$DATABASE_TYPE" | tr [[:lower:]] [[:upper:]])';"
zbxfront_set "${targetdir%/}/${FRONTEND_CONF_PATH#/}" \
	  '$DB["SERVER"]' "'$DB_HOST';"
zbxfront_set "${targetdir%/}/${FRONTEND_CONF_PATH#/}" \
	  '$DB["DATABASE"]' "'$DATABASE';"
zbxfront_set "${targetdir%/}/${FRONTEND_CONF_PATH#/}" \
	  '$DB["USER"]' "'$USER';"
zbxfront_set "${targetdir%/}/${FRONTEND_CONF_PATH#/}" \
	  '$DB["PASSWORD"]' "'$PASSWORD';"

[ -n "$AGENT_CONF_PATH" ] || AGENT_CONF_PATH="$ZBX_AGENTCONF"
[ -n "$AGENT_REFERENCE_CONFIG" ] || AGENT_REFERENCE_CONFIG="${PROG_DATADIR%/}/refconf/zabbix_agentd.conf"
install -m 0644 -D "$AGENT_REFERENCE_CONFIG" \
		   "${targetdir%/}/${AGENT_CONF_PATH#/}"
uplink="$(shell_config_get "${workdir%/}/refconf" 'UPLINK' ': ')"
servers="$uplink"
downlink="$(shell_config_get "${workdir%/}/refconf" 'DOWNLINK' ': ')"
if [ -z "$servers" ]; then
	servers="$downlink"
elif [ -n "$downlink" ]; then
	servers="$servers,$downlink"
fi
if [ -n "$servers" ]; then
	zbxagent_set "${targetdir%/}/${AGENT_CONF_PATH#/}" \
		     'Server' "$servers"
else
	zbxagent_set "${targetdir%/}/${AGENT_CONF_PATH#/}" \
                     'Server' '127.0.0.1'
fi
hostname="$(shell_config_get "${workdir%/}/refconf" 'NAME' ': ')"
if [ -n "$hostname" ]; then
	zbxagent_set "${targetdir%/}/${AGENT_CONF_PATH#/}" \
		     'Hostname' "$hostname"
else
	zbxagent_comment "${targetdir%/}/${AGENT_CONF_PATH#/}" \
			 'Hostname'
fi
if [ -n "$downlink" ]; then
	zbxagent_set "${targetdir%/}/${AGENT_CONF_PATH#/}" \
		     'ListenIP' "127.0.0.1,$downlink"
else
	zbxagent_set "${targetdir%/}/${AGENT_CONF_PATH#/}" \
	             'ListenIP' "127.0.0.1"
fi

expand_vars() {
	local filelist="$(ls -1 "${targetdir%/}" | tr '\n' ' ')"
	echo "$1" | sed -e "s/%o/$(quote_sed_regexp "${OUTPUT%/}")/g" \
			-e "s/%d/$(quote_sed_regexp "${targetdir%/}")/g" \
			-e "s/%F/$(quote_sed_regexp "$filelist")/g"
}

if [ -z "$DIRECT_WRITE" ]; then
	if [ -n "$PACK_WITH" ]; then
		pcmd="$(expand_vars "$PACK_WITH")"
		verbose "Pack the files with $PACK_WITH"
		olddir="$PWD"
		cd "${targetdir%/}"
		$pcmd
		ret=$?
		cd "$olddir"
		[ $ret = 0 ] || fatal "Error while run the pack command: $pcmd"
	else
		ccmd="$(expand_vars 'cp -R %d %o')"
		verbose "Copy the generated files to the target directory ${OUTPUT%/}"
		$ccmd || fatal "Error while copy the files"
	fi
fi
