#!/bin/sh -fu

. shell-config

ZABBIX_ITEM_VALUE_TYPE_FLOAT=0
ZABBIX_ITEM_VALUE_TYPE_STR=1
ZABBIX_ITEM_VALUE_TYPE_LOG=2
ZABBIX_ITEM_VALUE_TYPE_UINT64=3
ZABBIX_ITEM_VALUE_TYPE_TEXT=4

ZABBIX_HOST_STATUS_MONITORED=0
ZABBIX_HOST_STATUS_NOT_MONITORED=1
#ZABBIX_HOST_STATUS_UNREACHABLE=2
ZABBIX_HOST_STATUS_TEMPLATE=3
#ZABBIX_HOST_STATUS_DELETED=4
ZABBIX_HOST_STATUS_PROXY_ACTIVE=5
ZABBIX_HOST_STATUS_PROXY_PASSIVE=6

ZABBIX_ITEM_STATUS_ACTIVE=0
ZABBIX_ITEM_STATUS_DISABLED=1
ZABBIX_ITEM_STATUS_NOTSUPPORTED=3

ZABBIX_CONF=/etc/zabbix/zabbix_server.conf

_IFS=�
_split_tabs() {
        tr '\t' '�'
}

_zabbix_dbname() {
        shell_config_get "$ZABBIX_CONF" 'DBName'
}

_zabbix_mysql_query() {
        local stm="$1"; shift
        local ret=

        if [ $# -gt 0 ]; then
                stm="$(printf "$stm" "$@")"
        fi

        echo "$stm" | mysql -B -u root --protocol=SOCKET -N --default-character-set=utf8 "$(_zabbix_dbname)"
        ret=$?

        if [ $ret -ne 0 ]; then
                echo "MySQL error for statement: $stm" >&2
        fi

        return $ret
}

_list_zabbix_mysql_items() {
        _zabbix_mysql_query "select min(i.itemid) as id, i.key_ as key_, min(i.lastvalue) as minvalue, avg(i.lastvalue) as avgvalue, max(i.lastvalue) as maxvalue, min(i.value_type) as ztype, min(i.description) as label from hosts h left join items i on h.hostid = i.hostid where h.status != $ZABBIX_HOST_STATUS_TEMPLATE group by key_ order by 7 asc"
}

list_zabbix_mysql_items() {
        _list_zabbix_mysql_items | _format_zabbix_items
}

_format_zabbix_items() {
        local IFS=$_IFS
        
        _split_tabs | \
        while read -r id key minv avgv maxv type desc; do
                _format_zabbix_item "$id" "$key" "$minv" "$avgv" "$maxv" "$type" "$desc"
        done
}

_format_zabbix_datatype() {
        local type="$1"
        local dtype=

        case "$type" in
                $ZABBIX_ITEM_VALUE_TYPE_FLOAT)
                        dtype='float'
                        ;;
                $ZABBIX_ITEM_VALUE_TYPE_STR)
                        dtype='string'
                        ;;
                $ZABBIX_ITEM_VALUE_TYPE_LOG)
                        dtype='string'
                        ;;
                $ZABBIX_ITEM_VALUE_TYPE_UINT64)
                        dtype='int'
                        ;;
                $ZABBIX_ITEM_VALUE_TYPE_TEXT)
                        dtype='string'
                        ;;
                *)
                        dtype='string'
                        ;;
        esac

        echo "$dtype"
}

_format_zabbix_item_desc() {
        local key="${1-}"
        local desc="${2-}"
        local args=

        if [ $# -eq 0 ]; then
                _split_tabs | (
                        IFS=$_IFS
                        read -r key desc
                        [ -n "$key" ] && _format_zabbix_item_desc "$key" "$desc"
                )
                return $?
        fi

        args="${key#*[}"
        args="${args%]*}"

        if [ -n "$args" ]; then
                desc="$(IFS=','; _fill_zabbix_args "$desc" $args)"
        fi

        echo "$desc"
}

_format_zabbix_item() {
        local id="$1"
        local key="$2"
        local minv="$3"
        local avgv="$4"
        local maxv="$5"
        local ztype="$6"
        local desc="$7"
        local dtype=

        desc="$(_format_zabbix_item_desc "$key" "$desc")"
        dtype="$(_format_zabbix_datatype "$ztype")"
        printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$id" "$dtype" "$minv" "$avgv" "$maxv" "$desc"
}

_fill_zabbix_args() {
        local desc="$1"; shift

        eval "echo \"$desc\""
}

_check_int_value() {
        echo "$1" | grep -q '^[0-9]\+$'
}

_check_float_value() {
        echo "$1" | grep -q '^[0-9.]\+$'
}

write_error() {
        echo "$1" >&2
}

write_error_message() {
        local msg="$1"; shift

        if [ $# -gt 0 ]; then
                msg="$(printf "$msg\\n" "$@")"
        fi

        write_error "$msg"
}

_() {
        echo "$1"
}

_translate_filter() {
        local res="$1"; shift
        local proc="$1"; shift

        sed -e 's/[[:space:]]\+and[[:space:]]\+/\n/g' | (
        while read -r stm; do
                vars="$(echo "$stm" | sed -n -e 's/^[[:space:]]*\[\([^]]\+\)\][[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+"\(.*\)"[[:space:]]*$/itemid="\1"; op="\2"; val="\3";/p' -e 's/^[[:space:]]*\[\([^]]\+\)\][[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^"[:space:]]\+\)[[:space:]]*$/itemid="\1"; op="\2"; val="\3";/p')"
                if [ -z "$vars" ]; then
                        write_error_message "`_ 'Syntax error in expression: %s'`" "$stm"
                        return 1
                fi
                eval "$vars"
                res="$("$proc" "$res" "$itemid" "$op" "$val" "$@")" || return $?
        done
        echo "$res" )
}

_format_zabbix_mysql_value() {
        local itemid="$1"
        local val="$2"
        local ztype=
        local dtype=

        ztype="$(_zabbix_mysql_query "select value_type from items where itemid = %s" "$itemid")" || return $?
        dtype="$(_format_zabbix_datatype "$ztype")"
        case "$dtype" in
                int)
                        if ! _check_int_value "$val"; then
                                write_error_message "`_ 'Not an integer number: %s'`" "$val"
                                return 2
                        fi
                        ;;
                float)
                        if ! _check_float_value "$val"; then
                                write_error_message "`_ 'Not a float number: %s'`" "$val"
                                return 3
                        fi
                        val="$(echo "$val" | sed -e 's/^[0-9]\+$/&.0/')"
                        ;;
                *)
                        val="\"$(echo "$val" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')\""
                        ;;
        esac

        echo "$val"
}

_translate_filter_expr_zabbix_mysql() {
        local filter="$1"
        local itemid="$2"
        local op="$3"
        local val="$4"

        val="$(_format_zabbix_mysql_value "$itemid" "$val")" || return $?
        [ -z "$filter" ] || filter="$filter and"
        filter="$filter h.hostid in (select hostid from items where key_ in (select key_ from items where itemid = $itemid) and lastvalue $op $val)"

        echo "$filter"
}

translate_filter_zabbix_mysql() {
        _translate_filter "" _translate_filter_expr_zabbix_mysql
}

_op_pattern() {
        local op="$1"

        case "$op" in
                =)
                        _ '%s = %s'
                        ;;
                \!=)
                        _ '%s != %s'
                        ;;
                \<)
                        _ '%s < %s'
                        ;;
                \<=)
                        _ '%s <= %s'
                        ;;
                \>)
                        _ '%s > %s'
                        ;;
                \>=)
                        _ '%s >= %s'
                        ;;
                like)
                        _ '%s like %s'
                        ;;
                *)
                        echo "%s $op %s"
                        ;;
        esac
}

_describe_filter_expr_zabbix_mysql() {
        local filter="$1"
        local itemid="$2"
        local op="$3"
        local val="$4"
        local label=

        label="$(_zabbix_mysql_query "select key_, description from items where itemid = %s" "$itemid" | _format_zabbix_item_desc)" || return $?
        val="$(_format_zabbix_mysql_value "$itemid" "$val")" || return $?
        [ -z "$filter" ] || filter="$filter `_ 'AND'`"
        filter="$filter $(printf "$(_op_pattern "$op")" "[$label]" "$val")"

        echo "$filter"
}

describe_filter_zabbix_mysql() {
        _translate_filter "" _describe_filter_expr_zabbix_mysql
}

list_zabbix_mysql_groups() {
        _zabbix_mysql_query "select groupid, name from groups"
}

_zabbix_mysql_hosts_query() {
        local host="${1-}"
        local group="${2-}"
        local filter="${3-}"
        local ipstart=
        local ipend=
        local stm=

        eval "$(echo "$host" | sed -n -e 's/^[[:space:]]*\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)[[:space:]]*-[[:space:]]*\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)[[:space:]]*$/ipstart="\1"; ipend="\2";/p')"
        if [ -n "$ipstart" ] && [ -n "$ipend" ]; then
                [ -z "$filter" ] || filter="$filter and "
                filter="${filter}inet_aton(h.ip) >= inet_aton('$ipstart') and inet_aton(h.ip) <= inet_aton('$ipend')"
                host=
        fi

        stm="select h.hostid, h.host, h.ip as address, ifnull(max(m.value), 'NOTSET') as value, case when h.status != $ZABBIX_HOST_STATUS_MONITORED then 'DISABLED' else case when max(i0.lastvalue) is null or max(i0.lastvalue) not rlike '^[0-9a-f]{40}' then 'UNKNOWN' else max(i0.lastvalue) end end as lastvalue from hosts h left join hostmacro m on h.hostid = m.hostid and m.macro = '{\$ETCGIT_HEAD}' left join items i0 on h.hostid = i0.hostid and i0.key_ = 'system.run[sudo etcgit]' and h.status = $ZABBIX_HOST_STATUS_MONITORED and i0.status = $ZABBIX_ITEM_STATUS_ACTIVE"
        [ -n "$group" ] && stm="$stm left join hosts_groups g on h.hostid = g.hostid"
        stm="$stm where h.status != $ZABBIX_HOST_STATUS_TEMPLATE"
        [ -n "$host" ] && stm="$stm and (h.host like '$host' or h.ip like '$host' or h.dns like '$host')"
        [ -n "$group" ] && stm="$stm and g.groupid = $group"
        [ -n "$filter" ] && stm="$stm and $filter"
        stm="$stm group by h.hostid"

        echo "$stm"
}

_zabbix_mysql_add_hoststatus_filter() {
        local status="$1"
        local filter="$2"

        [ -z "$filter" ] || filter="$filter and"
        case "$status" in
                disabled)
                        filter="$filter h.status != $ZABBIX_HOST_STATUS_MONITORED"
                        ;;
                monitored)
                        filter="$filter h.status = $ZABBIX_HOST_STATUS_MONITORED"
                        ;;
                notset)
                        filter="$filter m.value is null"
                        ;;
                modified)
                        filter="$filter m.value is not null and i0.lastvalue is not null and m.value != i0.lastvalue"
                        ;;
        esac

        echo "$filter"
}

list_zabbix_mysql_hosts() {
        local skip="${4-0}"
        local limit="${5-100}"
        local stm=

        stm="$(_zabbix_mysql_hosts_query "$@") order by 2 asc limit $skip, $limit"
        _zabbix_mysql_query "$stm"
}

stats_zabbix_mysql_hosts() {
        local head="$1"; shift
        local stm=
        local valexpr=

        if [ -n "$head" ]; then
                valexpr="'$head'"
        else
                valexpr="value"
        fi

        stm="select count(hostid), case when value != 'NOTSET' and value = $valexpr then 'OK' when value != 'NOTSET' and value != $valexpr then 'MODIFIED' else 'NOTSET' end as value_status, case when value != 'NOTSET' and lastvalue != 'DISABLED' and lastvalue != 'UNKNOWN' and substring_index(lastvalue, '\t', 1) != $valexpr then 'MODIFIED' when value != 'NOTSET' and lastvalue != 'DISABLED' and lastvalue != 'UNKNOWN' and substring_index(lastvalue, '\t', 1) = $valexpr then 'OK' when lastvalue != 'DISABLED' and lastvalue != 'UNKNOWN' then 'OK' else lastvalue end as lastvalue_status from ($(_zabbix_mysql_hosts_query "$@")) s group by value_status, lastvalue_status"
        _zabbix_mysql_query "$stm"
}

_format_zabbix_values() {
        local IFS=$_IFS
        
        _split_tabs | \
        while read -r id key val type desc; do
                _format_zabbix_value "$id" "$key" "$val" "$type" "$desc"
        done
}

_format_zabbix_value() {
        local id="$1"
        local key="$2"
        local val="$3"
        local ztype="$4"
        local desc="$5"
        local dtype=

        desc="$(_format_zabbix_item_desc "$key" "$desc")"
        dtype="$(_format_zabbix_datatype "$ztype")"
        printf '%s\t%s\t%s\t%s\n' "$id" "$dtype" "$val" "$desc"
}

_list_zabbix_mysql_values() {
        local hostid="$1"

        _zabbix_mysql_query "select i.itemid as id, i.key_ as key_, i.lastvalue as lastvalue, i.value_type as ztype, i.description as label from hosts h left join items i on h.hostid = i.hostid where h.hostid = %s order by 5 asc" "$hostid"
}

list_zabbix_mysql_values() {
        _list_zabbix_mysql_values "$@" | _format_zabbix_values
}

_format_zabbix_host_status() {
        local status="$1"

        case "$status" in
                $ZABBIX_HOST_STATUS_MONITORED)
                        echo 'monitored'
                        ;;
                $ZABBIX_HOST_STATUS_NOT_MONITORED)
                        echo 'notmonitored'
                        ;;
                $ZABBIX_HOST_STATUS_TEMPLATE)
                        echo 'template'
                        ;;
                $ZABBIX_HOST_STATUS_PROXY_ACTIVE)
                        echo 'active-proxy'
                        ;;
                $ZABBIX_HOST_STATUS_PROXY_PASSIVE)
                        echo 'passive-proxy'
                        ;;
                *)
                        echo 'unknown'
                        ;;
        esac
}

read_zabbix_mysql_host() {
        local hostid="$1"

        _zabbix_mysql_query "select hostid, host as hostname, ip, dns, status from hosts where hostid = %s" "$hostid" | (
                IFS=$_IFS
                _split_tabs | \
                while read -r id name ip dns status; do
                        printf '%s\t%s\t%s\t%s\t%s\n' "$id" "$name" "$ip" "$dns" "$(_format_zabbix_host_status "$status")"
                done
        )
}

_add_zabbix_mysql_hostid_filter() {
        local filtersql="$1"; shift
        local hostid="$1"

        if [ -n "$hostid" ]; then
                [ -z "$filtersql" ] || printf '%s or ' "$filtersql"
                printf 'h.hostid = %s' "$hostid"
                printf '\n'
        else
                printf '%s\n' "$filtersql"
        fi
}

hostlist_filter() {
        local hostlist="${1-}"
        local source=
        local filtersql=

        for srchostid in $hostlist; do
                if [ -n "${srchostid%/*}" ]; then
                        source="${srchostid%/*}"
                fi
                [ -n "$source" ] || continue
                case "$source" in
                        zabbix/mysql)
                                filtersql="$(_add_zabbix_mysql_hostid_filter "$filtersql" "${srchostid##*/}")"
                                ;;
                        *)
                                continue
                                ;;
                esac
        done

        echo "$filtersql"
}

zabbix_mysql_set_profile_hash() {
        local hostid="$1"
        local phash="$2"
        local stm=
        local ret=0

        stm="replace hostmacro select ifnull((select hostmacroid from hostmacro where hostid = $hostid and macro = '{\$ETCGIT_HEAD}'), (select (1 + ifnull(max(hostmacroid), 0)) from hostmacro)) as hostmacroid, $hostid as hostid, '{\$ETCGIT_HEAD}' as macro, '$phash' as value"
        _zabbix_mysql_query "$stm" 1>/dev/null 2>/dev/null
        ret=$?
        if [ $ret -eq 0 ]; then
                stm="update ids set nextid = (select coalesce(max(hostmacroid), 0) + 1 as hostmacroid from hostmacro) where table_name = 'hostmacro' and field_name = 'hostmacroid'"
                _zabbix_mysql_query "$stm" 1>/dev/null 2>/dev/null
                ret=$?
        fi

        return $ret
}
