From ea072138dd9858df190579b3757ebd4d2c4f810e Mon Sep 17 00:00:00 2001 From: mrjk Date: Mon, 14 Jul 2025 21:11:40 -0400 Subject: [PATCH] add: libvirt_server role from mrjk.debian_libvirt --- roles/libvirt_server/defaults/main.yml | 3 + roles/libvirt_server/files/bashrc | 555 ++++++++++++++++++ .../files/virt-disk-template.sh | 309 ++++++++++ roles/libvirt_server/handlers/main.yml | 10 + roles/libvirt_server/tasks/main.yml | 67 +++ 5 files changed, 944 insertions(+) create mode 100644 roles/libvirt_server/defaults/main.yml create mode 100644 roles/libvirt_server/files/bashrc create mode 100755 roles/libvirt_server/files/virt-disk-template.sh create mode 100644 roles/libvirt_server/handlers/main.yml create mode 100644 roles/libvirt_server/tasks/main.yml diff --git a/roles/libvirt_server/defaults/main.yml b/roles/libvirt_server/defaults/main.yml new file mode 100644 index 0000000..f9571a4 --- /dev/null +++ b/roles/libvirt_server/defaults/main.yml @@ -0,0 +1,3 @@ + +libvirt_server_group_name: libvirt +libvirt_server_group_members: [] diff --git a/roles/libvirt_server/files/bashrc b/roles/libvirt_server/files/bashrc new file mode 100644 index 0000000..98c6316 --- /dev/null +++ b/roles/libvirt_server/files/bashrc @@ -0,0 +1,555 @@ +########################## +# Initialisation +########################## + +# To live try: +# source <( curl https://raw.githubusercontent.com/mrjk/linux-personal-env/master/bash/bash.bashrc) +# To install locally +# curl https://raw.githubusercontent.com/mrjk/linux-personal-env/master/bash/bash.bashrc > ~/.bashrc + +# If not running interactively, don't do anything +case $- in + *i*) + ;; + *) + return + ;; +esac + + +########################## +# Func: Global variables +########################## + +shell_global_variable () { + HOSTNAME=$(head -n 1 /etc/hostname) + + # Custom variables + ########################## + + # Regex to match IP + RGX_IP='(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' + RGX_IP='([0-2][0-9]{2}\.){3}' + + # Regex to match emplty lines and comments ... use with grep -E -v + RGX_EL='^[[:blank:]]*#|^$' + + # Match a username + RGX_USER='[a-z0-9-]' + + # Match a domain ANSI + RGX_DOM='([a-z][a-z0-9\-]+(\.|\-*\.))+[a-z]{2,6}' + RGX_DOM='[a-z0-9-]+(\.[a-z0-9-]+)?\.[a-z0-9-]{2,6}' + + # Match Hexadecimal, + RGX_HEX='#?([a-f0-9]{6}|[a-f0-9]{3})' + + # Match email + RGX_EMAIL='([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})' + + # Match URL + RGX_URL='(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?' +} + + +########################## +# Func: Global color +########################## + +shell_global_color () { + # Easy + RED='\[\033[31m\]' + GREEN='\[\033[32m\]' + YELLOW='\[\033[33m\]' + BLUE='\[\033[34m\]' + PURPLE='\[\033[35m\]' + CYAN='\[\033[36m\]' + WHITE='\[\033[37m\]' + NIL='\[\033[00m\]' + + # Reset + Color_Off='\e[0m' # Text Reset + + # Regular Colors + Black='\e[0;30m' # Black + Red='\e[0;31m' # Red + Green='\e[0;32m' # Green + Yellow='\e[0;33m' # Yellow + Blue='\e[0;34m' # Blue + Purple='\e[0;35m' # Purple + Cyan='\e[0;36m' # Cyan + White='\e[0;37m' # White + + # Bold + BBlack='\e[1;30m' # Black + BRed='\e[1;31m' # Red + BGreen='\e[1;32m' # Green + BYellow='\e[1;33m' # Yellow + BBlue='\e[1;34m' # Blue + BPurple='\e[1;35m' # Purple + BCyan='\e[1;36m' # Cyan + BWhite='\e[1;37m' # White + + # Underline + UBlack='\e[4;30m' # Black + URed='\e[4;31m' # Red + UGreen='\e[4;32m' # Green + UYellow='\e[4;33m' # Yellow + UBlue='\e[4;34m' # Blue + UPurple='\e[4;35m' # Purple + UCyan='\e[4;36m' # Cyan + UWhite='\e[4;37m' # White + + # Background + On_Black='\e[40m' # Black + On_Red='\e[41m' # Red + On_Green='\e[42m' # Green + On_Yellow='\e[43m' # Yellow + On_Blue='\e[44m' # Blue + On_Purple='\e[45m' # Purple + On_Cyan='\e[46m' # Cyan + On_White='\e[47m' # White + + # High Intensity + IBlack='\e[0;90m' # Black + IRed='\e[0;91m' # Red + IGreen='\e[0;92m' # Green + IYellow='\e[0;93m' # Yellow + IBlue='\e[0;94m' # Blue + IPurple='\e[0;95m' # Purple + ICyan='\e[0;96m' # Cyan + IWhite='\e[0;97m' # White + + # Bold High Intensity + BIBlack='\e[1;90m' # Black + BIRed='\e[1;91m' # Red + BIGreen='\e[1;92m' # Green + BIYellow='\e[1;93m' # Yellow + BIBlue='\e[1;94m' # Blue + BIPurple='\e[1;95m' # Purple + BICyan='\e[1;96m' # Cyan + BIWhite='\e[1;97m' # White + + # High Intensity backgrounds + On_IBlack='\e[0;100m' # Black + On_IRed='\e[0;101m' # Red + On_IGreen='\e[0;102m' # Green + On_IYellow='\e[0;103m' # Yellow + On_IBlue='\e[0;104m' # Blue + On_IPurple='\e[0;105m' # Purple + On_ICyan='\e[0;106m' # Cyan + On_IWhite='\e[0;107m' # White +} + + +########################## +# Func: shell PS1 +########################## + +# Full function wrapper +shell_ps1 () { + shell_ps1_advanced + #shell_ps1_simple +} + +# PS1 shell reset (in case of emergency) +shell_ps1_simple () { + PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' + #PS1='$USER@$(hostname):$PWD\$ ' +} + +# Full function +shell_ps1_advanced () { + + # Define dynamic prompt variables (Fucking bashisms :-() + local PS1_RETURN="\$( + PS1_EXIT=\$?; + [[ \$PS1_EXIT == 0 ]] || echo -n \"\[$Red\]\${PS1_EXIT}\[${Color_Off}\] \" + )" + + local PS1_PATH="\$( + if [ -s \"\${PWD}\" ] ; then + # PS1_DF=\$(command df -P \"\${PWD}\" | grep -E -o '[0-9]{1,3}%' | grep -E -o '[0-9]{1,3}'); + PS1_DF=\$(command timeout 1s df -P \"\${PWD}\" | awk 'END {print \$5} {sub(/%/,\"\")}'); + + if [ \"\${PS1_DF:-0}\" -gt 95 ]; then + PS1_PATH=\"\[$Red\]:\" + elif [ \"\${PS1_DF:-0}\" -gt 90 ]; then + PS1_PATH=\"\[$Yellow\]:\" + else + PS1_PATH=\"\[$White\]:\" + fi + else + # Current directory is size '0' (like /proc, /sys etc). + PS1_PATH=\"\[$Yellow\]:\"; + fi + if [ -w \"\${PWD}\" ] ; then + PS1_PATH=\"\${PS1_PATH}\[$Blue\]\w\" + else + # No 'write' privilege in the current directory. + PS1_PATH=\"\${PS1_PATH}\[$Yellow\]\w\" + fi + + echo -e \"\${PS1_PATH}\" + )" + + # Get jobs + local PS1_JOBS="\$( + PS1_JOBS=''; + PS1_JOBS_RUNNING=\$(jobs -r | wc -l); + PS1_JOBS_STOPPED=\$(jobs -s | wc -l); + if [ \${PS1_JOBS_RUNNING} -gt 0 ] || [ \${PS1_JOBS_STOPPED} -gt 0 ] + then + if [ \${PS1_JOBS_RUNNING:-0} -gt 0 ]; then + PS1_JOBS_RUNNING=\"\[$Green\]\${PS1_JOBS_RUNNING}\[$Color_Off\]\" + else + PS1_JOBS_RUNNING='' + fi + + if [ \${PS1_JOBS_STOPPED:-0} -gt 0 ]; then + PS1_JOBS_STOPPED=\"\[$Yellow\]\${PS1_JOBS_STOPPED}\[$Color_Off\]\" + else + PS1_JOBS_STOPPED='' + fi + PS1_JOBS=\"\${PS1_JOBS_RUNNING}:\${PS1_JOBS_STOPPED} \"; + else + PS1_JOBS='' + fi + echo -e \"\${PS1_JOBS}\"; + )" + + # Time execution checker + + # Maximal time to consider prompt as slow in ms + local PS1_MAX_EXEC_TIME=500 + # Number of time needed before swithing to basic prompt + local PS1_MAX_TIME=3 + # Time windows to check + local PS1_MAX_EXEC_DELAY=60 + # Time before reloading full PS1 after showing simple prompt + local PS1_DELAY_RELOAD=300 + + # Debug +# local PS1_MAX_EXEC_TIME=5 +# local PS1_MAX_TIME=3 +# local PS1_MAX_EXEC_DELAY=60 +# local PS1_DELAY_RELOAD=30 + + local PS1_TMP_FILE=/tmp/.load-$(whoami) + chown $(whoami):$(whoami) ${PS1_TMP_FILE} 2>/dev/null + local PS1_START="\$( + if [ -f ${PS1_TMP_FILE} ] && [ \"\$(cat ${PS1_TMP_FILE} | grep -E -o '^reset')\" = \"reset\" ] + then + echo '\u@\h:\[\033[01;34m\]\w\[\033[00m\]\\$ ' + if [ \$((\$(date +%s) - \$(stat -c %Y ${PS1_TMP_FILE}) )) -gt ${PS1_DELAY_RELOAD} ] + then + echo -n \"Shell: full prompt reactivated.\n\" + > ${PS1_TMP_FILE}; + fi + else + ts=\$(date +%s%N); + echo -e \"" + local PS1_STOP="\"; + tt=\$(((\$(date +%s%N) - \${ts})/1000000)); + if [ \${tt} -gt ${PS1_MAX_EXEC_TIME} ] + then + echo 1 >> ${PS1_TMP_FILE}; + if [ \$((\$(date +%s) - \$(stat -c %Y ${PS1_TMP_FILE}) )) -gt ${PS1_MAX_EXEC_DELAY} ] || [ \$(wc -l ${PS1_TMP_FILE} | grep -E -o '^[0-9]{1,3}') -gt ${PS1_MAX_TIME} ] + then + echo "reset" > ${PS1_TMP_FILE}; + echo -n \"Shell: prompt is taking more than ${PS1_MAX_EXEC_TIME}ms to anwser. Normal prompt will be reactivated in ${PS1_DELAY_RELOAD}s. Execute 'rm ${PS1_TMP_FILE}' to force.\n\" ; + fi + fi + fi + )" + + # Define static prompt variables + local PS1_ACOUNT="\[$White\]\\$ " + local PS1_CHROOT_DEB="${debian_chroot:+($debian_chroot)}" + + # Set variable identifying the chroot you work in (used in the prompt below) + if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + local debian_chroot=$(cat /etc/debian_chroot) + fi + + # Define prompt depending user + if [ $(id -u) -eq 0 ]; + then # you are root, make the prompt red + # Are you root ? + local PS1_USER="\[$Green\]\u" + # In green + elif [ -n "$(cat /etc/passwd | grep $(whoami) | grep -E -v ':/bin/(ba|z|t)?sh')" ]; then + # The you are a no login user ... + local PS1_USER="\[$Red\]\u" + # In red + elif [ $(id -u) -lt 1000 ]; then + # Are you a system user ? + local PS1_USER="\[$Yellow\]\u" + # In orange + elif [ $(id -u) -ge 1000 ]; then + # Are you a regular user ? + local PS1_USER="\[$White\]\u" + # In white + fi + + # Detect serial connection (virtualisation, ttySx) + if [ $(ps ax | grep $$ | awk '{ print $2 }' | grep 'ttyS.' | wc -l ) -gt 0 ]; then + local PS1_HOST="@\[$Red\]\h" + else + local PS1_HOST="\[$Green\]@\h" + fi + + # Set the prompt depending the shell + if [ "${CURRENT_SHELL}" = "bash" ]; then + PS1="\[$Color_Off\]${PS1_RETURN}${PS1_START}${PS1_JOBS}${PS1_USER}${PS1_HOST}${PS1_PATH}${PS1_ACOUNT}\[$Color_Off\]${PS1_STOP}" + elif [ "${CURRENT_SHELL}" = "dash" ] ; then + PS1='$USER@$HOSTNAME:$PWD\$ ' + else + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' + fi +} + + +########################## +# Func: Bash alias +########################## + +bash_alias () { + alias ll='ls -lh' + alias la='ls -lAh' + alias l='ls -ClFh' + alias ltr='ls -ahltr' + alias lsd="ls -l | grep ^d" + + alias mkdir='mkdir -p' + + alias ..='cd ..' + alias ...='cd ../..' + alias ....='cd ../..' + + alias h='history' + alias j='jobs -l' + + alias vih='vim /etc/hosts ' + alias vit='vim /etc/fstab ' + alias vif='vim /etc/network/interfaces ' + alias diff='colordiff ' + alias tmount='mount |column -t ' + + alias wgets='wget --no-check-certificate ' + alias wgeth='wget --no-check-certificate -S -O /dev/null ' + alias uncmt="grep '^[^#|^$|^ *$]' " + alias monip='wget -q -O - "$@" monip.org | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}"' + alias monhost='echo "My host is ..."; host $(wget -q -O - "$@" monip.org | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}")' + alias text2ascii='tail --bytes=+4' + + alias iptlist='sudo /sbin/iptables -L -n -v --line-numbers' + alias iptlistin='sudo /sbin/iptables -L INPUT -n -v --line-numbers' + alias iptlistout='sudo /sbin/iptables -L OUTPUT -n -v --line-numbers' + alias iptlistfw='sudo /sbin/iptables -L FORWARD -n -v --line-numbers' + + # Typo correction + alias cd..='cd ..' + + # List directory when moving + #cd() { builtin cd "$@"; ll; } + + # Chroot helper + chroot_mount_system () { + if [ -d $1 ]; then + mount -t proc none $1/proc + mount -obind /dev $1/dev + mount -obind /sys $1/sys + echo "Then: chroot $1 /bin/bash " + else + echo "Usage: chroot_mount_system " + fi + } + + # SSH Helper + alias ssh_unsecure='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no CheckHostIP=no' + alias ssh_local='ssh -o CheckHostIP=no ' +} + + +########################## +# Func: Command not found +########################## + +bash_completion () { + + # SSH Host completion + if [ -e ~/.ssh/known_hosts ] ; then + complete -W "$(echo `cat ~/.ssh/known_hosts | cut -f 1 -d ' ' | sed -e s/,.*//g | uniq | grep -v "\["`;)" ssh + # Note: HashKnownHosts need to be set 'no' in /etc/ssh/ssh_config + fi + +} + + +########################## +# Func: Command not found +########################## + +shell_command_not_found () { + # if the command-not-found package is installed, use it + if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then + command_not_found_handle () { + # check because c-n-f could've been removed in the meantime + if [ -x /usr/lib/command-not-found ]; then + /usr/bin/python /usr/lib/command-not-found -- "$1" + return $? + elif [ -x /usr/share/command-not-found/command-not-found ]; then + /usr/bin/python /usr/share/command-not-found/command-not-found -- "$1" + return $? + else + printf "%s: command not found\n" "$1" >&2 + return 127 + fi + } + fi +} + +########################## +# Func: Bash command color +########################## + +bash_command_color () { + + if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + alias dir='dir --color=auto' + alias vdir='vdir --color=auto' + + alias grep='grep --color=auto' + alias fgrep='fgrep --color=auto' + alias egrep='egrep --color=auto' + fi + + + if [ -x /usr/bin/colordiff ]; then + alias diff='colordiff' + fi + + + # Pager config and colors + #export MANPAGER='/usr/bin/most -S' + export MANPAGER='/usr/bin/less' + + LESS_TERMCAP_mb=$'\E[01;31m' + LESS_TERMCAP_us=$'\E[01;32m' + LESS_TERMCAP_md=$'\E[01;31m' + LESS_TERMCAP_se=$'\E[0m' + LESS_TERMCAP_so=$'\E[01;44;33m' + LESS_TERMCAP_ue=$'\E[0m' + LESS_TERMCAP_me=$'\E[0m' + + + # Ajout log en couleurs + ctail() { tail -f $1 | ccze -A; } + cless() { ccze -A < "$1" | less -R; } +} + + + +########################## +# Func: Bash config +########################## + + +bash_config () { + ########################## + # History management + ########################## + + # Force timestamp in history + export HISTTIMEFORMAT='%F %T ' + + # Ignore duplicates and lines starting by a space + export HISTCONTROL="" + + # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) + export HISTSIZE=10000 + export HISTFILESIZE=2000 + + if [ -w ~/.bash_history ]; then +# echo "Bash: enabling history file" + export HISTFILE=~/.bash_history + else +# echo "Bash: disabling history file" + unset HISTFILE + fi + + + ########################## + # Misc + ########################## + + # Disable mail check + unset MAILCHECK + + # Update shell output according to the terminal size + shopt -s checkwinsize + + # Load other aliases + if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases + fi + + # Synchronise history + shopt -s histappend + export PROMPT_COMMAND="history -a" + + # Enable completion + if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi + fi + + # Bash correction + shopt -s autocd + shopt -s cdspell + shopt -s checkjobs + shopt -s hostcomplete + shopt -s nocaseglob +} + + + +########################## +# Function calls +########################## + +# Checking the shell +case $(readlink -f $SHELL) in + */zsh) + CURRENT_SHELL="zsh" + ;; + */bash) + CURRENT_SHELL="bash"; + ;; + */dash) + CURRENT_SHELL="dash"; + ;; + *) + # assume something else + CURRENT_SHELL="none" +esac + +# Preset +shell_global_variable +shell_global_color +shell_ps1 +shell_command_not_found + +bash_alias +bash_command_color + +if [ ${CURRENT_SHELL} = "bash" ]; then + bash_config + bash_completion +fi diff --git a/roles/libvirt_server/files/virt-disk-template.sh b/roles/libvirt_server/files/virt-disk-template.sh new file mode 100755 index 0000000..f78d610 --- /dev/null +++ b/roles/libvirt_server/files/virt-disk-template.sh @@ -0,0 +1,309 @@ +#!/bin/bash + + + + +# This function help to convert measures Bytes to/from Bits +# Usage: [K|M|G|T]Byte|bit [K|M|G|T]Byte|bit +function _convert_bit { + + echo $1 $2 | awk -F ' ' ' { + + # Manage arguments: in + ################### + i_number=gensub(/^([0-9]+(.[0-9]+)?)([kgmtKMGT]?)(.*)$/, "\\1", "g", $1); + i_factor=gensub(/^([0-9]+(.[0-9]+)?)([kgmtKMGT]?)(.*)$/, "\\3", "g", $1); + i_unit=gensub(/^([0-9]+(.[0-9]+)?)([kgmtKMGT]?)(.*)$/, "\\4", "g", $1); + + + # Manage arguments: out + ################### + o_factor=gensub(/^([kgmtKMGT]?)(.*)$/, "\\1", "g", $2); + o_unit=gensub(/^([kgmtKMGT]?)(.*)$/, "\\2", "g", $2); + + + # Replace factor: in + ################### + switch (toupper(i_factor)) { + case "K": i_number=i_number * 1024 ; break + case "M": i_number=i_number * (1024 ^ 2 ) ; break + case "G": i_number=i_number * (1024 ^ 3 ) ; break + case "T": i_number=i_number * (1024 ^ 4 ) ; break + } + + + # Convert Bytes to Bits if necessary: in + ################### + if ( i_unit ~ /^B$|^[Bb]ytes?$/ || i_unit ~ /^[Oo](ctets?)?$/ ) + i_number = i_number * 8; + else if ( i_unit ~ /^$/ || i_unit ~ /^(b|[b|B]its?)$/ ) {} + else { + print "Error: " $1 " is not a valid argument."; + exit 1 + } + + + # Remove factor: out + ################### + switch (toupper(o_factor)) { + case "K": i_number=i_number / 1024 ; break + case "M": i_number=i_number / (1024 ^ 2 ) ; break + case "G": i_number=i_number / (1024 ^ 3 ) ; break + case "T": i_number=i_number / (1024 ^ 4 ) ; break + } + + # Convert Bits to Bytes if necessary: out + ################### + if ( o_unit ~ /^B$|^[Bb]ytes?$/ || o_unit ~ /^[Oo](ctets?)?$/ ) { + print i_number / 8 + } + else if ( o_unit ~ /^$/ || o_unit ~ /^(b|[b|B]its?)$/ ) { + print i_number + } + else { + print "Error: " o_unit " is not a valid argument."; + exit 1 + } + + + }' +} + +# This function helps to colorize console output +_shell_color () { + # Easy + RED='\033[31m' + GREEN='\033[32m' + YELLOW='\033[33m' + BLUE='\033[34m' + PURPLE='\033[35m' + CYAN='\033[36m' + WHITE='\033[37m' + NIL='\033[00m' +} + + +# This function display an error and quit +function _crit { + local RC=$1 + shift 1 + local MSG=$@ + + _log crit "$MSG" + exit $RC +} + +# This function display log messages +function _log { + + # Init + local CODE=${1,,} + shift 1 + local MSG=$@ + local COLOR="" + + # Detect level + case $CODE in + 0|emerg) + CODE="emerg" + COLOR=${RED} + ;; + 1|alert) + CODE="alert" + COLOR=${RED} + ;; + 2|crit) + CODE="crit" + COLOR=${RED} + ;; + 3|error|err) + CODE="error" + COLOR=${RED} + ;; + 4|warning|warn) + CODE="warn" + COLOR=${YELLOW} + ;; + 5|notice) + CODE="notice" + COLOR=${GREEN} + ;; + 6|info) + CODE="info" + COLOR=${CYAN} + ;; + 7|debug|*) + CODE="debug" + COLOR=${BLUE} + ;; + esac + + # Display message + echo $CODE | grep -E "$LOG_LEVEL" >/dev/null ; local RC=$? + if [ $RC -eq 0 ]; then + printf "${COLOR}%-6s: %s${NIL}\n" "${CODE^^}" "$MSG" + + fi +} + + +function _help { + +echo "Aide: " +} + +# Usage: +# bin [-f] [-s ] [-t ] src_template dst_disk +# -f : force +# -s 15G : size +# -t qcow|qcow2|raw : type of image +# -c (compress) +# -p (show progress) +# -i/-o +# + + +#convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename + + + + + +# This function will create the volume instances +function virt_disk_template { + + # Define default variables + ################ + local DISK_SRC= + local DISK_DEST= + + local DISK_DEST_SIZE=0 + local DISK_TYPE=qcow2 + + local DISK_COMPRESS=0 + local DISK_PROGRESS=0 + local DISK_FORCE=0 + local DISK_HELP=0 + + + local QEMU_IMG_BIN="$(whereis -b qemu-img | cut -d ' ' -f 2)" + local QEMU_IMG_OPT="" + + + # Get options + ################ + unset OPTIND + while getopts o:i:t:s:fcp flag; do + + case $flag in + # Required variables + o) + DISK_DEST=${OPTARG} + ;; + i) + DISK_SRC=${OPTARG} + ;; + t) + DISK_TYPE=${OPTARG} + ;; + s) + DISK_DEST_SIZE="${OPTARG}" + ;; + f) + DISK_FORCE=1 + ;; + c) + DISK_COMPRESS=1 + ;; + p) + DISK_PROGRESS=1 + ;; + *) + _crit 1 "Argument $flag is not valid" + esac + + done + shift $(( OPTIND - 1 )); + + + + # Check the file i/o + ################ + if [[ -z "${DISK_SRC}" || ! -f "${DISK_SRC}" ]]; then + _crit 1 "Source image $DISK_SRC is not valid" + fi + if [[ -z "${DISK_DEST}" ]]; then + _crit 1 "Destination image is empty" + fi + + + # Get the template size (in G) + ################ + + # get the size in BYTES ! + local DISK_SRC_SIZE=$( qemu-img info ${DISK_SRC} \ + | grep "virtual size" \ + | awk '{ print gensub( /.*\((.*)bytes\)$/ , "\\1", "g" ) }' ) + + if [ -z "$DISK_SRC_SIZE" ]; then + _log error "The output of qemu-img info ${DISK_SRC} may not return size in Gb?" + _crit 1 "Impossible to get the disk template size" + fi + + + # Define options + ################ + if [ ${DISK_COMPRESS} -eq 1 ]; then + QEMU_IMG_OPT="${QEMU_IMG_OPT} -c" + fi + if [ ${DISK_PROGRESS} -eq 1 ]; then + QEMU_IMG_OPT="${QEMU_IMG_OPT} -p" + fi + + + # Set the final disk size + ################ + DISK_DEST_SIZE=$(_convert_bit ${DISK_DEST_SIZE} bit); RC=$? + + if [ $RC -ne 0 ]; then + _crit 1 "Requested size for disk ${DISK_DEST} is not valid: ${DISK_DEST_SIZE}" + else + if [[ "${DISK_DEST_SIZE}" -ne 0 && "${DISK_DEST_SIZE}" -lt "${DISK_SRC_SIZE}" ]]; then + _log warn "Requested disk size for ${DISK_DEST} is lesser than template. Disk size set to $( _convert_bit ${DISK_SRC_SIZE} Gb)Gb" + else + QEMU_IMG_OPT="${QEMU_IMG_OPT} -S ${DISK_DEST_SIZE}" + fi + fi + + + # Create the disk + ################ + if [ "$DISK_TYPE" == "qcow2" ]; then + + + # Check if the destination exists and is writable + if [[ $DISK_FORCE -ne 1 && -f "$DISK_DEST" ]]; then + _crit 1 "The destination file already exists: $DISK_DEST" + else + _log info "The VM disk will be created in $DISK_DEST" + fi + + # Create disk + local CMD="qemu-img convert \ + ${QEMU_IMG_OPT} \ + -O qcow2 \ + ${DISK_SRC} ${DISK_DEST}" + _log DEBUG "$CMD" + $CMD + + fi + +} + + +if [[ -z "${@}" || "${1}" =~ ^--?h(elp)?$ ]]; then + _help +else + virt_disk_template $@ +fi + diff --git a/roles/libvirt_server/handlers/main.yml b/roles/libvirt_server/handlers/main.yml new file mode 100644 index 0000000..87487ee --- /dev/null +++ b/roles/libvirt_server/handlers/main.yml @@ -0,0 +1,10 @@ +--- + +- name: Remove KVM module + shell: modprobe -r kvm_intel + ignore_errors: yes + +- name: Add KVM module + shell: modprobe kvm_intel + ignore_errors: yes + diff --git a/roles/libvirt_server/tasks/main.yml b/roles/libvirt_server/tasks/main.yml new file mode 100644 index 0000000..87d1e37 --- /dev/null +++ b/roles/libvirt_server/tasks/main.yml @@ -0,0 +1,67 @@ +--- + +- name: Display libvirt_server role + ansible.builtin.debug: + var: role_config + vars: + role_config: + tasks: + - Install libvirt deamon packages + - Start and enable libvirt deamon + - "Features: nested qemu" + - "Allowed {{ libvirt_server_group_name }} users: {{ libvirt_server_group_members | join(' ') }}" + tags: + - config_show + + +################################ +# Base host setup +################################ + +- name: Install base package + package: + state: present + name: + - libvirt-daemon + - libvirt-daemon-system + - libvirt-clients + - python3-libvirt + - python3-lxml + - rsync + +# - libvirt-python +# - lsof +# - policycoreutils-python +# - python-lxml +# - qemu-kvm +# - wget + +################################ +# Configure the host as libvirt hypervisor +################################ + +- name: Enable libvirt + service: + name: libvirtd + state: started + enabled: true + ignore_errors: "{{ ansible_check_mode }}" + +- name: Copy local binaries + copy: + src: virt-disk-template.sh + dest: /usr/local/bin/virt-disk-template + mode: 0755 + +################################ +# Configure group permissions +################################ + +- name: Ensure user belongs to specified group + user: + name: "{{ item }}" + groups: "{{ libvirt_server_group_name }}" + append: yes + loop: "{{ libvirt_server_group_members }}" + +