diff --git a/Makefile b/Makefile index cadc4f4..c06c9ab 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ ZAF_EXPORT_OPTS=$(foreach o,$(ZAF_OPTIONS),$(shell echo $(o)|cut -d '=' -f 1)) DEBIAN_DIR=tmp/deb DEBIAN_CTRL=$(DEBIAN_DIR)/DEBIAN DEBIAN_PKG=$(shell . lib/zaf.lib.sh; echo out/zaf-$$ZAF_VERSION.deb) +ifeq ($(ZAF_DEBUG),) + ZAF_DEBUG=0 +endif ifeq ($(ZAF_OPTIONS),) ZAF_OPTIONS = ZAF_GIT=0 @@ -44,7 +47,8 @@ deb-control: for p in $(PLUGINS); do \ DEPENDS="$$DEPENDS,$$(zaf_ctrl_get_global_option $$p/control.zaf Depends-dpkg | tr ' ' ',')"; \ done; \ - zaf_far '{ZAF_VERSION}' "$$ZAF_VERSION" $(DEBIAN_CTRL)/control + [ "$$ZAF_GITBRANCH" = "master" ] && master=master; \ + zaf_far '{ZAF_VERSION}' "$${ZAF_VERSION}$$master" $(DEBIAN_CTRL)/control deb-scripts: @. lib/zaf.lib.sh; \ @@ -58,7 +62,7 @@ deb-scripts: deb-cp: @mkdir -p $(DEBIAN_DIR) - @set -e; INSTALL_PREFIX=$(DEBIAN_DIR) ZAF_DEBUG=0 ./install.sh auto $(ZAF_OPTIONS) $(AGENT_OPTIONS) + @set -e; INSTALL_PREFIX=$(DEBIAN_DIR) ZAF_DEBUG=$(ZAF_DEBUG) ./install.sh auto $(ZAF_OPTIONS) $(AGENT_OPTIONS) @. lib/zaf.lib.sh; \ . lib/ctrl.lib.sh; \ for p in $(PLUGINS); do \ diff --git a/files/postinst.template b/files/postinst.template index e5abaf1..6c95d4b 100644 --- a/files/postinst.template +++ b/files/postinst.template @@ -8,9 +8,10 @@ configure) . /usr/lib/zaf/os.lib.sh . /usr/lib/zaf/ctrl.lib.sh cd /usr/lib/zaf && /usr/lib/zaf/install.sh reconf - zaf reinstall {PLUGINS} {IPLUGINS} + [ -n "{PLUGINS}" ] && zaf reinstall {PLUGINS} {IPLUGINS} fi ;; esac +true diff --git a/install.sh b/install.sh index 369efb9..efb620c 100755 --- a/install.sh +++ b/install.sh @@ -3,7 +3,7 @@ [ -z "$ZAF_DEBUG" ] && ZAF_DEBUG=1 if [ -z "$ZAF_URL" ]; then # Runing as standalone install.sh. We have to download rest of files first. - [ -z "$ZAF_VERSION" ] && ZAF_VERSION=1.0 + [ -z "$ZAF_GITBRANCH" ] && ZAF_GITBRANCH=master ZAF_URL="https://github.com/limosek/zaf/" fi @@ -20,7 +20,7 @@ zaf_fetch_url(){ # Download tgz and extract to /tmp/zaf-installer zaf_download_files() { rm -rf /tmp/zaf-installer - zaf_fetch_url $ZAF_URL/archive/$ZAF_VERSION.tar.gz | tar -f - -C /tmp -zx && mv /tmp/zaf-$ZAF_VERSION /tmp/zaf-installer + zaf_fetch_url $ZAF_URL/archive/$ZAF_GITBRANCH.tar.gz | tar -f - -C /tmp -zx && mv /tmp/zaf-$ZAF_VERSION /tmp/zaf-installer } if ! [ -f README.md ]; then @@ -224,7 +224,12 @@ zaf_configure(){ zaf_get_option ZAF_AGENT_CONFIGD "Zabbix agent config.d" "/etc/zabbix/zabbix_agentd.conf.d/" "$INSTALL_MODE" zaf_get_option ZAF_AGENT_BIN "Zabbix agent binary" "/usr/sbin/zabbix_agentd" "$INSTALL_MODE" zaf_get_option ZAF_AGENT_RESTART "Zabbix agent restart cmd" "service zabbix-agent restart" "$INSTALL_MODE" - + zaf_get_option ZAF_CACHE_DIR "Cache directory" "${ZAF_TMP_BASE}c/" "$INSTALL_MODE" + zaf_get_option ZAF_ZBXAPI_URL "Zabbix API url" "http://localhost/zabbix/api_jsonrpc.php" "$INSTALL_MODE" + zaf_get_option ZAF_ZBXAPI_USER "Zabbix API user" "zaf" "$INSTALL_MODE" + zaf_get_option ZAF_ZBXAPI_PASS "Zabbix API password" "" "$INSTALL_MODE" + zaf_get_option ZAF_ZBXAPI_AUTHTYPE "Zabbix API authentication type" "internal" "$INSTALL_MODE" + if zaf_is_root && ! [ -x $ZAF_AGENT_BIN ]; then zaf_err "Zabbix agent ($ZAF_AGENT_BIN) not installed? Use ZAF_AGENT_BIN env variable to specify location. Exiting." fi @@ -251,6 +256,11 @@ zaf_configure(){ zaf_set_option ZAF_AGENT_CONFIGD "$ZAF_AGENT_CONFIGD" zaf_set_option ZAF_AGENT_BIN "$ZAF_AGENT_BIN" zaf_set_option ZAF_AGENT_RESTART "$ZAF_AGENT_RESTART" + zaf_set_option ZAF_CACHE_DIR "$ZAF_CACHE_DIR" + zaf_set_option ZAF_ZBXAPI_URL "$ZAF_ZBXAPI_URL" + zaf_set_option ZAF_ZBXAPI_USER "$ZAF_ZBXAPI_USER" + zaf_set_option ZAF_ZBXAPI_PASS "$ZAF_ZBXAPI_PASS" + zaf_set_option ZAF_ZBXAPI_AUTHTYPE "$ZAF_ZBXAPI_AUTHTYPE" [ -n "$ZAF_PREPACKAGED_DIR" ] && zaf_set_option ZAF_PREPACKAGED_DIR "$ZAF_PREPACKAGED_DIR" if zaf_is_root; then @@ -263,15 +273,15 @@ zaf_install_all() { rm -rif ${ZAF_TMP_DIR} mkdir -p ${ZAF_TMP_DIR} zaf_install_dir ${ZAF_LIB_DIR} - for i in lib/zaf.lib.sh lib/os.lib.sh lib/ctrl.lib.sh README.md; do - zaf_install $i ${ZAF_LIB_DIR}/ + for i in lib/zaf.lib.sh lib/os.lib.sh lib/ctrl.lib.sh lib/cache.lib.sh lib/zbxapi.lib.sh README.md; do + zaf_install $i ${ZAF_LIB_DIR}/ || zaf_err "Error installing $i" done for i in lib/zaflock lib/preload.sh; do - zaf_install_bin $i ${ZAF_LIB_DIR}/ + zaf_install_bin $i ${ZAF_LIB_DIR}/ || zaf_err "Error installing $i" done zaf_install_dir ${ZAF_BIN_DIR} for i in zaf; do - zaf_install_bin $i ${ZAF_BIN_DIR}/ + zaf_install_bin $i ${ZAF_BIN_DIR}/ || zaf_err "Error installing $i" done zaf_install_dir ${ZAF_PLUGINS_DIR} zaf_install_dir ${ZAF_PLUGINS_DIR} diff --git a/lib/cache.lib.sh b/lib/cache.lib.sh new file mode 100644 index 0000000..bc665f8 --- /dev/null +++ b/lib/cache.lib.sh @@ -0,0 +1,59 @@ +# Zaf cache related functions + +zaf_cache_init(){ + [ -n "$ZAF_CACHE_DIR" ] && rm -rf "$ZAF_CACHE_DIR" + mkdir -p "$ZAF_CACHE_DIR" +} + +# Get cache key from requested param +zaf_cachekey(){ + echo "$1" | md5sum - | cut -d ' ' -f 1 +} + +# Put object into cache +# $1 key +# $2 value +# $3 lifetime in seconds +zaf_tocache(){ + local key + local value + local lifetime + key=$(zaf_cachekey "$1") + echo "$2" >$ZAF_CACHE_DIR/$key + touch -m -d "$3 seconds" $ZAF_CACHE_DIR/$key.tme +} + +# Put object into cache from stdin and copy to stdout +# $1 key +# $2 lifetime in seconds +zaf_tocache_stdin(){ + local key + local lifetime + + key=$(zaf_cachekey "$1") + cat >$ZAF_CACHE_DIR/$key + touch -m -d "$3 seconds" $ZAF_CACHE_DIR/$key.tme + cat $ZAF_CACHE_DIR/$key +} + +# Get object from cache +# $1 key +zaf_fromcache(){ + local key + local value + key=$(zaf_cachekey "$1") + if [ -f $ZAF_CACHE_DIR/$key ]; then + if [ "$ZAF_CACHE_DIR/$key.tme" -nt "$ZAF_CACHE_DIR/$key" ]; then + zaf_trc "Cache: serving $1($key) from cache" + cat $ZAF_CACHE_DIR/$key + else + zaf_trc "Cache: removing old entry $1" + rm -f "$ZAF_CACHE_DIR/$key*" + return 2 + fi + else + zaf_trc "Cache: missing entry $1($key)" + return 1 + fi +} + diff --git a/lib/zaf.lib.sh b/lib/zaf.lib.sh index c45716b..acf7a4d 100644 --- a/lib/zaf.lib.sh +++ b/lib/zaf.lib.sh @@ -1,6 +1,7 @@ # Hardcoded variables -ZAF_VERSION="master" +ZAF_VERSION="1.1" +ZAF_GITBRANCH="master" ZAF_URL="https://github.com/limosek/zaf" ZAF_RAW_URL="https://raw.githubusercontent.com/limosek/zaf" @@ -9,6 +10,9 @@ ZAF_RAW_URL="https://raw.githubusercontent.com/limosek/zaf" zaf_msg() { echo $@ } +zaf_trc() { + [ "$ZAF_DEBUG" -ge "3" ] && logger -s -t zaf -- $@ +} zaf_dbg() { [ "$ZAF_DEBUG" -ge "2" ] && logger -s -t zaf -- $@ } @@ -32,7 +36,11 @@ zaf_fetch_url() { local scheme local uri local insecure + local out + if zaf_fromcache "$1"; then + return + fi scheme=$(echo $1|cut -d ':' -f 1) uri=$(echo $1|cut -d '/' -f 3-) if [ "$1" = "$scheme" ]; then @@ -43,7 +51,7 @@ zaf_fetch_url() { [ "$scheme" != "file" ] && [ -n "$ZAF_OFFLINE" ] && zaf_err "Cannot download $1 in offline mode!" [ "${ZAF_CURL_INSECURE}" = "1" ] && insecure="-k" zaf_dbg curl $insecure -f -s -L -o - $1 - curl $insecure -f -s -L -o - "$1" + curl $insecure -f -s -L -o - "$1" | zaf_tocache_stdin "$1" 120 ;; esac } @@ -238,8 +246,9 @@ zaf_install_plugin() { plugin=$(zaf_ctrl_get_global_block <"${ZAF_TMP_DIR}/plugin/control.zaf" | zaf_block_get_option Plugin) plugindir="${ZAF_PLUGINS_DIR}"/$plugin if [ -n "$plugin" ] && zaf_prepare_plugin "$1" $plugindir; then + zaf_wrn "Installing plugin $plugin from $url to $plugindir" control=${plugindir}/control.zaf - [ "$ZAF_DEBUG" -gt 0 ] && zaf_plugin_info "${control}" + [ "$ZAF_DEBUG" -gt 1 ] && zaf_plugin_info "${control}" zaf_ctrl_check_deps "${control}" zaf_ctrl_install "$url" "${control}" "${plugindir}" zaf_ctrl_generate_cfg "${control}" "${plugin}" \ diff --git a/lib/zbxapi.lib.sh b/lib/zbxapi.lib.sh old mode 100755 new mode 100644 index 52a53c8..be30bbb --- a/lib/zbxapi.lib.sh +++ b/lib/zbxapi.lib.sh @@ -1,4 +1,35 @@ -#!/bin/sh +# Call api function +# $1 - query string +zaf_zbxapi_do() { + local result + if ! zaf_fromcache "$1"; then + zaf_dbg "Zabbix API: $1" + result=$(curl -s -f -L -X POST -H 'Content-Type: application/json-rpc' -d "$1" "$ZAF_ZBXAPI_URL") + zaf_tocache "$1" "$result" 60 + echo $result + fi +} + +# Extract result from JSON response +zaf_zbxapi_getresult() { + sed -e 's/\({"jsonrpc":"2.0","result":\)\(.*\),\("id":.*\)/\2/g' | sed -e 's/^\[\]$//' +} + +# Extract XML result from JSON response +zaf_zbxapi_getxml() { + zaf_zbxapi_getresult | sed -e 's/{"jsonrpc":"2.0","result":"//' | sed -e 's/","id"\:1}//' | sed -e 's#\\\([<">/]\)#\1#g' +} + +# Extract string from JSON response result +zaf_zbxapi_getstring() { + sed -e 's/^"//' -e 's/"$//' -e 's/\\n/'\\n'/g' +} + +# Extract value from JSON response result +# $1 key +zaf_zbxapi_getvalue() { + tr ',' '\n' | grep "\"$1\":" | cut -d '"' -f 4 +} # Zabbix API related functions # Parameters in global variables ZAF_ZBX_API_* @@ -7,43 +38,124 @@ zaf_zbxapi_login(){ local authstr local user local pass + local result [ -z "$ZAF_ZBXAPI_URL" ] || [ -z "$ZAF_ZBXAPI_USER" ] || [ -z "$ZAF_ZBXAPI_PASS" ] && zaf_err "Zabbix Api parameters not set!" authstr='{ + "method" : "user.login", "params" : { "password" : "'$ZAF_ZBXAPI_PASS'", "user" : "'$ZAF_ZBXAPI_USER'" }, "id" : 0, - "jsonrpc" : "2.0", - "method" : "user.login" + "jsonrpc" : "2.0" }' - zaf_dbg "Zabbix API login: $authstr" - ZAF_ZBXAPI_AUTH=$(curl -s -f -L -X POST -H 'Content-Type: application/json-rpc' -d "$authstr" "$ZAF_ZBXAPI_URL" | json_pp | grep result | cut -d ':' -f 2 | tr -d '", ') || { zaf_err "Bad zabbix API parameters, cannot login."; } + + if [ "$ZAF_ZBXAPI_AUTHTYPE" = "http" ] ; then + ZAF_ZBXAPI_URL=$(echo $ZAF_ZBXAPI_URL | cut -d '/' -f 1)//$ZAF_ZBXAPI_USER:$ZAF_ZBXAPI_PASS@$(echo $ZAF_ZBXAPI_URL | cut -d '/' -f 3-) + fi + result=$(zaf_zbxapi_do "$authstr") + echo $result | grep -vq '"result":"' && zaf_err "Cannot login to API" + ZAF_ZBXAPI_AUTH=$(echo $result |zaf_zbxapi_getresult| zaf_zbxapi_getstring) zaf_dbg "Logged into zabbix API ($ZAF_ZBXAPI_AUTH)" } -zaf_zbxapi_getxml() { - sed -e 's/\({"jsonrpc":"2.0","result":\)"\(.*\)",\("id":.*\)/\n\2\n/g' | sed -r 's/\\([/"])/\1/g' +# $1 hostgroup name +zaf_zbxapi_gethostgroupid() { + local hstr + local filter + local gfilter + + hstr='{ + "method": "hostgroup.get", + "jsonrpc": "2.0", + "auth": "'$ZAF_ZBXAPI_AUTH'", + "params": { + "filter": { + "name": ["'$1'"] + }, + "output": "shorten" + }, + "id": 1 + }' + zaf_zbxapi_do "$hstr" | zaf_zbxapi_getresult | tr ',' '\n' | cut -d '"' -f 4 } -# $1 host group or empty -zaf_zbxapi_gethosts() { +# $1 hostname +zaf_zbxapi_gethostid() { local hstr - local hgroup + local host + local groupid local filter + local gfilter - hgroup="$1" - [ -n "$hgroup" ] && filter='"filter": { "hostgroup": [ "'$hgroup'" ] },' + host="$1" + if [ -n "$host" ] ; then + filter='"filter": { "host": [ "'$host'" ] },' + fi hstr='{ - "jsonrpc": "2.0", "method": "host.get", + "jsonrpc": "2.0", "auth": "'$ZAF_ZBXAPI_AUTH'", - '$filter' - "id": 2 + "params": { + '$filter' + "output": "shorten" + }, + "id": 1 }' - zaf_dbg "Zabbix Get hosts: $hstr" - curl -s -f -L -X POST -H 'Content-Type: application/json-rpc' -d "$hstr" "$ZAF_ZBXAPI_URL" | tr ',' '\n' | grep hostid | cut -d '"' -f 4 + zaf_zbxapi_do "$hstr" | zaf_zbxapi_getresult | tr ',' '\n' | cut -d '"' -f 4 +} + +# $1 hostid +zaf_zbxapi_gethost() { + local hstr + local host + local groupid + local filter + local gfilter + + hostid="$1" + if [ -n "$hostid" ] ; then + filter='"hostids": [ "'$hostid'" ],' + fi + hstr='{ + "method": "host.get", + "jsonrpc": "2.0", + "auth": "'$ZAF_ZBXAPI_AUTH'", + "params": { + '$filter' + "output": "extend" + }, + "id": 1 + }' + zaf_zbxapi_do "$hstr" | zaf_zbxapi_getresult | zaf_zbxapi_getvalue host +} + +# $1 hostgroupid +zaf_zbxapi_gethostsingroup() { + local hstr + local host + local groupid + local filter + local gfilter + + groupid="$1" + if [ -n "$groupid" ]; then + gfilter='"groupids": [ "'$groupid'" ],' + fi + + hstr='{ + "method": "host.get", + "jsonrpc": "2.0", + "auth": "'$ZAF_ZBXAPI_AUTH'", + "params": { + '$gfilter' + '$filter' + "output": "shorten" + }, + "id": 1 + }' + zaf_zbxapi_do "$hstr" | zaf_zbxapi_getresult | tr ',' '\n' | cut -d '"' -f 4 } # Host backup @@ -54,8 +166,8 @@ zaf_zbxapi_backuphost(){ host="$1" bkpstr=' { - "jsonrpc": "2.0", "method": "configuration.export", + "jsonrpc": "2.0", "params": { "options": { "hosts": [ @@ -67,8 +179,7 @@ zaf_zbxapi_backuphost(){ "auth": "'$ZAF_ZBXAPI_AUTH'", "id": 1 }' - zaf_dbg "Zabbix API backup host: $bkpstr" - curl -s -f -L -X POST -H 'Content-Type: application/json-rpc' -d "$bkpstr" "$ZAF_ZBXAPI_URL" | zaf_zbxapi_getxml + zaf_zbxapi_do "$bkpstr" | zaf_zbxapi_getxml } diff --git a/zaf b/zaf index 6c70dda..3ea1a22 100755 --- a/zaf +++ b/zaf @@ -27,16 +27,21 @@ if [ "$ZAF_DEBUG" -le 3 ]; then fi ! [ -d "${ZAF_TMP_DIR}" ] && mkdir "${ZAF_TMP_DIR}" +! [ -d "${ZAF_CACHE_DIR}" ] && mkdir "${ZAF_CACHE_DIR}" if [ -f ./lib/zaf.lib.sh ]; then . ./lib/zaf.lib.sh . ./lib/os.lib.sh . ./lib/ctrl.lib.sh +. ./lib/cache.lib.sh +. ./lib/zbxapi.lib.sh [ -f ./lib/zaf.${ZAF_OS}.sh ] && . ./lib/zaf.${ZAF_OS}.sh else . ${ZAF_LIB_DIR}/zaf.lib.sh . ${ZAF_LIB_DIR}/os.lib.sh . ${ZAF_LIB_DIR}/ctrl.lib.sh +. ${ZAF_LIB_DIR}/cache.lib.sh +. ${ZAF_LIB_DIR}/zbxapi.lib.sh [ -f ${ZAF_LIB_DIR}/zaf.${ZAF_OS}.sh ] && . ${ZAF_LIB_DIR}/zaf.${ZAF_OS}.sh fi @@ -70,9 +75,7 @@ update) zaf_update_repo ;; upgrade) - zaf_list_plugins | while read plugin; do - ZAF_DEBUG=$ZAF_DEBUG $0 reinstall $plugin || zaf_err "Error upgrading $plugin" - done + ZAF_DEBUG=$ZAF_DEBUG $0 reinstall $(zaf_list_plugins) ;; show) shift; @@ -219,7 +222,52 @@ self-remove) echo echo "To continue, please do $0 self-remove force" fi - ;; + ;; + +api) + zaf_zbxapi_login + case $2 in + hostid) + zaf_zbxapi_gethostid "$3" + ;; + host) + zaf_zbxapi_gethost "$3" + ;; + hostgroupid) + zaf_zbxapi_gethostgroupid "$3" + ;; + hosts) + gid=$(zaf_zbxapi_gethostgroupid "$3") + zaf_zbxapi_gethostsingroup $gid + ;; + backup-group) + gid=$(zaf_zbxapi_gethostgroupid "$3") + hosts=$(zaf_zbxapi_gethostsingroup $gid) + zaf_wrn "Will backup this hosts: $hosts" + for h in $hosts; do + hn=$(zaf_zbxapi_gethost $h) + zaf_wrn "Exporting host $hn($h)..." + zaf_zbxapi_backuphost $h >$hn.xml + done + ;; + backup-host) + hostid=$(zaf_zbxapi_gethostid "$3") + zaf_wrn "Exporting host $3($hostid)..." + zaf_zbxapi_backuphost $hostid + ;; + *) + echo "$0 api command [parameters]" + echo "hostid 'host' Get hostid from hostname" + echo "host 'hostid' Get hostname from hostid" + echo "hostgroupid 'hostgroup' Get hostgroup id from hostgroup" + echo "hosts 'hostgroup' Get hosts in group" + echo "backup-group 'hostgroup' [dir] Backup all hosts in group (get their config from zabbix and save to dir/hostname.xml)" + echo "backup-host 'host' Backup host (get config from zabbix to stdout)" + echo + exit + ;; + esac + ;; *) echo "$0 Version ${ZAF_VERSION}. Please use some of this commands:" @@ -234,6 +282,7 @@ self-remove) echo "$0 get [plugin[.item]] To test [all] suported items by zabbix_get [for plugin]" echo "$0 install plugin To install plugin" echo "$0 remove plugin To remove plugin" + echo "$0 api To zabbix API functions. See $0 api for more info." echo "$0 self-upgrade To self-upgrade zaf" echo "$0 self-remove To self-remove zaf and its config" echo