mirror of
https://github.com/infra7ti/dbtool.git
synced 2025-12-05 23:02:37 -03:00
Initial commit
This commit is contained in:
19
lib/builtin/getopts.bash
Normal file
19
lib/builtin/getopts.bash
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
declare -g options=':'
|
||||
declare -Ag arguments=()
|
||||
|
||||
function _requires_opt() {
|
||||
set +u
|
||||
local _opt=${1:-}
|
||||
local _var=$(eval echo ${2:-})
|
||||
|
||||
if [[ -z "${_var}" ]] && [[ ! -z "${_opt}" ]]; then
|
||||
${ui}.error && \
|
||||
${ui}.print \
|
||||
$"Required option missing: '-%s'. You must provide it.\n\n" \
|
||||
${_opt}
|
||||
exit -1
|
||||
fi
|
||||
set -u
|
||||
}
|
||||
6
lib/builtin/gettext.bash
Normal file
6
lib/builtin/gettext.bash
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
LC_MESSAGES=${LANG}
|
||||
TEXTDOMAIN="dbtool"
|
||||
TEXTDOMAINDIR="${PWD}/locale"
|
||||
INTERACTIVE=false
|
||||
229
lib/builtin/iniparse.bash
Normal file
229
lib/builtin/iniparse.bash
Normal file
@@ -0,0 +1,229 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# based on http://theoldschooldevops.com/2008/02/09/bash-ini-parser/
|
||||
#
|
||||
|
||||
PREFIX="cfg_section_"
|
||||
|
||||
function debug {
|
||||
if ! [ "x$BASH_INI_PARSER_DEBUG" == "x" ]
|
||||
then
|
||||
echo
|
||||
echo --start-- $*
|
||||
echo "${ini[*]}"
|
||||
echo --end--
|
||||
echo
|
||||
fi
|
||||
}
|
||||
|
||||
function cfg_parser {
|
||||
shopt -p extglob &> /dev/null
|
||||
CHANGE_EXTGLOB=$?
|
||||
if [ $CHANGE_EXTGLOB = 1 ]
|
||||
then
|
||||
shopt -s extglob
|
||||
fi
|
||||
ini="$(<$1)" # read the file
|
||||
ini=${ini//$'\r'/} # remove linefeed i.e dos2unix
|
||||
|
||||
ini="${ini//[/\\[}"
|
||||
debug "escaped ["
|
||||
ini="${ini//]/\\]}"
|
||||
debug "escaped ]"
|
||||
OLDIFS="$IFS"
|
||||
IFS=$'\n' && ini=( ${ini} ) # convert to line-array
|
||||
debug
|
||||
ini=( ${ini[*]/#*([[:space:]]);*/} )
|
||||
debug "removed ; comments"
|
||||
ini=( ${ini[*]/#*([[:space:]])\#*/} )
|
||||
debug "removed # comments"
|
||||
ini=( ${ini[*]/#+([[:space:]])/} ) # remove init whitespace
|
||||
debug "removed initial whitespace"
|
||||
ini=( ${ini[*]/%+([[:space:]])/} ) # remove ending whitespace
|
||||
debug "removed ending whitespace"
|
||||
ini=( ${ini[*]/%+([[:space:]])\\]/\\]} ) # remove non meaningful whitespace after sections
|
||||
debug "removed whitespace after section name"
|
||||
if [ $BASH_VERSINFO == 3 ]
|
||||
then
|
||||
ini=( ${ini[*]/+([[:space:]])=/=} ) # remove whitespace before =
|
||||
ini=( ${ini[*]/=+([[:space:]])/=} ) # remove whitespace after =
|
||||
ini=( ${ini[*]/+([[:space:]])=+([[:space:]])/=} ) # remove whitespace around =
|
||||
else
|
||||
ini=( ${ini[*]/*([[:space:]])=*([[:space:]])/=} ) # remove whitespace around =
|
||||
fi
|
||||
debug "removed space around ="
|
||||
ini=( ${ini[*]/#\\[/\}$'\n'"$PREFIX"} ) # set section prefix
|
||||
debug
|
||||
for ((i=0; i < "${#ini[@]}"; i++))
|
||||
do
|
||||
line="${ini[i]}"
|
||||
if [[ "$line" =~ $PREFIX.+ ]]
|
||||
then
|
||||
ini[$i]=${line// /_}
|
||||
fi
|
||||
done
|
||||
debug "subsections"
|
||||
ini=( ${ini[*]/%\\]/ \(} ) # convert text2function (1)
|
||||
debug
|
||||
ini=( ${ini[*]/=/=\( } ) # convert item to array
|
||||
debug
|
||||
ini=( ${ini[*]/%/ \)} ) # close array parenthesis
|
||||
debug
|
||||
ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
|
||||
debug
|
||||
ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
|
||||
debug
|
||||
ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
|
||||
ini=( ${ini[*]/%\{/\{$'\n''cfg_unset ${FUNCNAME/#'$PREFIX'}'$'\n'} ) # clean previous definition of section
|
||||
debug
|
||||
ini[0]="" # remove first element
|
||||
debug
|
||||
ini[${#ini[*]} + 1]='}' # add the last brace
|
||||
debug
|
||||
eval "$(echo "${ini[*]}")" # eval the result
|
||||
EVAL_STATUS=$?
|
||||
if [ $CHANGE_EXTGLOB = 1 ]
|
||||
then
|
||||
shopt -u extglob
|
||||
fi
|
||||
IFS="$OLDIFS"
|
||||
return $EVAL_STATUS
|
||||
}
|
||||
|
||||
function cfg_writer {
|
||||
local item fun newvar vars
|
||||
SECTION=$1
|
||||
OLDIFS="$IFS"
|
||||
IFS=' '$'\n'
|
||||
if [ -z "$SECTION" ]
|
||||
then
|
||||
fun="$(declare -F)"
|
||||
else
|
||||
fun="$(declare -F $PREFIX$SECTION)"
|
||||
if [ -z "$fun" ]
|
||||
then
|
||||
echo "section $SECTION not found" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fun="${fun//declare -f/}"
|
||||
for f in $fun; do
|
||||
[ "${f#$PREFIX}" == "${f}" ] && continue
|
||||
item="$(declare -f ${f})"
|
||||
item="${item##*\{}" # remove function definition
|
||||
item="${item##*FUNCNAME*$PREFIX\};}" # remove clear section
|
||||
item="${item/FUNCNAME\/#$PREFIX;}" # remove line
|
||||
item="${item/%\}}" # remove function close
|
||||
item="${item%)*}" # remove everything after parenthesis
|
||||
item="${item});" # add close parenthesis
|
||||
vars=""
|
||||
while [ "$item" != "" ]
|
||||
do
|
||||
newvar="${item%%=*}" # get item name
|
||||
vars="$vars$newvar" # add name to collection
|
||||
item="${item#*;}" # remove readed line
|
||||
done
|
||||
vars=$(echo "$vars" | sort -u) # remove duplication
|
||||
eval $f
|
||||
echo "[${f#$PREFIX}]" # output section
|
||||
for var in $vars; do
|
||||
eval 'local length=${#'$var'[*]}' # test if var is an array
|
||||
if [ $length == 1 ]
|
||||
then
|
||||
echo $var=\"${!var}\" #output var
|
||||
else
|
||||
echo ";$var is an array" # add comment denoting var is an array
|
||||
eval 'echo $var=\"${'$var'[*]}\"' # output array var
|
||||
fi
|
||||
done
|
||||
done
|
||||
IFS="$OLDIFS"
|
||||
}
|
||||
|
||||
function cfg_unset {
|
||||
local item fun newvar vars
|
||||
SECTION=$1
|
||||
OLDIFS="$IFS"
|
||||
IFS=' '$'\n'
|
||||
if [ -z "$SECTION" ]
|
||||
then
|
||||
fun="$(declare -F)"
|
||||
else
|
||||
fun="$(declare -F $PREFIX$SECTION)"
|
||||
if [ -z "$fun" ]
|
||||
then
|
||||
echo "section $SECTION not found" 1>&2
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fun="${fun//declare -f/}"
|
||||
for f in $fun; do
|
||||
[ "${f#$PREFIX}" == "${f}" ] && continue
|
||||
item="$(declare -f ${f})"
|
||||
item="${item##*\{}" # remove function definition
|
||||
item="${item##*FUNCNAME*$PREFIX\};}" # remove clear section
|
||||
item="${item/%\}}" # remove function close
|
||||
item="${item%)*}" # remove everything after parenthesis
|
||||
item="${item});" # add close parenthesis
|
||||
vars=""
|
||||
while [ "$item" != "" ]
|
||||
do
|
||||
newvar="${item%%=*}" # get item name
|
||||
vars="$vars $newvar" # add name to collection
|
||||
item="${item#*;}" # remove readed line
|
||||
done
|
||||
for var in $vars; do
|
||||
unset $var
|
||||
done
|
||||
done
|
||||
IFS="$OLDIFS"
|
||||
}
|
||||
|
||||
function cfg_clear {
|
||||
SECTION=$1
|
||||
OLDIFS="$IFS"
|
||||
IFS=' '$'\n'
|
||||
if [ -z "$SECTION" ]
|
||||
then
|
||||
fun="$(declare -F)"
|
||||
else
|
||||
fun="$(declare -F $PREFIX$SECTION)"
|
||||
if [ -z "$fun" ]
|
||||
then
|
||||
echo "section $SECTION not found" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fun="${fun//declare -f/}"
|
||||
for f in $fun; do
|
||||
[ "${f#$PREFIX}" == "${f}" ] && continue
|
||||
unset -f ${f}
|
||||
done
|
||||
IFS="$OLDIFS"
|
||||
}
|
||||
|
||||
function cfg_update {
|
||||
SECTION=$1
|
||||
VAR=$2
|
||||
OLDIFS="$IFS"
|
||||
IFS=' '$'\n'
|
||||
fun="$(declare -F $PREFIX$SECTION)"
|
||||
if [ -z "$fun" ]
|
||||
then
|
||||
echo "section $SECTION not found" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fun="${fun//declare -f/}"
|
||||
item="$(declare -f ${fun})"
|
||||
#item="${item##* $VAR=*}" # remove var declaration
|
||||
item="${item/%\}}" # remove function close
|
||||
item="${item}
|
||||
$VAR=(${!VAR})
|
||||
"
|
||||
item="${item}
|
||||
}" # close function again
|
||||
|
||||
eval "function $item"
|
||||
}
|
||||
|
||||
# vim: filetype=sh
|
||||
16
lib/builtin/ui.bash
Normal file
16
lib/builtin/ui.bash
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- UI Class -----------------------------------------------------------------
|
||||
|
||||
ui.__load__() {
|
||||
# Default User Interface
|
||||
: ${ui:=cli}
|
||||
|
||||
# Loads the UI
|
||||
source ${libdir}/ui/${ui}.bash
|
||||
}
|
||||
|
||||
ui.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
26
lib/commands/create.bash
Normal file
26
lib/commands/create.bash
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- Create Class --------------------------------------------------------------
|
||||
|
||||
create.run() {
|
||||
local _plugin=$(basename ${host[create_tool]})
|
||||
|
||||
${ui}.info && ${ui}.tab 2 && \
|
||||
${ui}.subitem $"Loading plugin: "
|
||||
test -f ${libdir}/plugins/${_plugin}.bash && \
|
||||
${ui}.emphasis $"%s " ${_plugin} && \
|
||||
source ${libdir}/plugins/${_plugin}.bash
|
||||
|
||||
eval \${_plugin}.run
|
||||
}
|
||||
|
||||
create.__load__() {
|
||||
unset -v cmd
|
||||
options+='c:'
|
||||
arguments['c']="cmd"
|
||||
}
|
||||
|
||||
create.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
26
lib/commands/drop.bash
Normal file
26
lib/commands/drop.bash
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- Create Class --------------------------------------------------------------
|
||||
|
||||
drop.run() {
|
||||
local _plugin=$(basename ${host[drop_tool]})
|
||||
|
||||
${ui}.info && ${ui}.tab 2 && \
|
||||
${ui}.subitem $"Loading plugin: "
|
||||
test -f ${libdir}/plugins/${_plugin}.bash && \
|
||||
${ui}.emphasis $"%s " ${_plugin} && \
|
||||
source ${libdir}/plugins/${_plugin}.bash
|
||||
|
||||
eval \${_plugin}.run
|
||||
}
|
||||
|
||||
drop.__load__() {
|
||||
unset -v cmd
|
||||
options+='c:'
|
||||
arguments['c']="cmd"
|
||||
}
|
||||
|
||||
drop.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
26
lib/commands/dump.bash
Normal file
26
lib/commands/dump.bash
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- Dump Class ---------------------------------------------------------------
|
||||
|
||||
dump.run() {
|
||||
local _plugin=$(basename ${host[dump_tool]})
|
||||
|
||||
${ui}.info && ${ui}.tab 2 && \
|
||||
${ui}.subitem $"Loading plugin: "
|
||||
test -f ${libdir}/plugins/${_plugin}.bash && \
|
||||
${ui}.emphasis $"%s " ${_plugin} && \
|
||||
source ${libdir}/plugins/${_plugin}.bash
|
||||
|
||||
eval \${_plugin}.run
|
||||
}
|
||||
|
||||
dump.__load__() {
|
||||
unset -v cmd
|
||||
options+='c:'
|
||||
arguments['c']="cmd"
|
||||
}
|
||||
|
||||
dump.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
26
lib/commands/restore.bash
Normal file
26
lib/commands/restore.bash
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- Restore Class ------------------------------------------------------------
|
||||
|
||||
restore.run() {
|
||||
local _plugin=$(basename ${host[restore_tool]})
|
||||
|
||||
${ui}.info && ${ui}.tab 2 && \
|
||||
${ui}.subitem $"Loading plugin: "
|
||||
test -f ${libdir}/plugins/${_plugin}.bash && \
|
||||
${ui}.emphasis $"%s " ${_plugin} && \
|
||||
source ${libdir}/plugins/${_plugin}.bash
|
||||
|
||||
eval \${_plugin}.run
|
||||
}
|
||||
|
||||
restore.__load__() {
|
||||
unset -v cmd
|
||||
options+='c:'
|
||||
arguments['c']="cmd"
|
||||
}
|
||||
|
||||
restore.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
15
lib/entities/dblist.bash
Normal file
15
lib/entities/dblist.bash
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- Host Class ---------------------------------------------------------------
|
||||
|
||||
dblist.__load__() {
|
||||
unset -v dblist
|
||||
|
||||
options+='d:'
|
||||
arguments['d']="dblist"
|
||||
}
|
||||
|
||||
dblist.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
120
lib/entities/host.bash
Normal file
120
lib/entities/host.bash
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- Host Class ---------------------------------------------------------------
|
||||
|
||||
host.__load__() {
|
||||
source ${libdir}/builtin/iniparse.bash
|
||||
unset -v host
|
||||
|
||||
options+='h:'
|
||||
arguments['h']="host"
|
||||
}
|
||||
|
||||
host.set() {
|
||||
function __in_array() {
|
||||
local a=${@:2}
|
||||
[[ " ${a[*]} " =~ " ${1} " ]] \
|
||||
&& return 0 \
|
||||
|| return 1
|
||||
}
|
||||
|
||||
function _validate_host() {
|
||||
__in_array ${host:-} "${_HOSTS[@]}" || \
|
||||
(
|
||||
${ui}.error
|
||||
${ui}.print $"Invalid host: %s: " ${host:-'(empty)'}
|
||||
kill -TERM ${$} 2>&1 > /dev/null
|
||||
)
|
||||
return 0
|
||||
}
|
||||
|
||||
function _get_config() {
|
||||
local _host=${host}
|
||||
set +eu
|
||||
|
||||
cfg_parser "${cfgdir}/hosts/${_host}.ini"
|
||||
|
||||
cfg_section_engines
|
||||
local _postgresql=${postgresql}
|
||||
local _mysql=${mysql}
|
||||
|
||||
if [[ "${_postgresql,,}" == "true" ]]; then
|
||||
cfg_section_postgresql
|
||||
query_tool='psql'
|
||||
create_tool='createdb'
|
||||
drop_tool='dropdb'
|
||||
dump_tool='pg_dump'
|
||||
restore_tool='pg_restore'
|
||||
fi
|
||||
|
||||
declare -Ag host=(
|
||||
[name]=${_host}
|
||||
[db_env]=${env}
|
||||
[db_host]=${db_host}
|
||||
[db_port]=${db_port}
|
||||
[db_user]=${db_user}
|
||||
[db_pass]=${db_password}
|
||||
)
|
||||
|
||||
exec=
|
||||
eval cfg_section_${query_tool}
|
||||
host+=(
|
||||
[query_tool]=${exec:-$(which ${query_tool})}
|
||||
[query_extraopts]=${extraopts}
|
||||
[query_ext]=${extension}
|
||||
)
|
||||
|
||||
exec=
|
||||
host+=(
|
||||
[create_tool]=${exec:-$(which ${create_tool})}
|
||||
[drop_tool]=${exec:-$(which ${drop_tool})}
|
||||
)
|
||||
|
||||
exec=
|
||||
eval cfg_section_${dump_tool}
|
||||
host+=(
|
||||
[dump_tool]=${exec:-$(which ${dump_tool})}
|
||||
[dump_format]=${dump_format:-'custom'} # postgresql only
|
||||
[dump_extraopts]=${extraopts}
|
||||
[dump_ext]=${extension}
|
||||
)
|
||||
|
||||
exec=
|
||||
eval cfg_section_${restore_tool}
|
||||
host+=(
|
||||
[restore_tool]=${exec:-$(which ${restore_tool})}
|
||||
[restore_format]=${dump_format:-'custom'} # postgresql only
|
||||
[restore_extraopts]=${extraopts}
|
||||
[restore_ext]=${extension}
|
||||
)
|
||||
|
||||
set -eu
|
||||
return 0
|
||||
}
|
||||
|
||||
declare -a _HOSTS=(
|
||||
$(find ${cfgdir}/hosts/ \
|
||||
-type f \
|
||||
-name \*.ini \
|
||||
-exec basename {} \; \
|
||||
| sed 's/.ini//g'
|
||||
)
|
||||
)
|
||||
|
||||
_validate_host
|
||||
_get_config
|
||||
|
||||
hostname=${host[name]}
|
||||
if [[ ! -z "${hostname}" ]]; then
|
||||
${ui}.debug 'line' 'compact'
|
||||
${ui}.info
|
||||
${ui}.item $"Selecting current server [host %s %s]\n" \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color) \
|
||||
${hostname}
|
||||
fi
|
||||
}
|
||||
|
||||
host.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
120
lib/plugins/createdb.bash
Normal file
120
lib/plugins/createdb.bash
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- CreateDb Class ------------------------------------------------------------
|
||||
|
||||
createdb.__load__() {
|
||||
return 0
|
||||
}
|
||||
|
||||
createdb.run() {
|
||||
local _logdir=$(pwd)/logs
|
||||
local _pgpassfile=~/.pgpass
|
||||
declare -A pids=()
|
||||
|
||||
function __write_pgpass() {
|
||||
if [[ ! -z "${host[name]}" ]]; then
|
||||
touch ${_pgpassfile} && \
|
||||
chmod 0600 ${_pgpassfile}
|
||||
printf "%s:%s:*:%s:%s" \
|
||||
${host[db_host]} \
|
||||
${host[db_port]} \
|
||||
${host[db_user]} \
|
||||
${host[db_pass]} \
|
||||
> ${_pgpassfile}
|
||||
fi
|
||||
}
|
||||
|
||||
function __pre() {
|
||||
${ui}.debug 'text' '\n'
|
||||
mkdir -p ${_logdir}
|
||||
__write_pgpass
|
||||
echo
|
||||
}
|
||||
|
||||
function __post() {
|
||||
rm -rf ${_pgpassfile}
|
||||
}
|
||||
|
||||
function __wait_processes() {
|
||||
${ui}.info && \
|
||||
${ui}.item $"Waiting for processes to complete\n"
|
||||
${ui}.line 'compact'
|
||||
while true; do
|
||||
alive_pids=()
|
||||
for _pid in "${!pids[@]}"; do
|
||||
_db=${pids[${_pid}]}
|
||||
kill -0 "${_pid}" 2>/dev/null \
|
||||
&& _etime[${_pid}]=$(ps -o etimes= -p ${_pid} || :) \
|
||||
&& alive_pids+="${_pid} "
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[createdb]")
|
||||
${ui}.color && \
|
||||
${ui}.subitem $"process still running "
|
||||
${ui}.print '%s ' \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color)
|
||||
${ui}.print "[db=${_db}, pid=${_pid}, etime=%ss]\n" \
|
||||
${_etime[${_pid}]}
|
||||
done
|
||||
[ ${#alive_pids[@]} -eq 0 ] && break
|
||||
sleep 5
|
||||
done
|
||||
${ui}.print '\n'
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[createdb]")
|
||||
${ui}.color && ${ui}.subitem $"all processes terminated\n"
|
||||
${ui}.line 'compact'
|
||||
}
|
||||
|
||||
function __createdb() {
|
||||
local _createdb=$(which createdb || exit -1)
|
||||
local _logfile=create-${db}-$(date +"%Y%m%d%H%M%S").log
|
||||
local _cmd="${_createdb} \
|
||||
-h ${host[db_host]} \
|
||||
-p ${host[db_port]} \
|
||||
-U ${host[db_user]} \
|
||||
${host[create_extraopts]:-} \
|
||||
-O ${host[db_user]} ${db}"
|
||||
_cmd=$(echo -ne ${_cmd})
|
||||
${ui}.debug 'line' 'compact'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Running command:\n"
|
||||
#${ui}.debug 'text' "$(${ui}.color gold)${_cmd//${_restoredir}\//} \
|
||||
${ui}.debug 'text' "$(${ui}.color gold)${_cmd} \
|
||||
$(${ui}.color)" \
|
||||
| fold -sw 60 \
|
||||
| sed "s/^/$(printf '%14s' ' ')/g"
|
||||
${ui}.debug 'text' '\n'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Process will run in backgroud [logfile=%s]\n" \
|
||||
${_logfile}
|
||||
${_cmd} > ${_logdir}/${_logfile} 2>&1 &
|
||||
pids+=([$!]=${db})
|
||||
}
|
||||
|
||||
function __run() {
|
||||
${ui}.debug 'text' '\n'
|
||||
dbs="${dblist}"
|
||||
test -f ${cfgdir}/databases/${dblist}.txt && \
|
||||
dbs=$(< ${cfgdir}/databases/${dblist}.txt);
|
||||
for db in ${dbs}; do
|
||||
${ui}.info && \
|
||||
${ui}.item $"Starting %s %s [host=%s, db=%s]\n" \
|
||||
"createdb" \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color) \
|
||||
${host[name]} \
|
||||
${db}
|
||||
__createdb
|
||||
${ui}.debug 'text' '\n'
|
||||
done
|
||||
__wait_processes
|
||||
}
|
||||
|
||||
__pre
|
||||
__run
|
||||
__post
|
||||
}
|
||||
|
||||
createdb.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
120
lib/plugins/dropdb.bash
Normal file
120
lib/plugins/dropdb.bash
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- DropDb Class --------------------------------------------------------------
|
||||
|
||||
dropdb.__load__() {
|
||||
return 0
|
||||
}
|
||||
|
||||
dropdb.run() {
|
||||
local _logdir=$(pwd)/logs
|
||||
local _pgpassfile=~/.pgpass
|
||||
declare -A pids=()
|
||||
|
||||
function __write_pgpass() {
|
||||
if [[ ! -z "${host[name]}" ]]; then
|
||||
touch ${_pgpassfile} && \
|
||||
chmod 0600 ${_pgpassfile}
|
||||
printf "%s:%s:*:%s:%s" \
|
||||
${host[db_host]} \
|
||||
${host[db_port]} \
|
||||
${host[db_user]} \
|
||||
${host[db_pass]} \
|
||||
> ${_pgpassfile}
|
||||
fi
|
||||
}
|
||||
|
||||
function __pre() {
|
||||
${ui}.debug 'text' '\n'
|
||||
mkdir -p ${_logdir}
|
||||
__write_pgpass
|
||||
echo
|
||||
}
|
||||
|
||||
function __post() {
|
||||
rm -rf ${_pgpassfile}
|
||||
}
|
||||
|
||||
function __wait_processes() {
|
||||
${ui}.info && \
|
||||
${ui}.item $"Waiting for processes to complete\n"
|
||||
${ui}.line 'compact'
|
||||
while true; do
|
||||
alive_pids=()
|
||||
for _pid in "${!pids[@]}"; do
|
||||
_db=${pids[${_pid}]}
|
||||
kill -0 "${_pid}" 2>/dev/null \
|
||||
&& _etime[${_pid}]=$(ps -o etimes= -p ${_pid} || :) \
|
||||
&& alive_pids+="${_pid} "
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[dropdb]")
|
||||
${ui}.color && \
|
||||
${ui}.subitem $"process still running "
|
||||
${ui}.print '%s ' \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color)
|
||||
${ui}.print "[db=${_db}, pid=${_pid}, etime=%ss]\n" \
|
||||
${_etime[${_pid}]}
|
||||
done
|
||||
[ ${#alive_pids[@]} -eq 0 ] && break
|
||||
sleep 5
|
||||
done
|
||||
${ui}.print '\n'
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[dropdb]")
|
||||
${ui}.color && ${ui}.subitem $"all processes terminated\n"
|
||||
${ui}.line 'compact'
|
||||
}
|
||||
|
||||
function __dropdb() {
|
||||
local _dropdb=$(which dropdb || exit -1)
|
||||
local _logfile=drop-${db}-$(date +"%Y%m%d%H%M%S").log
|
||||
local _cmd="${_dropdb} \
|
||||
-h ${host[db_host]} \
|
||||
-p ${host[db_port]} \
|
||||
-U ${host[db_user]} \
|
||||
${host[drop_extraopts]:-} \
|
||||
-f --if-exists ${db}"
|
||||
_cmd=$(echo -ne ${_cmd})
|
||||
${ui}.debug 'line' 'compact'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Running command:\n"
|
||||
#${ui}.debug 'text' "$(${ui}.color gold)${_cmd//${_restoredir}\//} \
|
||||
${ui}.debug 'text' "$(${ui}.color gold)${_cmd} \
|
||||
$(${ui}.color)" \
|
||||
| fold -sw 60 \
|
||||
| sed "s/^/$(printf '%14s' ' ')/g"
|
||||
${ui}.debug 'text' '\n'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Process will run in backgroud [logfile=%s]\n" \
|
||||
${_logfile}
|
||||
${_cmd} > ${_logdir}/${_logfile} 2>&1 &
|
||||
pids+=([$!]=${db})
|
||||
}
|
||||
|
||||
function __run() {
|
||||
${ui}.debug 'text' '\n'
|
||||
dbs="${dblist}"
|
||||
test -f ${cfgdir}/databases/${dblist}.txt && \
|
||||
dbs=$(< ${cfgdir}/databases/${dblist}.txt);
|
||||
for db in ${dbs}; do
|
||||
${ui}.info && \
|
||||
${ui}.item $"Starting %s %s [host=%s, db=%s]\n" \
|
||||
"dropdb" \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color) \
|
||||
${host[name]} \
|
||||
${db}
|
||||
__dropdb
|
||||
${ui}.debug 'text' '\n'
|
||||
done
|
||||
__wait_processes
|
||||
}
|
||||
|
||||
__pre
|
||||
__run
|
||||
__post
|
||||
}
|
||||
|
||||
dropdb.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
123
lib/plugins/pg_dump.bash
Normal file
123
lib/plugins/pg_dump.bash
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- PGDump Class -------------------------------------------------------------
|
||||
|
||||
pg_dump.__load__() {
|
||||
return 0
|
||||
}
|
||||
|
||||
pg_dump.run() {
|
||||
local _dumpdir=$(pwd)/export/${host[name]}
|
||||
local _logdir=$(pwd)/logs
|
||||
local _pgpassfile=~/.pgpass
|
||||
declare -A pids=()
|
||||
|
||||
function __write_pgpass() {
|
||||
if [[ ! -z "${host[name]}" ]]; then
|
||||
touch ${_pgpassfile} && \
|
||||
chmod 0600 ${_pgpassfile}
|
||||
printf "%s:%s:*:%s:%s" \
|
||||
${host[db_host]} \
|
||||
${host[db_port]} \
|
||||
${host[db_user]} \
|
||||
${host[db_pass]} \
|
||||
> ${_pgpassfile}
|
||||
fi
|
||||
}
|
||||
|
||||
function __pre() {
|
||||
__write_pgpass
|
||||
mkdir -p ${_dumpdir} ${_logdir}
|
||||
echo
|
||||
}
|
||||
|
||||
function __post() {
|
||||
rm -rf ${_pgpassfile}
|
||||
}
|
||||
|
||||
function __wait_processes() {
|
||||
${ui}.info && \
|
||||
${ui}.item $"Waiting for processes to complete\n"
|
||||
${ui}.line 'compact'
|
||||
while true; do
|
||||
alive_pids=()
|
||||
for _pid in "${!pids[@]}"; do
|
||||
_db=${pids[${_pid}]}
|
||||
kill -0 "${_pid}" 2>/dev/null \
|
||||
&& _etime[${_pid}]=$(ps -o etimes= -p ${_pid} || :) \
|
||||
&& alive_pids+="${_pid} "
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[pg_dump]")
|
||||
${ui}.color && \
|
||||
${ui}.subitem $"process still running "
|
||||
${ui}.print '%s ' \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color)
|
||||
${ui}.print "[db=${_db}, pid=${_pid}, etime=%ss]\n" \
|
||||
${_etime[${_pid}]}
|
||||
done
|
||||
[ ${#alive_pids[@]} -eq 0 ] && break
|
||||
sleep 5
|
||||
done
|
||||
${ui}.print '\n'
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[pg_dump]")
|
||||
${ui}.color && ${ui}.subitem $"all processes terminated\n"
|
||||
${ui}.line 'compact'
|
||||
}
|
||||
|
||||
function __pg_dump() {
|
||||
local _pg_dump=$(which pg_dump || exit -1)
|
||||
local _ext=${host[dump_ext]:-pgc}
|
||||
local _dumpfile=${db}-$(date +"%Y%m%d%H%M%S").${_ext}
|
||||
local _logfile=export-${db}-$(date +"%Y%m%d%H%M%S").${_ext}.log
|
||||
local _cmd="${_pg_dump} \
|
||||
-h ${host[db_host]} \
|
||||
-p ${host[db_port]} \
|
||||
-U ${host[db_user]} \
|
||||
-F ${host[dump_format]:0:1} \
|
||||
${host[dump_extraopts]:-} \
|
||||
-O -v -d ${db} \
|
||||
-f ${_dumpdir}/${_dumpfile}"
|
||||
_cmd=$(echo -ne ${_cmd})
|
||||
${ui}.debug 'line' 'compact'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Running command:\n"
|
||||
${ui}.debug 'text' "$(${ui}.color gold)${_cmd//${_dumpdir}\//} \
|
||||
$(${ui}.color)" \
|
||||
| fold -sw 60 \
|
||||
| sed "s/^/$(printf '%14s' ' ')/g"
|
||||
${ui}.debug 'text' '\n'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Process will run in backgroud [logfile=%s]\n" \
|
||||
${_logfile}
|
||||
${_cmd} > ${_logdir}/${_logfile} 2>&1 &
|
||||
pids+=([$!]=${db})
|
||||
}
|
||||
|
||||
function __run() {
|
||||
${ui}.debug 'text' '\n'
|
||||
dbs="${dblist}"
|
||||
test -f ${cfgdir}/databases/${dblist}.txt && \
|
||||
dbs=$(< ${cfgdir}/databases/${dblist}.txt);
|
||||
for db in ${dbs}; do
|
||||
${ui}.info && \
|
||||
${ui}.item $"Starting %s %s [host=%s, db=%s]\n" \
|
||||
"pg_dump" \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color) \
|
||||
${host[name]} \
|
||||
${db}
|
||||
__pg_dump
|
||||
${ui}.debug 'text' '\n'
|
||||
done
|
||||
__wait_processes
|
||||
}
|
||||
|
||||
__pre
|
||||
__run
|
||||
__post
|
||||
}
|
||||
|
||||
pg_dump.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
135
lib/plugins/pg_restore.bash
Normal file
135
lib/plugins/pg_restore.bash
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- PGDump Class -------------------------------------------------------------
|
||||
|
||||
pg_restore.__load__() {
|
||||
return 0
|
||||
}
|
||||
|
||||
pg_restore.run() {
|
||||
local _restoredir=$(pwd)/import/${host[name]}
|
||||
local _logdir=$(pwd)/logs
|
||||
local _pgpassfile=~/.pgpass
|
||||
declare -A pids=()
|
||||
|
||||
function __write_pgpass() {
|
||||
if [[ ! -z "${host[name]}" ]]; then
|
||||
touch ${_pgpassfile} && \
|
||||
chmod 0600 ${_pgpassfile}
|
||||
printf "%s:%s:*:%s:%s" \
|
||||
${host[db_host]} \
|
||||
${host[db_port]} \
|
||||
${host[db_user]} \
|
||||
${host[db_pass]} \
|
||||
> ${_pgpassfile}
|
||||
fi
|
||||
}
|
||||
|
||||
function __pre() {
|
||||
${ui}.debug 'text' '\n'
|
||||
mkdir -p ${_restoredir} ${_logdir}
|
||||
dbs="${dblist}"
|
||||
test -f ${cfgdir}/databases/${dblist}.txt && \
|
||||
dbs=$(< ${cfgdir}/databases/${dblist}.txt);
|
||||
for db in ${dbs}; do
|
||||
# Check if dump file exists
|
||||
[ ! -f ${_restoredir}/${db}.${host[restore_ext]} ] && \
|
||||
${ui}.error && \
|
||||
${ui}.print $"Dump file not found: %s\n" \
|
||||
${db}.${host[restore_ext]} && \
|
||||
exit -1
|
||||
done
|
||||
__write_pgpass
|
||||
echo
|
||||
}
|
||||
|
||||
function __post() {
|
||||
rm -rf ${_pgpassfile}
|
||||
}
|
||||
|
||||
function __wait_processes() {
|
||||
${ui}.info && \
|
||||
${ui}.item $"Waiting for processes to complete\n"
|
||||
${ui}.line 'compact'
|
||||
while true; do
|
||||
alive_pids=()
|
||||
for _pid in "${!pids[@]}"; do
|
||||
_db=${pids[${_pid}]}
|
||||
kill -0 "${_pid}" 2>/dev/null \
|
||||
&& _etime[${_pid}]=$(ps -o etimes= -p ${_pid} || :) \
|
||||
&& alive_pids+="${_pid} "
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[pg_restore]")
|
||||
${ui}.color && \
|
||||
${ui}.subitem $"process still running "
|
||||
${ui}.print '%s ' \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color)
|
||||
${ui}.print "[db=${_db}, pid=${_pid}, etime=%ss]\n" \
|
||||
${_etime[${_pid}]}
|
||||
done
|
||||
[ ${#alive_pids[@]} -eq 0 ] && break
|
||||
sleep 5
|
||||
done
|
||||
${ui}.print '\n'
|
||||
${ui}.info && \
|
||||
${ui}.print '%s ' $(${ui}.color green; echo "[pg_restore]")
|
||||
${ui}.color && ${ui}.subitem $"all processes terminated\n"
|
||||
${ui}.line 'compact'
|
||||
}
|
||||
|
||||
function __pg_restore() {
|
||||
local _pg_restore=$(which pg_restore || exit -1)
|
||||
local _ext=${host[restore_ext]:-pgc}
|
||||
local _restorefile=${db}.${_ext}
|
||||
local _logfile=import-${db}-$(date +"%Y%m%d%H%M%S").${_ext}.log
|
||||
local _cmd="${_pg_restore} \
|
||||
-h ${host[db_host]} \
|
||||
-p ${host[db_port]} \
|
||||
-U ${host[db_user]} \
|
||||
-F ${host[restore_format]:0:1} \
|
||||
${host[restore_extraopts]:-} \
|
||||
-O -v -d ${db} \
|
||||
${_restoredir}/${_restorefile}"
|
||||
_cmd=$(echo -ne ${_cmd})
|
||||
${ui}.debug 'line' 'compact'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Running command:\n"
|
||||
${ui}.debug 'text' "$(${ui}.color gold)${_cmd//${_restoredir}\//} \
|
||||
$(${ui}.color)" \
|
||||
| fold -sw 60 \
|
||||
| sed "s/^/$(printf '%14s' ' ')/g"
|
||||
${ui}.debug 'text' '\n'
|
||||
${ui}.debug && ${ui}.tab 2
|
||||
${ui}.debug 'subitem' $"Process will run in backgroud [logfile=%s]\n" \
|
||||
${_logfile}
|
||||
${_cmd} > ${_logdir}/${_logfile} 2>&1 &
|
||||
pids+=([$!]=${db})
|
||||
}
|
||||
|
||||
function __run() {
|
||||
${ui}.debug 'text' '\n'
|
||||
dbs="${dblist}"
|
||||
test -f ${cfgdir}/databases/${dblist}.txt && \
|
||||
dbs=$(< ${cfgdir}/databases/${dblist}.txt);
|
||||
for db in ${dbs}; do
|
||||
${ui}.info && \
|
||||
${ui}.item $"Starting %s %s [host=%s, db=%s]\n" \
|
||||
"pg_restore" \
|
||||
$(${ui}.color gold; ${ui}.arrow right; ${ui}.color) \
|
||||
${host[name]} \
|
||||
${db}
|
||||
__pg_restore
|
||||
${ui}.debug 'text' '\n'
|
||||
done
|
||||
__wait_processes
|
||||
}
|
||||
|
||||
__pre
|
||||
__run
|
||||
__post
|
||||
}
|
||||
|
||||
pg_restore.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
160
lib/ui/cli.bash
Normal file
160
lib/ui/cli.bash
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# -- CLI Class ----------------------------------------------------------------
|
||||
|
||||
cli.__load__() {
|
||||
NO_FMT="\033[0m"
|
||||
F_BOLD="\033[1m"
|
||||
|
||||
C_NONE="\033[0m"
|
||||
C_RED="\033[38;5;9m"
|
||||
C_BLUE="\033[38;5;12m"
|
||||
C_GREEN="\033[38;5;2m"
|
||||
C_ORANGE="\033[38;5;208m"
|
||||
C_GOLD="\033[38;5;220m"
|
||||
C_PURPLE="\033[38;5;5m"
|
||||
|
||||
S_INFO="${C_BLUE}\u2691 [info] ${NO_FMT} "
|
||||
S_SUCCESS="${C_GREEN}\u2691 [success] ${NO_FMT}\u2713 "
|
||||
S_DEBUG="${C_PURPLE}\u2691 [debug]${NO_FMT} "
|
||||
S_WARN="${C_ORANGE}\u2691 [warning] ${NO_FMT} "
|
||||
S_ERROR="${C_RED}\u2691 [error] ${NO_FMT}\u2715 "
|
||||
|
||||
U_ITEM="\u2192"
|
||||
U_SUBITEM="\u21E2"
|
||||
#U_SUBITEM="\u21B3"
|
||||
}
|
||||
|
||||
cli.init() {
|
||||
function __write_header() {
|
||||
#clear && echo
|
||||
printf $"${@:1:1}" \
|
||||
$(echo -ne ${F_BOLD}${C_BLUE}${@:2:1}${NO_FMT}) \
|
||||
$(echo -ne ${C_ORANGE}${@:3}${NO_FMT}) && \
|
||||
for i in $(seq 1 ${cols}); do \
|
||||
printf "%s" $(echo -ne "\u2581"); \
|
||||
sleep .005; \
|
||||
done && \
|
||||
printf "%2s\n"
|
||||
}
|
||||
|
||||
function _init() {
|
||||
__write_header "${@}"
|
||||
}
|
||||
|
||||
cols=$(tput cols)
|
||||
_init "${@}"
|
||||
}
|
||||
|
||||
cli.line() {
|
||||
[ -z "${1:-}" ] && printf "%s\n"
|
||||
for i in $(seq 1 ${cols}); do \
|
||||
printf "%s" $(echo -ne "\u2504${NO_FMT}"); \
|
||||
done && \
|
||||
[ -z "${1:-}" ] && printf "%2s\n"
|
||||
return 0
|
||||
}
|
||||
|
||||
cli.move_to_col() {
|
||||
echo -ne "echo -en \\033[${1:-0}G"
|
||||
}
|
||||
|
||||
cli.info() {
|
||||
cli.move_to_col 0
|
||||
echo -ne "${S_INFO}"
|
||||
}
|
||||
|
||||
cli.warn() {
|
||||
cli.move_to_col 0
|
||||
echo -ne "${S_WARN}"
|
||||
}
|
||||
|
||||
cli.error() {
|
||||
cli.move_to_col 0
|
||||
echo -ne "${S_ERROR}"
|
||||
}
|
||||
|
||||
cli.print() {
|
||||
printf $"${@:1:1}" ${@:2}
|
||||
}
|
||||
|
||||
cli.tab() {
|
||||
printf "%${1:-2}s" ' '
|
||||
}
|
||||
|
||||
cli.item() {
|
||||
echo -ne "${U_ITEM} "
|
||||
cli.print "${@}"
|
||||
}
|
||||
|
||||
cli.subitem() {
|
||||
echo -ne "${U_SUBITEM} "
|
||||
cli.print "${@}"
|
||||
}
|
||||
|
||||
cli.emphasis() {
|
||||
echo -ne "${F_BOLD}${C_BLUE}"
|
||||
cli.print "${@}"
|
||||
echo -ne "${NO_FMT}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
cli.debug() {
|
||||
if [ "${debug:-,,}" == "true" ]; then
|
||||
[ ${#@} -lt 1 ] && \
|
||||
cli.move_to_col 0 && \
|
||||
echo -ne "${S_DEBUG}"
|
||||
|
||||
[ "${1:-z}" == "text" ] && \
|
||||
cli.print "${@:2}"
|
||||
|
||||
[ "${1:-z}" == "line" ] && \
|
||||
cli.move_to_col 0 && \
|
||||
cli.line "${@:2}"
|
||||
|
||||
[ "${1:-z}" == "item" ] && \
|
||||
cli.item "${@:2}"
|
||||
|
||||
[ "${1:-z}" == "subitem" ] && \
|
||||
cli.subitem "${@:2}"
|
||||
|
||||
[ "${1:-z}" == "emphasis" ] && \
|
||||
cli.emphasis "${@:2}"
|
||||
fi
|
||||
set -e # why we need to set it again?
|
||||
}
|
||||
|
||||
cli.dbg_item() {
|
||||
[ "${debug:-,,}" == "true" ] && \
|
||||
echo -ne "${U_ITEM} " && \
|
||||
cli.print "${@}"
|
||||
}
|
||||
cli.dbg_subitem() {
|
||||
[ "${debug:-,,}" == "true" ] && \
|
||||
echo -ne "${U_SUBITEM} " && \
|
||||
cli.print "${@}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
cli.color() {
|
||||
color=${1:-NONE}
|
||||
eval "echo -ne \${C_${color^^}}"
|
||||
}
|
||||
|
||||
cli.arrow() {
|
||||
case ${1:-} in
|
||||
top) arrow='\u21E1';;
|
||||
left) arrow='\u21E0' ;;
|
||||
bottom) arrow='\u21E3' ;;
|
||||
right) arrow='\u21E2' ;;
|
||||
esac
|
||||
|
||||
echo -ne "${arrow}"
|
||||
}
|
||||
|
||||
cli.__load__
|
||||
|
||||
# vim: ts=4:sw=4:sts=4:et
|
||||
Reference in New Issue
Block a user