diff --git a/README.md b/README.md index 3cf11a9..4c5a7b8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ WebVirtCloud is a virtualization web interface for admins and users. It can dele ## Quick Install with Installer (Beta) -Install an OS and run specified commands. Installer supported OSes: Ubuntu 20.04/22.04/24.04, Debian 10/11/12, Rocky/Alma/OEL/RHEL 10. +Install an OS and run specified commands. Installer supported OSes: Ubuntu 20.04/22.04/24.04, Debian 10/11/12, Rocky/Alma/OEL/RHEL 9/10, openSUSE Leap 15.x / Tumbleweed, and SLES 15. It can be installed on a virtual machine, physical host or on a KVM host. ```bash @@ -169,6 +169,26 @@ python manage.py migrate python manage.py runserver 0.0.0.0:8000 ``` +#### openSUSE Leap 15.x / Tumbleweed / SLES 15: +```bash +# 1. Install system prerequisites +sudo zypper --non-interactive install -y git python3-devel python3-pip python3-virtualenv libvirt-devel python3-libvirt python3-lxml openldap2-devel cyrus-sasl-devel libopenssl-devel libxslt-devel libxml2-devel gcc pkg-config + +# 2. Create virtual environment with system site packages +python3 -m venv --system-site-packages .venv +source .venv/bin/activate + +# 3. Install Python dependencies +pip install -r conf/requirements.txt +pip install -r dev/requirements.txt + +# 4. Initialize configuration and run local dev server +cp webvirtcloud/settings.py.template webvirtcloud/settings.py +sed -i -E 's/SECRET_KEY = .*/SECRET_KEY = "'$(python3 conf/runit/secret_generator.py)'"/' webvirtcloud/settings.py +python manage.py migrate +python manage.py runserver 0.0.0.0:8000 +``` + #### Configure the supervisor for RHEL Based OS Add the following after the [include] line (after **files = ...** actually): diff --git a/conf/nginx/suse_nginx.conf b/conf/nginx/suse_nginx.conf new file mode 100644 index 0000000..d698de3 --- /dev/null +++ b/conf/nginx/suse_nginx.conf @@ -0,0 +1,30 @@ +# Nginx configuration for openSUSE and SUSE Linux Enterprise Server (SLES) +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Load modular configuration files from the /etc/nginx/conf.d and vhosts.d directories + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/vhosts.d/*.conf; +} diff --git a/dev/libvirt-bootstrap.sh b/dev/libvirt-bootstrap.sh index 11a7a9b..68c85ca 100644 --- a/dev/libvirt-bootstrap.sh +++ b/dev/libvirt-bootstrap.sh @@ -160,11 +160,11 @@ __gather_linux_system_info() { # Let's convert CamelCase to Camel Case DISTRO_NAME=$(__camelcase_split "$DISTRO_NAME") fi - if [ "${DISTRO_NAME}" = "openSUSE project" ]; then + if [ "${DISTRO_NAME}" = "openSUSE project" ] || [ "${DISTRO_NAME}" = "openSUSE" ] || [ "${DISTRO_NAME}" = "opensuse" ]; then # lsb_release -si returns "openSUSE project" on openSUSE 12.3 DISTRO_NAME="opensuse" fi - if [ "${DISTRO_NAME}" = "SUSE LINUX" ]; then + if [ "${DISTRO_NAME}" = "SUSE LINUX" ] || [ "${DISTRO_NAME}" = "SLES" ] || [ "${DISTRO_NAME}" = "sles" ]; then # lsb_release -si returns "SUSE LINUX" on SLES 11 SP3 DISTRO_NAME="suse" fi @@ -223,8 +223,8 @@ __gather_linux_system_info() { done < /etc/"${rsource}" ;; os ) - nn=$(grep '^ID=' /etc/os-release | sed -e 's/^ID=\(.*\)$/\1/g') - rv=$(grep '^VERSION_ID=' /etc/os-release | sed -e 's/^VERSION_ID=\(.*\)$/\1/g') + nn=$(grep '^ID=' /etc/os-release | sed -e 's/^ID=\(.*\)$/\1/g' | tr -d '"'\'') + rv=$(grep '^VERSION_ID=' /etc/os-release | sed -e 's/^VERSION_ID=\(.*\)$/\1/g' | tr -d '"'\'') [ "${rv}x" != "x" ] && v=$(__parse_version_string "$rv") || v="" case $(echo "${nn}" | tr '[:upper:]' '[:lower:]') in arch ) @@ -242,6 +242,12 @@ __gather_linux_system_info() { echowarn "Unable to parse the Debian Version" fi ;; + opensuse* ) + n="opensuse" + ;; + sles*|sled*|suse* ) + n="suse" + ;; * ) n=${nn} ;; @@ -305,10 +311,8 @@ __check_end_of_life_versions() { ;; opensuse) - # openSUSE versions not supported - # - # <= 12.1 - if { [ "$DISTRO_MAJOR_VERSION" -eq 12 ] && [ "$DISTRO_MINOR_VERSION" -eq 1 ]; } || [ "$DISTRO_MAJOR_VERSION" -lt 12 ]; then + # openSUSE: allow Leap 15.x and Tumbleweed + if [ -n "$DISTRO_MAJOR_VERSION" ] && [ "$DISTRO_MAJOR_VERSION" -lt 12 ]; then echoerror "End of life distributions are not supported." echoerror "Please consider upgrading to the next stable. See:" echoerror " http://en.opensuse.org/Lifetime" @@ -317,16 +321,19 @@ __check_end_of_life_versions() { ;; suse) - # SuSE versions not supported - # - # < 11 SP2 - SUSE_PATCHLEVEL=$(awk '/PATCHLEVEL/ {print $3}' /etc/SuSE-release ) - if [ "x${SUSE_PATCHLEVEL}" = "x" ]; then - SUSE_PATCHLEVEL="00" - fi - if { [ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$SUSE_PATCHLEVEL" -lt 02 ]; } || [ "$DISTRO_MAJOR_VERSION" -lt 11 ]; then - echoerror "Versions lower than SuSE 11 SP2 are not supported." - echoerror "Please consider upgrading to the next stable" + # SLES / SLED + if [ -f /etc/SuSE-release ]; then + SUSE_PATCHLEVEL=$(awk '/PATCHLEVEL/ {print $3}' /etc/SuSE-release ) + if [ "x${SUSE_PATCHLEVEL}" = "x" ]; then + SUSE_PATCHLEVEL="00" + fi + if { [ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$SUSE_PATCHLEVEL" -lt 02 ]; } || [ "$DISTRO_MAJOR_VERSION" -lt 11 ]; then + echoerror "Versions lower than SuSE 11 SP2 are not supported." + echoerror "Please consider upgrading to the next stable" + exit 1 + fi + elif [ -n "$DISTRO_MAJOR_VERSION" ] && [ "$DISTRO_MAJOR_VERSION" -lt 12 ]; then + echoerror "Versions lower than SUSE 12 are not supported." exit 1 fi ;; @@ -546,58 +553,63 @@ daemons_running_fedora() { # Opensuse Install Functions # install_opensuse() { - zypper -n install -l kvm libvirt bridge-utils python3-libguestfs supervisor || return 1 + zypper -n install -l qemu-kvm libvirt libvirt-daemon-qemu libvirt-client bridge-utils python3-libguestfs supervisor || \ + zypper -n install -l kvm libvirt bridge-utils python3-libguestfs python3-supervisor || \ + zypper -n install -l kvm libvirt bridge-utils python3-libguestfs || return 1 return 0 } install_opensuse_post() { if [ -f /etc/sysconfig/libvirtd ]; then sed -i 's/#LIBVIRTD_ARGS/LIBVIRTD_ARGS/g' /etc/sysconfig/libvirtd - else - echoerror "/etc/sysconfig/libvirtd not found. Exiting..." - exit 1 fi if [ -f /etc/libvirt/libvirtd.conf ]; then sed -i 's/#listen_tls/listen_tls/g' /etc/libvirt/libvirtd.conf sed -i 's/#listen_tcp/listen_tcp/g' /etc/libvirt/libvirtd.conf sed -i 's/#auth_tcp/auth_tcp/g' /etc/libvirt/libvirtd.conf - else - echoerror "/etc/libvirt/libvirtd.conf not found. Exiting..." - exit 1 + sed -i 's/#unix_sock_group = "libvirt"/unix_sock_group = "libvirt"/g' /etc/libvirt/libvirtd.conf + sed -i 's/#unix_sock_rw_perms = "0770"/unix_sock_rw_perms = "0770"/g' /etc/libvirt/libvirtd.conf + sed -i 's/#auth_unix_rw = "polkit"/auth_unix_rw = "none"/g' /etc/libvirt/libvirtd.conf + fi + if [ -f /etc/libvirt/virtqemud.conf ]; then + sed -i 's/#listen_tls/listen_tls/g' /etc/libvirt/virtqemud.conf + sed -i 's/#listen_tcp/listen_tcp/g' /etc/libvirt/virtqemud.conf + sed -i 's/#auth_tcp/auth_tcp/g' /etc/libvirt/virtqemud.conf + sed -i 's/#unix_sock_group = "libvirt"/unix_sock_group = "libvirt"/g' /etc/libvirt/virtqemud.conf + sed -i 's/#unix_sock_rw_perms = "0770"/unix_sock_rw_perms = "0770"/g' /etc/libvirt/virtqemud.conf + sed -i 's/#auth_unix_rw = "polkit"/auth_unix_rw = "none"/g' /etc/libvirt/virtqemud.conf fi if [ -f /etc/libvirt/qemu.conf ]; then sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf sed -i 's/#[ ]*spice_listen.*/spice_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf - else - echoerror "/etc/libvirt/qemu.conf not found. Exiting..." - exit 1 fi - if [ -f /etc/supervisord.conf ]; then - curl https://raw.githubusercontent.com/retspen/webvirtcloud/master/conf/daemon/gstfsd > /usr/local/bin/gstfsd - chmod +x /usr/local/bin/gstfsd - curl https://raw.githubusercontent.com/retspen/webvirtcloud/master/conf/supervisor/gstfsd.conf > /etc/supervisor.d/gstfsd.ini - else - echoerror "Supervisor not found. Exiting..." - exit 1 + mkdir -p /etc/supervisord.d /etc/supervisor/conf.d + if [ -f /etc/supervisord.conf ] || [ -f /etc/supervisor/supervisord.conf ]; then + curl -fsSL https://raw.githubusercontent.com/retspen/webvirtcloud/master/conf/daemon/gstfsd > /usr/local/bin/gstfsd 2>/dev/null || true + chmod +x /usr/local/bin/gstfsd 2>/dev/null || true + curl -fsSL https://raw.githubusercontent.com/retspen/webvirtcloud/master/conf/supervisor/gstfsd.conf > /etc/supervisord.d/gstfsd.ini 2>/dev/null || true fi return 0 } daemons_running_opensuse() { - if [ -f /usr/lib/systemd/system/libvirtd.service ]; then - systemctl stop libvirtd.service > /dev/null 2>&1 - systemctl start libvirtd.service - fi - if [ -f /usr/lib/systemd/system/libvirt-guests.service ]; then - systemctl stop libvirt-guests.service > /dev/null 2>&1 - systemctl start libvirt-guests.service - fi - if [ -f /usr/lib/systemd/system/supervisord.service ]; then - systemctl stop supervisord.service > /dev/null 2>&1 - systemctl start supervisord.service - fi + systemctl enable --now libvirtd.service 2>/dev/null || systemctl enable --now virtqemud.service 2>/dev/null || true + systemctl enable --now libvirt-guests.service 2>/dev/null || true + systemctl enable --now supervisord.service 2>/dev/null || systemctl enable --now supervisor.service 2>/dev/null || true return 0 } + +install_suse() { + install_opensuse "$@" +} + +install_suse_post() { + install_opensuse_post "$@" +} + +daemons_running_suse() { + daemons_running_opensuse "$@" +} # # Ended openSUSE Install Functions # diff --git a/doc/architecture.md b/doc/architecture.md index 2dacfd4..d970cd4 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -169,3 +169,16 @@ Built on Django REST Framework with `drf-nested-routers`, providing a structured ## 7. Architecture Highlights WebVirtCloud leverages a hybrid pattern: it combines the relational strengths of the **Django ORM** for persistent entities (users, compute host credentials, quotas, and permission delegations) with direct, real-time **libvirt C API bindings** for dynamic virtualization state (VM statuses, resource allocation, storage metrics, and live hardware statistics). This design ensures that the web console always reflects the true hypervisor state without risk of database desynchronization. + +--- + +## 8. Supported Distributions & Platform Matrix + +WebVirtCloud installer and runtime dependencies support modern enterprise Linux distributions: + +| Distribution Family | Target Versions | Package Manager | Init / Supervisor | +|---|---|---|---| +| **Ubuntu** | 20.04, 22.04 LTS, 24.04 LTS | `apt` | Systemd / Supervisor | +| **Debian** | 10, 11, 12 (Bookworm) | `apt` | Systemd / Supervisor / Runit | +| **RHEL / Rocky / Alma** | 9.x, 10.x | `dnf` | Systemd / Supervisord | +| **openSUSE / SLES** | openSUSE Leap 15.x, Tumbleweed, SLES 15 | `zypper` | Systemd / Supervisord | diff --git a/webvirtcloud.sh b/webvirtcloud.sh index 6d2c878..cc96f23 100755 --- a/webvirtcloud.sh +++ b/webvirtcloud.sh @@ -160,6 +160,16 @@ install_packages () { fi done ;; + suse) + for p in $PACKAGES; do + if rpm -q "$p" >/dev/null 2>&1; then + echo " * $p already installed" + else + echo " * Installing $p" + log "zypper --non-interactive install -y $p" + fi + done + ;; esac } @@ -206,12 +216,17 @@ create_user () { if [ "$distro" == "ubuntu" ] || [ "$distro" == "debian" ] || [[ "$distro" == "uos" && "$codename" == "eagle" ]]; then adduser --quiet --disabled-password --gecos '""' "$APP_USER" + elif [ "$distro" == "suse" ]; then + useradd -m -s /bin/bash "$APP_USER" 2>/dev/null || adduser "$APP_USER" else - adduser "$APP_USER" + useradd -m -s /bin/bash "$APP_USER" 2>/dev/null || adduser "$APP_USER" fi - usermod -a -G "$nginx_group" "$APP_USER" - usermod -a -G libvirt "$nginx_group" + usermod -a -G "$nginx_group" "$APP_USER" 2>/dev/null || true + usermod -a -G libvirt "$nginx_group" 2>/dev/null || true + usermod -a -G kvm "$nginx_group" 2>/dev/null || true + usermod -a -G libvirt "$APP_USER" 2>/dev/null || true + usermod -a -G kvm "$APP_USER" 2>/dev/null || true } run_as_app_user () { @@ -371,6 +386,8 @@ if [ -f /etc/os-release ]; then version="$(source /etc/os-release && echo "$VERSION_ID")" # shellcheck disable=SC1091 codename="$(source /etc/os-release && echo "${VERSION_CODENAME:-$UBUNTU_CODENAME}")" + # shellcheck disable=SC1091 + id_like="$(source /etc/os-release && echo "${ID_LIKE:-}")" elif [[ -f /etc/lsb-release || -f /etc/debian_version ]]; then if command -v lsb_release >/dev/null 2>&1; then distro="$(lsb_release -is)" @@ -384,6 +401,11 @@ elif [[ -f /etc/lsb-release || -f /etc/debian_version ]]; then elif [ -f /etc/centos-release ]; then distro="centos" version="8" +elif [ -f /etc/SuSE-release ]; then + distro="suse" + version="15" + codename="" + id_like="suse" else distro="unsupported" fi @@ -394,7 +416,7 @@ echo ' ' echo "" -echo " Welcome to Webvirtcloud Installer for RHEL Based OSes, Debian and Ubuntu!" +echo " Welcome to Webvirtcloud Installer for RHEL Based OSes, Debian, Ubuntu, and SUSE!" echo "" shopt -s nocasematch case $distro in @@ -425,6 +447,15 @@ case $distro in supervisor_conf_path=/etc/supervisord.d supervisor_file_name=webvirtcloud.ini ;; + *opensuse*|*sles*|*sled*|*suse*) + echo " The installer has detected $distro version $version." + distro=suse + nginx_group=nginx + nginxfile=/etc/nginx/conf.d/$APP_NAME.conf + supervisor_service=supervisord + supervisor_conf_path=/etc/supervisord.d + supervisor_file_name=webvirtcloud.ini + ;; *Uos*|*uos*) # codename may be fuyu, kongzi, eagle or empty string. output_expand="" @@ -456,8 +487,18 @@ case $distro in supervisor_file_name=webvirtcloud.ini ;; *) - echo " The installer was unable to determine your OS. Exiting for safety." - exit 1 + if [[ "$id_like" =~ suse ]]; then + echo " The installer has detected $distro (SUSE family) version $version." + distro=suse + nginx_group=nginx + nginxfile=/etc/nginx/conf.d/$APP_NAME.conf + supervisor_service=supervisord + supervisor_conf_path=/etc/supervisord.d + supervisor_file_name=webvirtcloud.ini + else + echo " The installer was unable to determine your OS. Exiting for safety." + exit 1 + fi ;; esac @@ -684,6 +725,80 @@ case $distro in restart_nginx fi ;; + suse) + # Install for openSUSE Leap 15.x / Tumbleweed / SLES 15 + tzone=\'$(get_timezone)\' + + echo -n "* Updating installed packages." + log "zypper --non-interactive refresh" & pid=$! + progress + + echo "* Installing OS requirements." + PACKAGES="git python3-devel python3-pip python3-virtualenv libvirt-devel python3-libvirt python3-lxml openldap2-devel cyrus-sasl-devel libopenssl-devel libxslt-devel libxml2-devel gcc pkg-config nginx" + install_packages + + set_hosts + + # Supervisor on SUSE (available as python3-supervisor, supervisor, or via pip) + if ! command -v supervisord >/dev/null 2>&1; then + if zypper --non-interactive install -y python3-supervisor >/dev/null 2>&1; then + echo " * python3-supervisor installed via zypper" + elif zypper --non-interactive install -y supervisor >/dev/null 2>&1; then + echo " * supervisor installed via zypper" + else + echo " * Installing supervisor via pip3" + log "pip3 install supervisor" + fi + fi + + # Ensure /etc/supervisord.d directory exists and is included in supervisord.conf + mkdir -p /etc/supervisord.d + if [ ! -f /etc/supervisord.conf ] && [ ! -f /etc/supervisor/supervisord.conf ]; then + if command -v echo_supervisord_conf >/dev/null 2>&1; then + echo_supervisord_conf > /etc/supervisord.conf + echo -e "\n[include]\nfiles = /etc/supervisord.d/*.ini\n" >> /etc/supervisord.conf + fi + elif [ -f /etc/supervisord.conf ] && ! grep -q "/etc/supervisord.d" /etc/supervisord.conf; then + echo -e "\n[include]\nfiles = /etc/supervisord.d/*.ini\n" >> /etc/supervisord.conf + fi + + # Ensure systemd service for supervisord exists if installed via pip + if [ ! -f /usr/lib/systemd/system/supervisord.service ] && [ ! -f /etc/systemd/system/supervisord.service ]; then + supervisord_bin="$(command -v supervisord 2>/dev/null || echo "/usr/local/bin/supervisord")" + supervisorctl_bin="$(command -v supervisorctl 2>/dev/null || echo "/usr/local/bin/supervisorctl")" + cat > /etc/systemd/system/supervisord.service </dev/null 2>&1 || true + fi + + install_webvirtcloud + + echo "* Configuring Nginx." + configure_nginx + + echo "* Configuring Supervisor." + configure_supervisor + + set_firewall + + restart_supervisor + restart_nginx + ;; esac