Select Git revision
check_file_age
director-cli.sh 23.93 KiB
#!/bin/bash
# ======================================================================
#
# director helper
#
# C
# R
# U
# D
# actions for a host, commands, services
#
# This script contains specific logic for IML
# - use puppet facts
# - UniBe network and group names - see hostCreate()
#
# ----------------------------------------------------------------------
# ah = axel.hahn@iml.unibe.ch
# 2022-02-16 v0.2 ah add --cfg param
# 2022-03-04 v0.3 ah abort on http 5xx error
# ======================================================================
tmpfile=/tmp/outcurl.tmp
tmpfile2=/tmp/outcurl2.tmp
# a custom file to source instead of detecting local data
loadfile=
# UNUSED
# APICLIENT=`dirname $0`/api2director
# MY_NAME=`facter fqdn | cut -f -2 -d ">"`
# MY_IP=`facter ipaddress | cut -f -2 -d ">"`
# TODO: test ... maybe uncomment it again
# MY_NAME=`hostname -f`
# MY_IP=`_getIpFrontend`
typeset cfg_debug=false
typeset cfg_dryrun=false
# ======================================================================
#
# FUNCTIONS
#
# ======================================================================
# helper to make http base setup for host actions
function _initHttp(){
# see inc_functions
_initHttpWithConfigfile "${dir_cfg}/api-director.cfg"
if [ $cfg_debug = true ]; then
http.setDebug 1
fi
}
function _initVars(){
if [ -z "$loadfile" ]; then
MY_NAME=`hostname -f`
MY_IP=`_getIpFrontend | head -1`
MY_ZONE=
fi
IDC_host__cachefile="${dir_data}/${MY_NAME}__host_at-director.txt"
IDC_service__cachefile="${dir_data}/${MY_NAME}__all_defined_services__at-director.txt"
IDC_svcathost__cachefile="${dir_data}/${MY_NAME}__services_on_host__at-director.txt"
# ch="`dirname $0`/inc/confighandler.sh"
ch="./inc/confighandler.sh"
}
function flushDatadir(){
if [ ! -z "${dir_data}" -a -d "${dir_data}" ]; then
_wd "deleting ${dir_data} ..."
rm -f ${dir_data}/*.txt
fi
}
# ----------------------------------------------------------------------
# functions for objects
# ----------------------------------------------------------------------
# ............................................................
# set $ch to store all object vars
function _generateJsonForHost(){
if [ -z "$loadfile" ]; then
# --- host infos
local MY_Platform=`uname -a | cut -f 1 -d ' '`
. `dirname $0`/plugins/inc_pluginfunctions || exit 1
local MY_OSName=`ph.getOS`
local MY_OSMajorVersion=`ph.getOSMajor`
fi
export CFGSTORAGE="directorhost"
(
$ch --flush
$ch --set object_name \"$MY_NAME\"
$ch --set object_type '"object"'
$ch --set address \"$MY_IP\"
$ch --set display_name \"$MY_NAME\"
$ch --set zone \"$MY_ZONE\"
$ch --set icon_image \"/images/os/${MY_OSName}.png\"
$ch --set icon_image_alt \"${MY_Platform}\:\ ${MY_OSName}\ ${MY_OSMajorVersion}\"
) 2>/dev/null
# --- detect phase
# local phase="live"
# for myphase in preview stage demo
# do
# echo $MY_NAME | grep "\.$myphase\." >/dev/null && phase=$myphase
# done
(
# ----- facter data to host vars
$ch --set vars.platform \"${MY_Platform}\"
$ch --set vars.os \"${MY_OSName}${MY_OSMajorVersion}\"
# ----- set host type
# host in the UNIBE network:
echo $MY_IP | grep -E "^(10\.|172\.1[6-9]\.|172.2[0-9]\.|172\.3[01]\.|192.168\.)" >/dev/null
if [ $? -eq 0 ]; then
$ch --set imports '["host passive only"]'
else
$ch --set imports '["host in network"]'
# port checks initiated by icinga server to monitor client
if [ ! -z "${host_vars_tcpport}" -a "${host_vars_tcpport}" != "[]" ]; then
host_vars_tcpport=`echo ${host_vars_tcpport} | sed "s# ##g"`
$ch --set vars.tcp_port ${host_vars_tcpport}
fi
fi
# ----- /host type
# ----- host groups
# all host groups must exist in director - otherwise the creation
# of a host will fail
# see https://icinga.one.iml.unibe.ch/icingaweb2/director/dashboard?name=hosts#!/icingaweb2/director/hostgroups
$ch --set groups '["iml", "iml-server"]'
# ... and add some others
# $ch --add groups \"iml-phase-$phase\"
) 2>/dev/null
# ----- generate data and send to DIRECTOR
# DATA=`$ch --json 2>/dev/null`
# $ch --flush 2>/dev/null
}
# ............................................................
# set $ch to store all object vars
# UNUSED see response of GET director/commands/templates
# function _generateJsonForCommand(){
# export CFGSTORAGE="command-${IDC_command__obj_name}"
# (
# $ch --flush
# $ch --set object_name "\"${IDC_command__obj_name}\""
# $ch --set object_type \"template\"
#
# ) 2>/dev/null
# }
# ............................................................
# set $ch to store all object vars
function _generateJsonForServicetemplate(){
export CFGSTORAGE="service-${IDC_service__obj_name}"
(
$ch --flush
$ch --set object_name "\"${IDC_service__obj_name}\""
$ch --set check_command \"${IDC_command__obj_name}\"
$ch --set object_type \"template\"
$ch --set enable_active_checks false
$ch --set enable_passive_checks true
$ch --set check_interval "\"${checkInterval}s\"" # see _parseCheckConfig FILE
# graphite
# $ch --set enable_perfdata true
# $ch --set vars.check_command \"${checkName}\" # for graphite plugin
if [ ! -z "${checkIcon}" ]; then
$ch --set icon_image \"${checkIcon}\"
$ch --set icon_image_alt \"${checkName}\"
fi
if [ ! -z "${checkMaxAttempts}" ]; then
$ch --set max_check_attempts \"${checkMaxAttempts}\"
fi
) 2>/dev/null
}
# ............................................................
# set $ch to store all object vars
function _generateJsonForSvclink(){
export CFGSTORAGE="servicelink-${IDC_svcathost__obj_name}"
(
$ch --flush
$ch --set object_name "\"${IDC_svcathost__obj_name}\""
$ch --set object_type \"object\"
$ch --set host "\"${MY_NAME}\""
$ch --set imports "[ \"${IDC_service__obj_name}\" ]"
) 2>/dev/null
}
# ............................................................
# CRUD actions for an director object: host, service template, linked service
# examples:
# ObjAction create host
# ObjAction list svclink
#
# param string name of action; one of create|read|update|delete|exists|list
# param string name of object; one of host|service|svclink
# param string Dryrun (set any not empty value to show infos without execution)
function ObjAction(){
local _paramAction=$1
local _paramObj=$2
local _paramDryrun=$cfg_dryrun
test -z "$3" || _paramDryrun=true
local _object_name= # name of the current object (for ouput only)
local _sMethod= # http method; GET|POST|PUT|DELETE
local _sUrl= # relative url (part behind REST API base url)
local _jsondata=
local _bNeedsCheck=false # requires $IDC* vars (a read check config first)
local _bSendData=false # true for PUT and POST
local _sUrlList=
local _sUrlCreate=
local _sUrlExists=
local _sUrlRead=
local _sUrlUpdate=
local _sUrlDelete=
local _existFilter=
local _bShowResponse=true
local _bShowFilter=false
# local _sCachefile=
_initHttp
# --- init object based vars
case "${_paramObj}" in
'host')
_object_name=$MY_NAME
_sUrlCreate=director/host
_sUrlExists=director/host?name=${_object_name}
_sUrlRead=director/host?name=${_object_name}
_sUrlUpdate=director/host?name=${_object_name}
_sUrlDelete=director/host?name=${_object_name}
_existFilter="object_name"
_generateJsonForHost
;;
'service')
_object_name=${IDC_service__obj_name}
_bNeedsCheck=true
_sUrlList=director/services/templates
_sUrlCreate=director/service
_sUrlExists=director/service?name=${_object_name}
_sUrlRead=director/service?name=${_object_name}
_sUrlUpdate=director/service?name=${_object_name}
_sUrlDelete=director/service?name=${_object_name}
_existFilter='"object_name": '
_generateJsonForServicetemplate
;;
'svclink')
_object_name=${IDC_svcathost__obj_name}
_bNeedsCheck=true
_sUrlList=director/services?host=$MY_NAME
_sUrlCreate=director/service
_sUrlExists="director/service?name=${IDC_svcathost__obj_name}&host=$MY_NAME"
_sUrlRead="director/service?name=${IDC_svcathost__obj_name}&host=$MY_NAME"
_sUrlUpdate="director/service?name=${IDC_svcathost__obj_name}&host=$MY_NAME"
_sUrlDelete="director/service?name=${IDC_svcathost__obj_name}&host=$MY_NAME"
# exists:
# _existFilter="object_name.*${IDC_svcathost__obj_name}"
_existFilter='"object_name": '
_generateJsonForSvclink
;;
*)
echo "ERROR: object [${_paramObj}] cannot be handled in ${FUNCNAME[0]} (yet?)."
exit 1
esac
# --- init method based vars
case "${_paramAction}" in
'list')
_sMethod=GET
_sUrl=$_sUrlList
_bShowResponse=false
_bShowFilter=true
_bNeedsCheck=false #
;;
'exists')
_sMethod=GET
_sUrl=$_sUrlExists
_bShowResponse=false
;;
'create')
_sMethod=PUT
_sUrl=$_sUrlCreate
_bSendData=true
;;
'read')
_sMethod=GET
_sUrl=$_sUrlRead
;;
'update')
_sMethod=POST
_sUrl=$_sUrlUpdate
_bSendData=true
;;
'delete')
_sMethod=DELETE
_sUrl=$_sUrlDelete
;;
*)
echo "ERROR: method [${_paramAction}] does not exist."
exit 1
esac
if [ -z "$_sUrl" ]; then
echo "SKIP: Action [${_paramAction}] is not supported (yet?) for object type [${_paramObj}]"
return
fi
if [ $_bNeedsCheck = true -a -z "${IDC_svcathost__obj_name}" ]; then
echo "SKIP: you need to load a check before accessing [${_paramObj}]"
return
fi
# --- get json data of object
if [ $_bSendData = true ]; then
_jsondata=`$ch --json 2>/dev/null`
fi
$ch --flush 2>/dev/null
# --- http request
if [ ${_paramDryrun} = false ]; then
_wd ">>>>> $_paramAction $_paramObj [${_object_name}] >> $_sMethod $_sUrl $_jsondata"
http.makeRequest "$_sMethod" "$_sUrl" "$_jsondata"
if http.isServerError >/dev/null; then
echo "CRITICAL ERROR: Director API request failed with a server error $_sMethod $_sUrl"
exit 1
fi
if [ $_bShowResponse = true ]; then
http.getResponseHeader
http.getResponse
fi
http.isOk >/dev/null
else
echo "DRYRUN: >>>>> $_paramAction $_paramObj [${_object_name}] >> $_sMethod $_sUrl $_jsondata"
echo "... _bShowResponse: $_bShowResponse"
echo "... _bSendData : $_bSendData"
fi
# --- on list action: filter response
if [ "${_paramAction}" = "list" ]; then
if [ ${_paramDryrun} = false ]; then
_wd ">>>>> filter response by [object_name]"
if [ $_bShowFilter = true ]; then
http.getResponse | grep object_name | cut -f 2- -d ":" | sed 's#^ "##g' | sed 's#",$##'
else
http.getResponse | grep object_name | cut -f 2- -d ":" | sed 's#^ "##g' | sed 's#",$##' >/dev/null
fi
else
echo "DRYRUN: >>>>> filter response by [object_name]"
echo
fi
fi
# --- on exist action: filter response
if [ "${_paramAction}" = "exists" -a ! -z "${_existFilter}" ]; then
if [ ${_paramDryrun} = false ]; then
_wd ">>>>> filter response by [$_existFilter]"
if [ $_bShowFilter = true ]; then
http.getResponse | grep $_existFilter
else
http.getResponse | grep $_existFilter >/dev/null
fi
else
echo "DRYRUN: >>>>> filter response by [$_existFilter]"
echo
fi
fi
}
# ----------------------------------------------------------------------
# functions for Host
# ----------------------------------------------------------------------
# ............................................................
# helper to create a base config for the current host
# UNUSED
function UNUSED_initHostdata(){
export CFGSTORAGE="directorhost"
(
$ch --flush
$ch --set object_name \"$MY_NAME\"
$ch --set object_type '"object"'
$ch --set address \"$MY_IP\"
$ch --set display_name \"$MY_NAME\"
) 2>/dev/null
}
# ............................................................
# create a host with PUT on director API
function hostCreate(){
_h2 "create host"
ObjAction create host
if [ -z "`http.isOk`" ]; then
echo "ERROR, host was NOT created."
else
echo "OK, host was created successfully."
fi
}
# ............................................................
# get data of current host from director API
# and by the way it updates the local host infos too.
function hostRead(){
_h2 "read host"
ObjAction read host
if [ -z "`http.isOk`" ]; then
echo "ERROR, host was NOT found."
fi
}
# ............................................................
# update current host
# param JSON part starting with ", " and some json data
function hostUpdate(){
_h2 "update host - set $1 $2"
ObjAction update host
if [ -z "`http.isOk`" ]; then
case `http.getStatuscode` in
"304")
echo "OK, no update"
;;
*)
echo "ERROR during update of the host data"
esac
else
echo "OK, host was updated"
fi
}
# ............................................................
# ensure that a host exists
function hostCreateOrUpdate(){
ObjAction exists host
if [ $? -ne 0 ]; then
hostCreate
else
hostUpdate
fi
}
# ............................................................
# delete the current host in the director
function hostDelete(){
_h2 "delete host"
ObjAction delete host
case `http.getStatuscode` in
"200")
echo "OK, host was deleted"
;;
"404")
echo "ERROR, host does not exist"
;;
*)
echo "ERROR, unable to delete host"
esac
flushDatadir
}
# ----------------------------------------------------------------------
# functions for services
# director/service
# ----------------------------------------------------------------------
# ............................................................
# helper for services: generate variable names for a check
# vars have prefix IDC for "Icinga Director"
# uses global var checkName
function _generateVarsByCheckname(){
if [ -z "${checkName}" ]; then
echo ERROR: checkName is empty - I guess _parseCheckConfig was not executed.
exit 1
fi
IDC_command__obj_name="${checkName}"
IDC_service__obj_name="service-template_for_command_${checkName}"
IDC_svcathost__obj_name="`_getName4Svcathost ${checkName}`"
# IDC_service__obj_name="${checkName}"
# IDC_svcathost__obj_name="${checkName}"
IDC_command__cachefile="${dir_data}/command_${checkName}__at-director.txt"
# IDC_service__cachefile="${dir_data}/all_defined_services__at-director.txt"
# IDC_svcathost__cachefile="${dir_data}/all_services_on_host__at-director.txt"
}
# ............................................................
# create a service in Icinga director
# uses global variables only
function serviceCreateOrUpdate(){
_h2 "${FUNCNAME[0]}()"
local sMode=update
local sMethod=POST
local _sUrl="director/service?name=${IDC_service__obj_name}"
ObjAction exists service
if [ $? -ne 0 ]; then
ObjAction create service
else
ObjAction update service
fi
if [ $? -ne 0 ]; then
echo "ERROR :/"
else
echo "OK"
fi
}
# ............................................................
# create a service in Icinga director
# param string filename of check config
function serviceCreateByCfgFile(){
_h2 "${FUNCNAME[0]}($1) - create single service of given file"
_parseCheckConfig "${1}"
_generateVarsByCheckname "${checkName}"
# create command if it does not exist
serviceCreateOrUpdate
ObjAction exists service
if [ $? -eq 0 ]; then
ObjAction exists svclink
if [ $? -ne 0 ]; then
ObjAction create svclink
else
echo "SKIP: linked service on host [${IDC_svcathost__obj_name}] exists"
# TODO, uncomment -- wenn es sinnvolle Features gibt
# ObjAction update svclink
fi
else
echo "SKIP link ... service template for ${checkName} not ready."
fi
}
# ............................................................
# create all functions; this functon is called with cli parameter
# no params
function servicesCreateAll(){
_h2 "${FUNCNAME[0]}() - create all services"
# loop over all services and create
for mycheckfile in `getChecks`
do
serviceCreateByCfgFile "${mycheckfile}"
echo
done
}
# ............................................................
# cleanup services - delete unneded links
function svclinkCleanup(){
_h2 "Cleanup linked service templates"
tmpRemote=/tmp/remoteLinks
tmpLocal=/tmp/localChecks
# --- perpare I: create file with remote service template links
ObjAction list svclink >$tmpRemote
# --- perpare II: create file with local configs and object names for its link
rm -f $tmpLocal 2>/dev/null
for mycheckfile in `getChecks`
do
_parseCheckConfig "${mycheckfile}"
_generateVarsByCheckname "${checkName}"
echo "$mycheckfile:${IDC_svcathost__obj_name}" >>$tmpLocal
done
# _h3 "local checks"
# cat $tmpLocal
# _h3 "remote linked service templates"
# ObjAction list svclink
# --- Compare ...
# _h3 "Compare"
cat $tmpRemote | while read remoteLink
do
grep $remoteLink $tmpLocal >/dev/null
if [ $? -eq 0 ]; then
echo "OK: $remoteLink"
else
echo "DELETE: link [$remoteLink] is not used by any local check anymore."
checkName=`_getName4Svcathost $remoteLink reverse`
_generateVarsByCheckname "${checkName}"
ObjAction delete svclink
fi
done
rm -f $tmpLocal $tmpRemote
echo --- done.
}
# ----------------------------------------------------------------------
# functions for Director
# ----------------------------------------------------------------------
# ............................................................
# kick the director to deploy config changes now
function directorDeploy(){
_h2 deploy
_initHttp
_wd POST director/config/deploy
_APIcall POST director/config/deploy
if [ -z "`http.isOk`" ]; then
echo "ERROR deploy config was not queued."
else
echo "OK, deploy was triggered"
fi
}
# ..................................................................
#
# show a help
function showHelp(){
script=`basename $0`
cat <<ENDOFHELP
HELP:
Host actions
--hc
--hostcreate
Create the host [$MY_NAME] in the icinga director
--hr
--hostread
Read the host information of [$MY_NAME] in the icinga director
--hu
--hostupdate
Update host in the Icinga director
--he
--hostensure
Create host if it does not exist otherwise update it in the
Icinga director
--hd
--hostdelete
Delete [$MY_NAME] in the icinga director
Check actions
--listchecks
list all local config files of known checks for [$MY_NAME]
--linkcleanup
verify added service templates on [$MY_NAME] with locally
defined checks and remove unneeded items.
--sca
--servicescreateall
Loop over defined checks and add all checks to the host.
See also:
--listchecks to get a list of config files.
--sc CFGFILE
--servicecreate CFGFILE
create service by naming a config file
See also:
--listchecks to get a list of config files.
Cache
--flushcache
remove files in [$dir_data]
Director actions
--deploy
icinga update (deploy director data)
Other parameters
--cfg CONFIGFILE
load a costom config file; default: ./inc_getconfig.sh
This must be the 1st parameter to be processed.
--debug
enable debug output.
--nodebug
disable debug output.
--dryrun
enable dryrun - it shows actions without execution.
EXAMPLES
# $script --dryrun --he
Show actions and api calls for "--he" parameter (create or update host)
ENDOFHELP
}
# ------------------------------------------------------------
#
# MAIN
#
# ------------------------------------------------------------
echo
echo "##### DIRECTOR HELPER $MY_NAME - $MY_IP"
echo
if [ "$1" = "--cfg" ] && [ -n "$2" ]; then
echo "INFO: loading custom config [$2]..."
. "${2}"
shift 2
else
. "$( dirname $0 )/inc_getconfig.sh"
fi
. `dirname $0`/inc_functions.sh
. `dirname $0`/inc/rest-api-client.sh
if [ $# -eq 0 ]; then
showHelp
exit 0
fi
cd `dirname $0`
ls ./`basename $0` >/dev/null || exit 1
_initVars
# ensure that ./inc_getconfig.sh was loaded
if [ -z "${dir_cfg}" ]; then
echo ERROR: Client is not installed/ configured yet on this machine.
exit 1
fi
while [ $# -gt 0 ];
do
case "$1" in
'--help' | '-h' | '-?')
showHelp
exit 0
;;
'--debug')
cfg_debug=true
;;
'--nodebug')
cfg_debug=false
;;
'--dryrun')
cfg_dryrun=true
;;
# ----- override local data with those from a file
'--load')
if [ ! -f "$2" ]; then
echo ERROR: file "$2" is not readable.
echo Hint: ist must be an absolute path or relative to $( pwd )
exit 1
fi
loadfile="$2"
. "${loadfile}"
shift
echo "loaded ${loadfile}"
;;
# ----- host actions
'--he' | '--hostensure')
hostCreateOrUpdate
;;
'--hc' | '--hostcreate')
hostCreate
;;
'--hr' | '--hostread')
hostRead
;;
'--hu' | '--hostupdate')
hostUpdate
;;
'--hd' | '--hostdelete')
hostDelete
;;
'--hs' | '--hostshow')
_generateJsonForHost
$ch --json 2>/dev/null
;;
# ----- check actions
'--listchecks')
getChecks
;;
'--sca' | '--servicescreateall')
servicesCreateAll
;;
'--sc' | '--servicecreate')
serviceCreateByCfgFile "${2}"
shift
;;
'--linkcleanup')
svclinkCleanup
shift
;;
'--flushcache')
flushDatadir
;;
# ----- director
'--deploy')
directorDeploy
exit 0
;;
'--testrun')
_h1 "svclink - without a config"
ObjAction "create" "svclink" dry
ObjAction "exists" "svclink" dry
ObjAction "read" "svclink" dry
ObjAction "update" "svclink" dry
ObjAction "delete" "svclink" dry
_h1 "svclink - with reading a config"
_parseCheckConfig "/etc/icinga2-passive-client/checks/CPU-usage"
_generateVarsByCheckname "${checkName}"
ObjAction "create" "svclink" dry
ObjAction "exists" "svclink" dry
ObjAction "read" "svclink" dry
ObjAction "update" "svclink" dry
ObjAction "delete" "svclink" dry
_h1 "host"
ObjAction "create" "host" dry
ObjAction "exists" "host" dry
ObjAction "read" "host" dry
ObjAction "update" "host" dry
ObjAction "delete" "host" dry
;;
# ---- ab hier TODO
u)
shift 1
hostUpdate "$*"
exit 0
;;
*)
echo "ERROR: unknown parameter detected. No idea what to do with [$1]."
echo "Exiting..."
exit 2
esac
shift 1
done
exit 0
# ======================================================================