confz

git mirror of https://ccx.te2000.cz/bzr/confz
git clone https://ccx.te2000.cz/git/confz
Log | Files | Refs

commit f9a2a7b460f387794c7903d2f1f8ca2f0cf29947
parent c94ce73d34773a007e88bcee9b4f9c2126ec58b5
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date:   Thu, 19 Jun 2014 16:42:02 +0200

vserver deployment functions
Diffstat:
Mbin/confz | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mzsh-functions/confz_fs_init | 131+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Azsh-functions/confz_vserver_init | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 +}