#!/bin/sh

# container-tools - Manage systemd-nspawn containers
# Copyright (C) 2014-2017 Daniel Baumann <daniel.baumann@open-infrastructure.net>
#
# This program 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 3 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, see <http://www.gnu.org/licenses/>.

set -e

COMMAND="$(basename ${0})"

CONFIG="/etc/container-tools/config"
HOOKS="/etc/container-tools/hooks"
MACHINES="/var/lib/machines"

VERSION="$(container version)"

Parameters ()
{
	GETOPT_LONGOPTIONS="all,csv-separator:,format:,host:,nwdiag-color:,nwdiag-label:,other,started,stopped,"
	GETOPT_OPTIONS="a,f:,h:,o,s,t,"

	PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})"

	if [ "${?}" != "0" ]
	then
		echo "'${COMMAND}': getopt exit" >&2
		exit 1
	fi

	eval set -- "${PARAMETERS}"

	while true
	do
		case "${1}" in
			-a|--all)
				LIST="${LIST} all"
				shift 1
				;;

			--csv-separator)
				CSV_SEPARATOR="${2}"
				shift 2
				;;

			-f|--format)
				FORMAT="${2}"
				shift 2
				;;

			-h|--host)
				HOST="${2}"
				shift 2
				;;

			--nwdiag-color)
				NWDIAG_COLOR="${2}"
				shift 2
				;;

			--nwdiag-label)
				NWDIAG_LABEL="${2}"
				shift 2
				;;

			-o|--other)
				LIST="${LIST} other"
				shift 1
				;;

			-s|--started)
				LIST="${LIST} started"
				shift 1
				;;

			-t|--stopped)
				LIST="${LIST} stopped"
				shift 1
				;;

			--)
				shift 1
				break
				;;

			*)
				echo "'${COMMAND}': getopt error" >&2
				exit 1
				;;
		esac
	done
}

Usage ()
{
	echo "Usage: container ${COMMAND} [-a|--all] [--csv-separator SEPARATOR] [--format FORMAT] [-h|--host HOSTNAME] [--nwdiag-color COLOR] [--nwdiag-label LABEL] [-o|--other] [-s|--started] [-t|--stopped]" >&2
	exit 1
}

Parameters "${@}"

LIST="${LIST:-started stopped}"
FORMAT="${FORMAT:-cli}"
HOST="${HOST:-$(hostname -f)}"

CSV_SEPARATOR="${CSV_SEPARATOR:-,}"

# Pre hooks
for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}"
do
	if [ -x "${FILE}" ]
	then
		"${FILE}"
	fi
done

# Run
List ()
{
	case "${FORMAT}" in
		cli)
			printf "%1s %-74s %-19s\n" "${STATUS}" "${BLUE}${CONTAINER}${NORMAL}" "${YELLOW}${ADDRESS}${NORMAL}"
			;;

		csv)
			echo "${HOST}${CSV_SEPARATOR}${CONTAINER}${CSV_SEPARATOR}${STATE}${CSV_SEPARATOR}${ADDRESS}"
			;;

		json)

cat << EOF
    {
      "name": "${CONTAINER}",
      "status": "${STATE}",
      "ipv4Address": "${ADDRESS}",
    },
EOF

			;;

		nwdiag)
			Nwdiag_width "${CONTAINER}"

			case "${STATE}" in
				started)
					COLOR="#73d216"
					;;

				stopped)
					COLOR="#cc0000"
					;;

				other)
					COLOR="#d3d7cf"
					;;
			esac

			echo "    ${CONTAINER} [address = \"${ADDRESS}\", color = \"${COLOR}\", shape = \"flowchart.terminator\", width = \"${WIDTH}\"];"
			;;

		shell)
			echo "${CONTAINER}"
			;;

		yaml)

cat << EOF
    - name:          ${CONTAINER}
      ipv4_address:  ${ADDRESS}
EOF

			;;

		xml)

cat << EOF
  <container>
    <name>${CONTAINER}</name>
    <ipv4Address>${ADDRESS}</ipv4Address>
  </container>
EOF

			;;
	esac
}

Nwdiag_width ()
{
	NAME="${1}"

	CHARACTERS="$(echo "${NAME}" | wc -c)"

	if [ "${CHARACTERS}" -gt 13 ]
	then
		# default width is 128 fitting 13 characters
		WIDTH="$(( ${CHARACTERS} - 13 ))"
		WIDTH="$(( ${WIDTH} * 5 ))"
		WIDTH="$(( ${WIDTH} + 128 ))"
	else
		WIDTH="128"
	fi
}

case "${FORMAT}" in
	cli)
		RED="$(tput setaf 1)$(tput bold)"
		GREEN="$(tput setaf 2)$(tput bold)"
		YELLOW="$(tput setaf 3)$(tput bold)"
		BLUE="$(tput setaf 4)$(tput bold)"
		WHITE="$(tput setaf 7)$(tput bold)"
		NORMAL="$(tput sgr0)"

cat << EOF
${WHITE} ${NORMAL} Container                                                   IPv4 Address(es)
--------------------------------------------------------------------------------
EOF

		;;

	csv)

cat << EOF
# container-tools version ${VERSION}
Host${CSV_SEPARATOR}Container${CSV_SEPARATOR}Status${CSV_SEPARATOR}IPv4-Address
EOF

		;;

	json)

cat << EOF
{
  "container-tools": {
    "version": "${VERSION}",
  },
  "host": {
    "name": "${HOST}",
  },
  "container": [
EOF

		;;

	nwdiag)
		NETWORK="$(echo ${HOST} | sed -e 's|\.|_|g')"

cat << EOF
# container-tools ${VERSION}
nwdiag {
  external_connector = none;
  network ${NETWORK} {
EOF

	if [ -n "${NWDIAG_LABEL}" ]
	then
		echo "    label = \"${NWDIAG_LABEL}\""
	else
		echo "    label = \"\""
	fi

	NUMBER="$(${0} --format=short | wc -l)"

	if [ -n "${NWDIAG_COLOR}" ]
	then
		COLOR="${NWDIAG_COLOR}"
	else
		COLOR="#3465a4"
	fi

	Nwdiag_width "${HOST}"

cat << EOF
    # host
    ${HOST} [color = "${COLOR}", shape = "box", numbered = "${NUMBER}", width = "${WIDTH}"];
    # container
EOF

		;;

	yaml)

cat << EOF
---
container_tools:
    version:  ${VERSION}

host:
    name:  ${HOST}

container:
EOF

		;;

	xml)

cat << EOF
<container-tools>
  <version>${VERSION}</version>
</container-tools>
<host>
  <name>${HOST}</name>
</host>
<containers>
EOF

		;;
esac

if ls "${MACHINES}"/* > /dev/null 2>&1
then
	CONTAINERS="$(cd "${MACHINES}" 2>/dev/null && find -maxdepth 1 -type d -and -not -name 'lost+found' -printf '%P\n' | sort)"
fi

for CONTAINER in ${CONTAINERS}
do
	STATE="$(machinectl show ${CONTAINER} 2>&1 | awk -F= '/^State=/ { print $2 }')"

	if [ -e "${CONFIG}/${CONTAINER}.conf" ]
	then
		CONTAINER_SERVER="$(awk -F= '/^cnt.container-server=/ { print $2 }' ${CONFIG}/${CONTAINER}.conf)"
		CONTAINER_SERVER="${CONTAINER_SERVER:-false}"

		case "${CONTAINER_SERVER}" in
			${HOST}|true)
				;;

			*)
				STATE="other"
				;;
		esac
	else
		STATE="other"
	fi

	case "${STATE}" in
		running)
			STATE="started"
			STATUS="${GREEN}●${NORMAL}"
			;;

		other)
			STATUS="${WHITE}○${NORMAL}"
			;;

		*)
			STATE="stopped"
			STATUS="${RED}●${NORMAL}"
			;;
	esac

	ADDRESS=""

	if ls "${MACHINES}/${CONTAINER}/etc/systemd/network"/*.network > /dev/null 2>&1
	then
		ADDRESS="$(awk -F= '/Address/ { print $2 }' ${MACHINES}/${CONTAINER}/etc/systemd/network/*.network | head -n1)"
	elif [ -e "${MACHINES}/${CONTAINER}/etc/network/interfaces" ]
	then
		ADDRESS="$(awk '/address/ { print $2 }' ${MACHINES}/${CONTAINER}/etc/network/interfaces | head -n1)"
	fi

	ADDRESS="${ADDRESS:-n/a}"

	if echo ${LIST} | grep -qs all
	then
		List
	fi

	for ITEM in other started stopped
	do
		if echo ${LIST} | grep -qs ${ITEM}
		then
			case "${STATE}" in
				${ITEM})
					List
					;;
			esac
		fi
	done
done

case "${FORMAT}" in
	cli)

cat << EOF
--------------------------------------------------------------------------------
EOF

		;;

	json)

cat << EOF
  ],
}
EOF

		;;

	nwdiag)

cat << EOF
  }
}
EOF

		;;

	xml)

cat << EOF
</containers>
EOF

		;;
esac

# Post hooks
for FILE in "${HOOKS}/post-${COMMAND}".* "${HOOKS}/${NAME}.post-${COMMAND}"
do
	if [ -x "${FILE}" ]
	then
		"${FILE}"
	fi
done