commit f9a2a7b460f387794c7903d2f1f8ca2f0cf29947
parent c94ce73d34773a007e88bcee9b4f9c2126ec58b5
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date: Thu, 19 Jun 2014 16:42:02 +0200
vserver deployment functions
Diffstat:
3 files changed, 203 insertions(+), 79 deletions(-)
diff --git a/bin/confz b/bin/confz
@@ -1,16 +1,18 @@
#!/bin/zsh
# vim: ft=zsh noet ts=4 sts=4 sw=4
-setopt extendedglob warn_create_global
+setopt extended_glob warn_create_global
-typeset -gA vars
-typeset -g c c_prev
+typeset -gA vars vars_prev
+typeset -ga do_command
+# helper that prints out error message and exits
die() {
- print -r - "$@"
+ print -r - "$@" >&2
exit 1
}
+# autoload all relevant functions and run confz_*_init
confz_load() {
local func
local -a match mbegin mend confz_functions func_files
@@ -31,46 +33,72 @@ confz_load() {
done
}
+# check & run a dependency
require() {
- local name arg c_switch
- local -a lift
-
- c_prev=$c
- c=${(j: :)${(q)*}}
+ # usage: require <name> [<variables>] [-- <arguments>]
+ # where:
+ # <name> is a dependency name as defined by function confz_<name>_check
+ # <variables> are variable assignments in the form of:
+ # foo=bar -- sets ${vars[foo]} in calee to value "bar"
+ # :foo -- passes ${vars[foo]} from caller to calee
+ # %foo -- passes ${vars[foo]} from callee to caller
+ # %foo=bar -- passes ${vars[foo]} from callee
+ # to variable ${vars[bar]} of caller
+ # <arguments> are arguments passed to the dependency function
+
+ local name outer inner
+ local -a do_command_prev
+ local -A vars_switch lift
name=$1
shift
+ # store $vars
+ vars_prev=( "${(kv)vars[@]}" )
+ vars=( )
+
+ # parse variable assignments
while $(($#)); do
- # TODO: nice regexps here
case $1 in
- (:*) vars[$c\|${1#:}]=vars[$c_prev\|${1#:}];;
- (%*) lift+=( ${1#%} );;
- (*=*) vars[$c\|${1%%=*}]=${1#*=};;
- (--) break;;
- (*) die "$name: unrecognised argument: ${(qqq)1}";;
+ (:*) vars[${1#:}]=vars_prev[${1#:}];;
+ (%*=*) lift[${${1#%}%%=*}]=${${1#%}#*=};;
+ (%*) lift[${1#%}]=${1#%};;
+ (*=*) vars[${1%%=*}]=${1#*=};;
+ (--) break;;
+ (*) die "$name: unrecognised argument: ${(qqq)1}";;
esac
shift
done
+ # store old $do_command
+ do_command_prev=( "${do_command[@]}" )
+ do_command=( confz_${name}_do )
+
+ # perform check & run
if confz_${name}_check "$@"; then
- confz_${name}_do "$@" || die "$name: failed with error $?"
+ "${do_command[@]}" "$@" || die "$name: command failed with error $?"
fi
confz_${name}_check "$@" || die "$name: check failed with error $?"
- for arg in $lift; do
- vars[$c_prev\|$arg]=vars[$c\|$arg]
+ # restore $do_command
+ do_command=( "${do_command_prev[@]}" )
+
+ # lift %variables from calee to caller
+ for outer inner in ${(kv)lift}; do
+ vars_prev[$outer]=vars[$inner]
done
- c_switch=$c
- c=$c_prev
- c_prev=$c_switch
+ # restore old $vars and put calee's $vars into $vars_prev
+ vars_switch=( "${(kv)vars[@]}" )
+ vars=( "${(kv)vars_prev[@]}" )
+ vars_prev=( "${(kv)vars_switch[@]}" )
}
+# "dependency" that represents toplevel and gets it's deps out of argv
confz_main_check() {
local arg
local -a args
- for arg in $argv; do
+ for arg in "$@"; do
if [[ $arg == ';' ]]; then
require $args
args=()
@@ -81,8 +109,10 @@ confz_main_check() {
$(( $#args )) && require $args
}
+# noop for the toplevel, functionality is in the dependencies
confz_main_do() {
true
}
-require main
+# run the toplevel
+require main -- "$@"
diff --git a/zsh-functions/confz_fs_init b/zsh-functions/confz_fs_init
@@ -1,45 +1,57 @@
# vim: ft=zsh noet ts=4 sts=4 sw=4
-confz_lv_check() {
- [[ -n ${vars[$c\|vg_name]:=$DEFAULT_VG} ]] || \
+#
+# confz functions for dealing with filesystem and mounting
+#
+
+
+# configure LVM2 logical volume
+confz_logical_volume_check() {
+ [[ -n ${vars[vg_name]:=$DEFAULT_VG} ]] || \
die "$0: DEFAULT_VG is unset and no 'vg_name' was passed"
- [[ -n ${vars[$c\|size]:=$DEFAULT_VOLUME_SIZE} ]] || \
+ [[ -n ${vars[size]:=$DEFAULT_VOLUME_SIZE} ]] || \
die "$0: DEFAULT_VOLUME_SIZE is unset and no 'size' was passed"
- [[ -n ${vars[$c\|lv_name]} ]] || \
+ [[ -n ${vars[lv_name]} ]] || \
die "$0: no 'lv_name' was passed"
- vars[$c\|device]=/dev/mapper/${DEFAULT_VG}-$1
- [[ -b ${vars[$c\|device]} ]]
-}
+ vars[device]=/dev/mapper/${DEFAULT_VG}-$1
-confz_lv_do() {
- lvcreate \
- --name ${vars[$c\|lv_name]} \
- --size ${vars[$c\|size]} \
- ${vars[$c\|vg_name]}
+ do_command=(
+ lvcreate
+ --name ${vars[lv_name]}
+ --size ${vars[size]}
+ ${vars[vg_name]}
+ )
+
+ [[ -b ${vars[device]} ]]
}
-confz_mkfs_check() {
- [[ -n ${vars[$c\|device]} ]] || \
+# create filesystem on block device
+confz_filesystem_check() {
+ [[ -n ${vars[device]} ]] || \
die "$0: no 'device' was passed"
- [[ -n ${vars[$c\|filesystem]:=$DEFAULT_FS} ]] || \
+ [[ -n ${vars[filesystem]:=$DEFAULT_FS} ]] || \
die "$0: DEFAULT_FS is unset and no 'filesystem' was passed"
- [[ -n ${vars[$c\|label]} ]] || \
+ [[ -n ${vars[label]} ]] || \
die "$0: no 'label' was passed"
- [[ -b ${vars[$c\|device]} ]] || \
- die "$0: not a block device: ${(qqq)vars[$c\|device]}"
+ [[ -b ${vars[device]} ]] || \
+ die "$0: not a block device: ${(qqq)vars[device]}"
+
+ do_command=(
+ mkfs -t ${vars[filesystem]} -L ${vars[label]} ${vars[device]}
+ )
local tries blk_out DEVNAME LABEL UUID TYPE
tries=10
while $((tries)); do
- blk_out=$(blkid -o export ${vars[$c\|device]})
+ blk_out=$(blkid -o export ${vars[device]})
case $? in
(0) break;;
(2) ;;
@@ -50,49 +62,51 @@ confz_mkfs_check() {
[[ -z $blk_out ]] && return 1 # nothing found on the device
eval $blk_out
- [[ $LABEL == ${vars[$c\|label]} && $TYPE == xfs ]] && return 0
- die "$0: filesystem already present on ${(qqq)vars[$c\|device]}!"
-}
-
-confz_mkfs_do() {
- mkfs -t ${vars[$c\|filesystem]} -L ${vars[$c\|label]} ${vars[$c\|device]}
+ [[ $LABEL == ${vars[label]} && $TYPE == xfs ]] && return 0
+ die "$0: filesystem already present on ${(qqq)vars[device]}!"
}
+# put mountpoint for device into /etc/fstab
confz_fstab_check() {
- [[ -n ${vars[$c\|device]} ]] || \
+ [[ -n ${vars[device]} ]] || \
die "$0: no 'device' was passed"
- [[ -n ${vars[$c\|mountpoint]} ]] || \
+ [[ -n ${vars[mountpoint]} ]] || \
die "$0: no 'mountpoint' was passed"
- [[ -n ${vars[$c\|filesystem]} ]] || \
+ [[ -n ${vars[filesystem]} ]] || \
die "$0: no 'filesystem' was passed"
- [[ -n ${vars[$c\|opts]} ]] || \
+ [[ -n ${vars[opts]} ]] || \
die "$0: no 'opts' was passed"
- : ${vars[$c\|dump]:=0}
- : ${vars[$c\|pass]:=2}
+ : ${vars[dump]:=0}
+ : ${vars[pass]:=2}
local device mountpoint filesystem opts dump pass
sed '/^[ \t]*#/d;s/#.*//;s/[ \t]\+/ /g' /etc/fstab | \
while read device mountpoint filesystem opts dump pass; do
- if [[ $mountpoint == ${vars[$c\|mountpoint]} ]]; then
- [[ $device == ${vars[$c\|device]} ]] || \
- die "$0: $mountpoint already present with different device"
+ if [[ $mountpoint == ${vars[mountpoint]} ]]; then
+ [[ $device == ${vars[device]} ]] || \
+ die "$0: $mountpoint already present" \
+ "with different device than ${(qqq)vars[device]}"
- [[ $filesystem == ${vars[$c\|filesystem]} ]] || \
- die "$0: $mountpoint already present with different filesystem"
+ [[ $filesystem == ${vars[filesystem]} ]] || \
+ die "$0: $mountpoint already present" \
+ "with different filesystem than ${(qqq)vars[filesystem]}"
- [[ $opts == ${vars[$c\|opts]} ]] || \
- die "$0: $mountpoint already present with different opts"
+ [[ $opts == ${vars[opts]} ]] || \
+ die "$0: $mountpoint already present" \
+ "with different opts than ${(qqq)vars[opts]}"
- [[ $dump == ${vars[$c\|dump]} ]] || \
- die "$0: $mountpoint already present with different dump"
+ [[ $dump == ${vars[dump]} ]] || \
+ die "$0: $mountpoint already present" \
+ "with different dump than ${(qqq)vars[dump]}"
- [[ $pass == ${vars[$c\|pass]} ]] || \
- die "$0: $mountpoint already present with different pass"
+ [[ $pass == ${vars[pass]} ]] || \
+ die "$0: $mountpoint already present" \
+ "with different pass than ${(qqq)vars[pass]}"
return 0 # found matching entry
fi
@@ -101,41 +115,46 @@ confz_fstab_check() {
}
confz_fstab_do() {
- print -r - >>/etc/fstab "${vars[$c\|device]} ${vars[$c\|mountpoint]} ${vars[$c\|filesystem]} ${vars[$c\|opts]} ${vars[$c\|dump]} ${vars[$c\|pass]}"
+ print -r - >>/etc/fstab "${vars[device]} ${vars[mountpoint]} ${vars[filesystem]} ${vars[opts]} ${vars[dump]} ${vars[pass]}"
}
+# make device mounted on mountpoint
confz_mounted_check() {
- [[ -n ${vars[$c\|device]} ]] || \
+ [[ -n ${vars[device]} ]] || \
die "$0: no 'device' was passed"
- [[ -n ${vars[$c\|mountpoint]} ]] || \
+ [[ -n ${vars[mountpoint]} ]] || \
die "$0: no 'mountpoint' was passed"
- grep -q "^${vars[$c\|device]} ${vars[$c\|mountpoint]} " /proc/mounts
+ grep -q "^${vars[device]} ${vars[mountpoint]} " /proc/mounts
}
-confz_mounted_do() {
- mount ${vars[$c\|device]} ${vars[$c\|mountpoint]}
+confz_mounted_check() {
+ mkdir -p ${vars[mountpoint]} || return $?
+ mount "$@" ${vars[device]} ${vars[mountpoint]}
}
+# create LVM2 logical volume, and make sure it's in fstab and mounted
confz_mounted_volume_check() {
- [[ -n ${vars[$c\|lv_name]} ]] || \
+ [[ -n ${vars[lv_name]} ]] || \
die "$0: no 'lv_name' was passed"
- [[ -n ${vars[$c\|mountpoint]} ]] || \
+ [[ -n ${vars[mountpoint]} ]] || \
die "$0: no 'mountpoint' was passed"
- [[ -n ${vars[$c\|size]} ]] || \
+ [[ -n ${vars[size]} ]] || \
die "$0: no 'size' was passed"
- : ${vars[$c\|filesystem]:=xfs}
- : ${vars[$c\|opts]:=noatime}
- : ${vars[$c\|label]:=${vars[$c\|lv_name]}}
+ : ${vars[filesystem]:=xfs}
+ : ${vars[opts]:=noatime}
+ : ${vars[label]:=${vars[lv_name]}}
- require lv %device :vg_name :size :lv_name
- require mkfs :device :label :filesystem
+ require logical_volume %device :vg_name :size :lv_name
+ require filesystem :device :label :filesystem
require fstab :device :mountpoint :filesystem :opts :dump :pass
require mounted :device :mountpoint
+
+ do_command=( true )
}
diff --git a/zsh-functions/confz_vserver_init b/zsh-functions/confz_vserver_init
@@ -0,0 +1,75 @@
+# vim: ft=zsh noet ts=4 sts=4 sw=4
+
+#
+# confz functions for deploying vservers
+#
+
+
+# deploy filesystem image from directory, rsync or tarball
+confz_deployed_system_check() {
+ [[ -n ${vars[path]} ]] || \
+ die "$0: no 'path' was passed"
+
+ [[ -n ${vars[source]} ]] || \
+ die "$0: no 'source' was passed"
+
+ if [[ ${vars[source]} == */ ]]; then
+ do_command=( rsync -aAH ${vars[source]} ${vars[path]}/ )
+ else
+ do_command=( tar -pC ${vars[path]} -f ${vars[source]} )
+ fi
+
+ [[ -e ${vars[path]}/bin ]]
+}
+
+
+
+# configure and deploy vserver
+confz_vserver_check() {
+ [[ -n ${vars[name]} ]] || \
+ die "$0: no 'name' was passed"
+
+ [[ -n ${vars[size]} ]] || \
+ die "$0: no 'size' was passed"
+
+ [[ -n ${vars[context_id]} ]] || \
+ die "$0: no 'context_id' was passed"
+
+ [[ -n ${vars[source]} ]] || \
+ die "$0: no 'source' was passed"
+
+ vars[etcdir]=/etc/vservers/${vars[name]}
+ vars[vdir]=/vservers/${vars[name]}
+
+ local ctx run
+ run=1
+
+ if [[ -e ${vars[etcdir]}/context ]]; then
+ if [[ $(<${vars[etcdir]}/context) == ${vars[context_id]} ]]; then
+ run=0
+ else
+ die "$0: vserver ${(qqq)vars[name]} " \
+ "has context different from ${(qqq)vars[context_id]}"
+ fi
+ for ctx in /etc/vservers/*/context; do
+ if [[ $(<$ctx) == ${vars[context_id]} ]]; then
+ die "$0: context id already used by $ctx"
+ fi
+ done
+ fi
+ [[ -l ${vars[etcdir]}/vdir ]] || run=1
+
+ require mounted_volume :name :size :filesystem mountpoint=${vars[vdir]}
+ require deployed_system :source path=${vars[vdir]}
+
+ return $run
+}
+
+confz_vserver_do() {
+ mkdir -p ${vars[etcdir]} || return $?
+ if [[ -l ${vars[etcdir]}/vdir ]]; then
+ rm ${vars[etcdir]}/vdir || return $?
+ ln -s $vars[vdir] ${vars[etcdir]}/vdir || return $?
+ fi
+ print -r - ${vars[context_id]} >${vars[etcdir]}/context
+}