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 }}"
+
+