domingo, 9 de mayo de 2010

Añadir auditoría en los scripts de FreeNAS

En el artículo previo del presente blog "Automatización copiado interno en FreeNAS" podíamos ver un ejemplo de uso de scripting para la centralización de contenido multimedia. Vamos a dar un paso mas, creando una librería de funciones, y para empezar:

"Registro de auditoria (logs) centralizado"

Los pasos que debemos seguir son:
  1. Creación (o selección) de un directorio del disco duro o almacenamiento permanente, p.ej. /mnt/cfinterno/scripts/
  2. Creación de una entrada en WebGUI -> Sistema -> Avanzado -> rc.conf llamada usr_path y que apuntará al directorio creado en el paso anterior.
  3. Creación de un subdirectorio inc en el directorio creado en los pasos anteriores (p.ej. /mnt/cfinterno/scripts/inc/)
  4. Creación de un fichero llamado log.sh y almacenado en el directorio del paso anterior, con el contenido siguiente:


#***************************************************************************************************
#inc/log.sh
#Version: 0.1
#Author: Serg Rogovtsev (freenas@hawke.ru)
#Confirmed to work on: FreeNAS 0.7RC1 i386 embedded
#***************************************************************************************************
#
#Please send your working configurations/platforms to freenas@hawke.ru
#
#Is expected (altough not *fully* tested yet) to be safe to run from CRON:
#all paths are absolute, so we don't expect anything to be in out PATH
#
#Note: most "reasonable" console configuration is default, which puts all logging messages to STDERR.
#This way you could echo needed data from sub-shells and still don't mix it with log messages
#If you use H_LOG_USE_STDOUT you may end up with "VERBOSE" and "NORMAL" messages mixing up with your output.
#
#***************************************************************************************************

#Don't include twice
test -n "$H_LOG_INCLUDED" && return;
readonly H_LOG_INCLUDED="Y";

#***************************************************************************************************
#What to use as log source (used in full-line formats and in syslog). Default - script filename without .sh extension
H_LOG_SOURCE="${H_LOG_SOURCE:-`/usr/bin/basename -s \".sh\" \"$0\"`}"
#"Y" will put console log ouput to STDOUT instead of STDERR
H_LOG_USE_STDOUT=${H_LOG_USE_STDOUT:-N}
#"Y" will allow output of "verbose" (0) level messages (to all media); will also force output of normal (1) messages if not console is connected
H_LOG_VERBOSE=${H_LOG_VERBOSE:-N}
#"Y" will suspend ALL console log output
H_LOG_QUIET=${H_LOG_QUIET:-N}
#"Y" will enable syslogd logging, "N" will disable it
H_LOG_SYSLOG_ENABLED=${H_LOG_SYSLOG_ENABLED:-Y}
#Non-empty will be treated as a path to additional log for all messages.
#Useful in rc.conf when you don't have a permanent syslog or just want to have a separate global log for script actions.
H_LOG_GLOBAL_FILE_PATH=${H_LOG_GLOBAL_FILE_PATH:-}
#Non-empaty will be treated as a path to additional log.
#Expected to be set in script when file log is requested
H_LOG_FILE_PATH=${H_LOG_FILE_PATH:-}
#Sets date format for file and console-full logging
#see man date(1)
H_LOG_DATE_FORMAT="${H_LOG_DATE_FORMAT:-%b %e %T}"
#"Y" will enable full-line console logging. Default is "N" (short)
H_LOG_CONSOLE_FULL="${H_LOG_CONSOLE_FULL:-N}"
#***************************************************************************************************

#returns stream id depending on H_LOG_USE_STDOUT value
#Arguments: none
#Output: none
#Return: stream id
h_log_get_console()
{
if [ "$H_LOG_USE_STDOUT" = "Y" ]; then
echo 1;
else
echo 2;
fi
}

#Formats message for "full" logging style. Used in file logging and console logging
#Arguments: priority, message
#Output: formatted line
#Return: error code
h_log_format_message()
{
local date="`/bin/date \"+$H_LOG_DATE_FORMAT\"`";
local source="$H_LOG_SOURCE";
local priority="$1";
local message="$2";

/usr/bin/printf '%s %-8s %s: %s\n' "$date" "$priority" "$source" "$message";
}

#Logs message to console depending on settings. Will not log if H_LOG_QUIET=Y. Message format depends on H_LOG_CONSOLE_FULL.
#Arguments: priority, message
#Output: formatted message on STDERR or STDOUT (depending on H_LOG_USE_STDOUT and H_LOG_QUIET)
#Return: error code
h_log_console()
{
test "$H_LOG_QUIET" = "Y" && return
local n=`h_log_get_console`
local msg;
if [ "$H_LOG_CONSOLE_FULL" = "Y" ]; then
msg="`h_log_format_message \"$1\" \"$2\"`";
else
msg="$2";
fi

echo "$msg" >&$n
}

#Logs message to file
#Arguments: priority, message, logfilepath
#Output: none
#Return: error code
h_log_file()
{
h_log_format_message "$1" "$2" >> "$3"
}

#Logs message to syslog
#Arguments: priority, message
#Output: none
#Return: error code
h_log_syslog()
{
/usr/bin/logger -p "user.$1" -t "$H_LOG_SOURCE" "$2"
}

#Logs message to all enabled permanent (non-console) outputs
#Arguments: priority, message
#Output: none
#Return: always 0
h_log_perm()
{
test "$H_LOG_SYSLOG_ENABLED" = "Y" && h_log_syslog "$1" "$2"
test -n "$H_LOG_GLOBAL_FILE_PATH" && h_log_file "$1" "$2" "$H_LOG_GLOBAL_FILE_PATH"
test -n "$H_LOG_FILE_PATH" && h_log_file "$1" "$2" "$H_LOG_FILE_PATH"
return 0
}

#Logs message acccording to settings, level and connected console
#Arguments: level (0 for verbose, 1 for normal, 2 for error), message
#Output: formated message on STDERR or STDOUT (depending on message level, whether console is connected, H_LOG_VERBOSE, H_LOG_USE_STDOUT and H_LOG_QUIET)
#Return: always 0
h_log()
{
case "$1" in
0)
#Verbose messages will be logged only if H_LOG_VERBOSE is Y
#In that case they will be sent to "console" output irregardless it is console or redirect, and to configured permanent outputs
#The only way to stop verbose messaging on console output is using H_LOG_QUIET, which suppresses any "console" log output
test "$H_LOG_VERBOSE" != "Y" && return 0
h_log_console notice "$2"
h_log_perm notice "$2"
;;
1)
#Normal messages will be always logged to configured permanent outputs
#They will be sent to "console" output only if real console is connected to configured stream OR H_LOG_VERBOSE is Y
test -t `h_log_get_console` -o "$H_LOG_VERBOSE" = "Y" && h_log_console notice "$2"
h_log_perm notice "$2"
;;
2)
#Error messages will be always logged to all configured outputs
h_log_console error "$2"
h_log_perm error "$2"
;;
*)
#Unknown levels will be logged as error with additional notification
h_log 2 "Invalid log level $1. Message was: $2"
;;
esac
return 0;
}

#Log error and quits current (sub)shell
#Arguments: message[, exitcode (default 255)]
#Output: formatted error message on STDERR/STDOUT depending on settings
#Return: does not return
h_log_die()
{
h_log_error "Aborted: $1"
exit ${2:-255}
}

#shortcuts
h_log_verbose()
{
h_log 0 "$1"
}

h_log_normal()
{
h_log 1 "$1"
}

h_log_error()
{
h_log 2 "$1"
}


Como cualquier usuario podra utilizar estas rutinas, solo es necesario permiso de lectura. El permiso de ejecución no es necesario ya que no es un script sino un include.
De esta manera ya podemos usar auditoria (logs) en los scripts, y para muestra un simple script de test.


#!/bin/sh
#including rc.conf so we can get user script path
. /etc/rc.conf
#including our subroutines
. "$usr_path/inc/log.sh"

#now doing something, and then logging:
h_log_normal "We're good!"
#oops
h_log_error "Something broken"
#fatality!
h_log_die "You've killed me now, chap..."

La adaptación del script de "Automatización copiado interno en FreeNAS"quedaría pues de la siguiente manera:


#!/bin/sh

# variables de entorno
. /etc/rc.conf
. "$usr_path/inc/log.sh"

# variables del programa
DIRORIGEN="/mnt/cfinterno/usr/transmission/TEMPORAL";
DIRDESTINO="/mnt/data01/CINE/CINE13"

#inicio del programa
h_log_normal "status=start source=$DIRORIGEN target=$DIRDESTINO date=`date -j +"%d/%m/%Y %H:%M:%S"`"
for file in $DIRORIGEN/*.avi
do
if [ "$file" != "$DIRORIGEN/*.avi" ]; then
h_log_normal "[`date -j +"%d/%m/%Y %H:%M:%S"`] $file movido a destino."
mv "$file" "$DIRDESTINO"
sleep 1
fi
done
h_log_normal "status=end"

Un ejemplo de la programación mediante cron del script anterior.

No hay comentarios: