Monday, November 9, 2026

Agama Unattended installation via profile.json of Tumbleweed's KVM Guests

 Tumbleweed updates continuously, manual interactive installations frequently break due to rapid package, installer (Agama), or kernel shifts. Unattended automation ensures predictable, reproducible, and verifiable states for disaster recovery and testing.

• Mitigating Rolling-Release Fluctuation: Tumbleweed moves extremely fast. An unattended deployment via structured templates (profile.json) ensures that the initial bootstrap bypasses manual package selection errors and unpredictable installer UI changes.
• Disaster Recovery Validation Loop: For continuous integration or automated DR testing, environments must be torn down and rebuilt from scratch. Unattended installation scripts (using virt-install) allow you to programmatically verify that guest configurations, volume management definitions, and baseline application profiles work under the latest upstream packages without human intervention. (*)

=================================================================

The sample of profile.json file ( generated via Agama Web UI )

=================================================================

boris@localhost:~/Downloads> cat profile.json
{
 "product": {
   "id": "Tumbleweed"
 },
 "software": {
   "patterns": {
     "add": [
       "kvm_tools",
       "kde",
       "cockpit",
       "selinux"
     ],
     "remove": [
       "office"
     ]
   }
 },
 "storage": {
   "boot": {
     "configure": true
   },
   "drives": [
     {
       "partitions": [
         {
           "filesystem": {
             "mountOptions": [],
             "path": "/boot/efi",
             "reuseIfPossible": false,
             "type": "vfat"
           },
           "id": "esp",
           "size": {
             "max": 1000000000,
             "min": 1000000000
           }
         },
         {
           "filesystem": {
             "mountOptions": [],
             "path": "/boot",
             "reuseIfPossible": false,
             "type": "ext4"
           },
           "size": {
             "max": 2500000000,
             "min": 2500000000
           }
         },
         {
           "filesystem": {
             "mountOptions": [],
             "path": "/",
             "reuseIfPossible": false,
             "type": {
               "btrfs": {
                 "snapshots": true
               }
             }
           },
           "size": {
             "max": 55000000000,
             "min": 55000000000
           }
         },
         {
           "filesystem": {
             "mountOptions": [],
             "path": "/opt",
             "reuseIfPossible": false,
             "type": "xfs"
           },
           "size": {
             "max": 67000000000,
             "min": 67000000000
           }
         },
         {
           "delete": true,
           "search": {
             "ifNotFound": "skip"
           }
         }
       ],
       "search": {
         "condition": {
           "name": "/dev/vda"
         },
         "ifNotFound": "error"
       }
     }
   ],
   "mdRaids": [],
   "volumeGroups": []
 },
 "user": {
   "fullName": "boris",
   "password": "***********",
   "hashedPassword": false,
   "userName": "boris",
   "sshPublicKeys": []
 },
 "root": {
   "password": "************",
   "hashedPassword": false,
   "sshPublicKeys": [
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+XwCy1t95wJD0eErIpkeeXc9qGN/rwX1hAkEKJoOGQ name@example.com"
   ]
 }

Python3 -m http.server 8080 --bind 192.168.0.23 starts a basic local web server that shares the files in your current working directory over the network. Port 8080 been opened requires some additional tuning (*)

$ sudo firewall-cmd --zone=trusted --remove-interface=bridge0 --permanent
OR if you added the port explicitly to a public/home zone:
$ sudo firewall-cmd --zone=public --remove-port=8080/tcp --permanent
$ sudo firewall-cmd --reload

boris@localhost:~/Downloads> python3 -m http.server 8080 --bind 192.168.0.23  Serving HTTP on 192.168.0.23 port 8080 (http://192.168.0.23:8080/) ...
192.168.0.51 - - [09/Nov/2026 08:25:19] "GET /profile.json HTTP/1.1" 200 -

Virt-install command line for unattended deploying Tumbleweed KVM Guest (*)
 #!/bin/bash
virt-install \
  --name tw-unattended-Guest03 \
  --memory 10000 \
  --vcpus 4 \
  --osinfo opensusetumbleweed \
  --disk pool=default,size=125,bus=virtio,format=qcow2 \
  --network bridge=bridge0,model=virtio \
  --graphics vnc,listen=127.0.0.1 \

  --location /home/boris/Downloads/agama-installer.x86_64-25.0.0-openSUSE-Build8.23.iso,kernel=boot/x86_64/loader/linux,initrd=boot/x86_64/loader/initrd \
  --extra-args "inst.auto=http://192.168.0.23:8080/profile.json"

Virt-install Guest startup and Guest's Console

localhost:~ # ./virtInst03.sh
WARNING  No display detected. Not running virt-viewer.
WARNING  No console to launch for the guest, defaulting to --wait -1

Starting install...
Retrieving 'linux'                        |  17 MB  00:00:00
Retrieving 'initrd'                       | 128 MB  00:00:00
Allocating 'tw-unattended-Guest03.qcow2'   |         00:00:00
Creating domain...                                                                                             
Domain is still running. Installation may be in progress.
Waiting for the installation to complete.
Domain has shutdown. Continuing.
Domain creation completed.
Restarting guest. 


 










(*) Enabling audio via switching to Spice Console

Inside VM issue

#1. Access the guest OS command line to spin up the virtualization channels
$ sudo systemctl start qemu-guest-agent
$ sudo systemctl start spice-vdagentd
# 2. Verify the active background execution status of both services
$ sudo systemctl status qemu-guest-agent spice-vdagentd 

Shutdown VM and update XML-profile as follows 

$ sudo EDITOR=nano virsh edit tw-unattended-guest01 

 <sound model='ich9'>
  <audio id='1'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
</sound>
<audio id='1' type='spice'/>

Detached VNC Console created using Cockpit Web interface






REFERENCES

(*) Assisted by Google AI 

 

Friday, November 6, 2026

Online setup openSUSE Leap 16.1 on bare metal and tuning correct resolution for HDMI Monitor

What is Agama's SSH Injection Advantage: By injecting public key into Agama during the network deployment phase, the very first boot of the newly installed operating system will have the OpenSSH daemon (sshd) active and your authorized keys populated.This gives you a guaranteed SSH hook into the target host. You don't have to look at the unreadable HDMI screen to execute the configuration script below you can run it smoothly from a remote machine, cleanly stripping away the problematic
SDDM prompt and tailoring the Wayland desktop profile securely. 

Online setup openSUSE Leap 16.1 on bare metal may be performed via Leap-16.1-online-installer-x86_64.install.iso with same Web UI as Agama installer. Installation phase is almost the same as described in Install Tumbleweed via Agama Installer and automated detection of HDMI ports   However , on Leap 16.1 the block marked in blue is critical on Leap 16.1

"You should have SSH trusted pair already generated prior to configuring root's credentials on target box. I've selected password and SSH public key. Cut and paste SSH Public key into corresponding field on Agama Web UI. Save and start installation" which would be initiated remotely from your workstation.
Agama follows standard transactional schema when preparing desired disk layout, selection of DE, user's and root's credentials until you press "Install".  In particular user1 credentials were also injected into Agama Web UI before installation started.

Upon system reboot instead of struggling with poor quality SDDM login you better issue from host initiated Agama Web UI and having ssh trusted connection to target box (*) :

# systemctl disable sddm.service --force
# systemctl set-default multi-user.target
# mkdir -p /etc/systemd/system/getty@tty1.service.d
# nano /etc/systemd/system/getty@tty1.service.d/override.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin user1 --noclear %I $TERM

CTRL+O; CTRL+X

# su - user1 

======================
$ nano ~/.bash_profile
====================== 

# Source the user1's primary bash configuration if it exists
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
# Automatically launch KDE Plasma Wayland if logging in on TTY1
if [ -z "${DISPLAY}" ] && [ "${XDG_VTNR}" -eq 1 ]; then
    exec /usr/bin/startplasma-wayland
fi

Verification step

user1@localhost:~> systemctl cat getty@tty1.service | grep "autologin"
ExecStart=-/sbin/agetty --autologin user1 --noclear %I $TERM

Upon next reboot of target box you would avoid SDDM login prompt and automatically start KDE Plasma 6.6.4 session.

I also have to notice that dracut hook like this 

$ echo 'hostonly="no"' | sudo tee /etc/dracut.conf.d/00-generic.conf
$ sudo dracut -f --regenerate-all 
won't help you under kernel 6.12 ( vs 7.2-7.3 kernels on Tumbleweed) 

  

 References

(*) Assisted by Google AI 

Wednesday, November 4, 2026

Simulation Agama Web UI in Virtual environment as KVM Guest (Assisted by Google AI)

 Start deploying your VM via Virtual-manager. When Spice console would pop up just press the button "Send key" from horizontal menu and select CTL+ALT+F3.  Spice console becomes a text terminal providing you IP-ADDRESS of your KVM guest and root password. Launch browser of KVM Host machine to IP-ADDRESS of your Agama VM .  This action will bring you to Agama Web UI and you will be prompted for root password, which may be picked up from text terminal. Log into Agama Web UI running on your Host machine and responsible for deployment selected Suse flavor on KVM Guest. 

After entering WEB UI environment select OS (Tumbleweed for instance) manage  as usual until creating root account. You should have SSH trusted pair already generated prior to configuring root's credentials on VM. I've selected password and SSH public key. Cut and paste SSH Public key into corresponding field on Agama Web UI. Save and start installation which was initiated via KVM host on KVM guest.
Agama follows standard transactional schema when preparing desired disk layout, selection of DE, user's and root's credentials until you press "Install".

Upon reboot of VM with installed tumbleweed instance Agama installer will bring you to text terminal with login prompt for user1 (for instance).

Now switch to root's terminal of Host machine and connect to VM via trusted ssh connection. Inside VM proceed as follows (actually same action may be undertaken on bare metal from the host completed Agama Web UI which was responsible for deploying Suse flavor on target host )  :

# systemctl disable sddm.service --force
# systemctl set-default multi-user.target
# mkdir -p /etc/systemd/system/getty@tty1.service.d
# nano /etc/systemd/system/getty@tty1.service.d/override.conf
Input
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin user1 --noclear %I $TERM

CTRL+O; CTRL+X

# su - user1 

======================
$ nano ~/.bash_profile
====================== 

# Source the user1's primary bash configuration if it exists
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
# Automatically launch KDE Plasma Wayland if logging in on TTY1
if [ -z "${DISPLAY}" ] && [ "${XDG_VTNR}" -eq 1 ]; then
    exec /usr/bin/startplasma-wayland
fi

Verification step

user1@localhost:~> systemctl cat getty@tty1.service | grep "autologin"
ExecStart=-/sbin/agetty --autologin user1 --noclear %I $TERM

You are all set .
Now reboot you VM . You will be brought right into
KDE Plasma 6.8.2  running on your VM (KVM Guest )


Monday, November 2, 2026

Install Tumbleweed via Agama Installer and automated detection of HDMI ports (bare metal)

UPDATE as of 11/04/26  ( Google AI ) 

Auto login to KDE Plasma 6.8.2 on Tumbleweed

sudo systemctl disable sddm.service --force
sudo systemctl set-default multi-user.target
sudo mkdir -p /etc/systemd/system/getty@tty1.service.d
sudo nano /etc/systemd/system/getty@tty1.service.d/override.conf
Input
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin boris --noclear %I $TERM

==================
nano ~/.bash_profile
==================

# Source the user's primary bash configuration if it exists
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
# Automatically launch KDE Plasma Wayland if logging in on TTY1
if [ -z "${DISPLAY}" ] && [ "${XDG_VTNR}" -eq 1 ]; then
    exec /usr/bin/startplasma-wayland
fi

Verification step

boris@localhost:~> systemctl cat getty@tty1.service | grep "autologin"
ExecStart=-/sbin/agetty --autologin boris --noclear %I $TERM


System reboot

END UPDATE

UPDATE as of 11/03/26  

Reproduced with no issues on SATA SSD ( the same Intel Haswell Xeon box ).  I have to notice that Agama installer in phase of deployment packages on target drive demonstrates extremely high speed. Thin XFS LVM  Provisioning and Libvirt hooks implementation may be done only in post installation phase via CLI. 

END UPDATE 

 After booting via USB pen on target box,right after you see the first screen  
for selection of desired OS . You are supposed to press CTRL+ALT+F3 without any other input into the first page of Agama Web UI

You would see in terminal  IP-TARGET of target host and root password.
From another box ( in my case no IP:443 ) launch your browser to https://IP-TARGET. 
This step will bring you to Agama WEB UI and prompt for root "password".
After entering WEB UI environment select OS (Tumbleweed for instance)
manage as usual until creating root account.
You should have SSH trusted pair already generated prior to configuring
root's credentials on target box. I've selected password and SSH public key. 
Cut and paste SSH Public key into corresponding field on Agama Web UI. 
Save and start installation which was initiated remotely from your workstation.
Agama follows standard transactional schema when preparing desired disk layout, 
selection of DE, user's and root's credentials until you press "Install".
Upon reboot installed tumbleweed instance would properly detect HDMI port 
and Monitor of target box. Booting up brought me to text console
with resolution 1920x1080. After logging into text mode terminal
I've just issued /usr/bin/startplasma-wayland and get into

KDE Plasma 6.8.2
environment. I believe that slow WD 596 GB HDD,
as the only one empty drive was just too slow to bring up Plasma itself.

In my case I was ready to log into target box via SSH and run:
$ echo 'hostonly="no"' | sudo tee /etc/dracut.conf.d/00-generic.conf

If you want a small ramdisk but want to force legacy drivers to include anyway

$ echo 'add_drivers+=" nouveau amdgpu radeon i915 "'| \
sudo tee /etc/dracut.conf.d/00-generic.conf

$ sudo cat /etc/dracut.conf.d/00-generic.conf
$ sudo dracut -f --regenerate-all


to rebuild
initrd-7.3.0-1-default with required legacy drivers as after YaST installation.
But resolution of HDMI Monitor/port were already detected by Agama Installer

 





























boris@localhost:~> df -Th
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/sda3      btrfs     140G  7.1G  132G   6% /
devtmpfs       devtmpfs   16G     0   16G   0% /dev
tmpfs          tmpfs      16G     0   16G   0% /dev/shm
efivarfs       efivarfs  256K   94K  158K  38% /sys/firmware/efi/efivars
tmpfs          tmpfs     6.3G  2.0M  6.3G   1% /run
none           tmpfs     1.0M     0  1.0M   0% /run/credentials/systemd-journald.service
tmpfs          tmpfs      16G  8.0K   16G   1% /tmp
/dev/sda3      btrfs     140G  7.1G  132G   6% /.snapshots
/dev/sda3      btrfs     140G  7.1G  132G   6% /srv
/dev/sda3      btrfs     140G  7.1G  132G   6% /home
/dev/sda3      btrfs     140G  7.1G  132G   6% /usr/local
/dev/sda3      btrfs     140G  7.1G  132G   6% /var
/dev/sda3      btrfs     140G  7.1G  132G   6% /root
/dev/sda4      xfs       233G  4.5G  229G   2% /opt
/dev/sda2      ext4      1.8G  105M  1.6G   7% /boot
/dev/sda1      vfat      477M  100M  378M  21% /boot/efi
none           tmpfs     1.0M     0  1.0M   0% /run/credentials/getty@tty1.service
tmpfs          tmpfs     3.2G   72K  3.2G   1% /run/user/1000


Thursday, October 29, 2026

Tuning HDMI Monitors to work flawlessly with OpenSUSE Tumbleweed ( Assisted by Google AI )

   openSUSE defaults to hostonly="yes": openSUSE aggressively optimizes for ramdisk size and boot speed. When you run an openSUSE installer over SSH or text mode, YaST builds an initramfs tailored strictly to the hardware it detected as active during that specific install phase. Since you bypassed the graphical display pipeline during the text/SSH run, openSUSE's Dracut config frequently omits the full-featured open-source video drivers and associated firmware, fallback-loading a basic VESA framebuffer (which locks you at 800x600). 

 You can force openSUSE to match Fedora's robust driver behaviors by purging the restricted flags and forcing Dracut to build a comprehensive, unconstrained ramdisk. 

For instance 

$ cat /proc/cmdline
$ cat /proc/cmdline | grep nomodeset
$ sudo nano /etc/default/grub
$ which dracut

$ echo 'hostonly="no"' | sudo tee /etc/dracut.conf.d/00-generic.conf

If you want a small ramdisk but want to force legacy drivers to include anyway

$ echo 'add_drivers+=" nouveau amdgpu radeon i915 "' | sudo tee /etc/dracut.conf.d/00-generic.conf
$ sudo cat /etc/dracut.conf.d/00-generic.conf
$ sudo dracut -f --regenerate-all

Reboot && Attach modern Monitor via HDMI port


boris@localhost:~> xrandr | grep '*'
WARNING: running xrandr against an Xwayland server. See the xrandr man page for details.
   1920x1080     59.96*+
oris@localhost:~> xdpyinfo | grep 'dimensions:'
  dimensions:    1920x1080 pixels (508x285 millimeters)

For KDE Plasma (Wayland)

boris@localhost:~> kscreen-doctor --outputs
Output: 1 HDMI-A-1 3009548e-124a-4193-b142-47da4d280632
       enabled
       connected
       priority 1
       HDMI
       replication source:
0
       Modes:  1:1920x1080@60.00*  2:1920x1080@59.94  3:1920x1080@60.00!  4:1920x1080@59.94  5:1920x1080@50.00  6:1920x1080@50.00  7:1
280x1024@75.03  8:1280x1024@60.02  9:1280x960@60.00  10:1280x720@60.00  11:1280x720@59.94  12:1280x720@50.00  13:1024x768@75.03  14:102
4x768@70.07  15:1024x768@60.00  16:800x600@75.00  17:800x600@72.19  18:800x600@60.32  19:720x576@50.00  20:720x480@60.00  21:720x480@59
.94  22:640x480@75.00  23:640x480@72.81  24:640x480@60.00  25:640x480@59.94  
       Custom modes: None
       Geometry: 0,0 1920x1080
       Scale: 1
       Rotation: 1
       Overscan: 0
       Vrr: incapable
       RgbRange: unknown
       HDR: incapable
       Wide Color Gamut: incapable
       ICC profile: none
       Color profile source: sRGB
       Color power preference: prefer efficiency and performance
       Brightness control: supported, set to 100% and dimming to 100%
       Color resolution: unknown
       Allow EDR: unsupported
       Sharpness control: unsupported
       Automatic brightness: unsupported
       Auto Rotate Policy: incapable
       Adaptive backlight modulation: unsupported

























Friday, October 23, 2026

Convertion F45 Server to Thin XFS LVM Provisioning with Libvirt-Hooks based on BTRFS-XFS Setup ( Assisted by Google AI )

UPDATE as of 10/30/26  

See Tuning HDMI Monitors to work flawlessly with OpenSUSE Tumbleweed 

END UPDATE 

                                        
 Converting a server (such as a high-density virtualization platform) to Thin XFS LVM Provisioning with Libvirt-Hooks solves the fundamental performance bottlenecks and management complexities inherent in running virtual machines on top of a native or hybrid Btrfs storage architecture.
While Btrfs excels at features like transparent compression, file-level subvolumes, and data scrubbing, its structural design makes it highly inefficient for heavy virtual machine disk (.qcow2 or .img) storage. Moving to a thin-provisioned LVM layer running XFS-orchestrated dynamically via Libvirt event hooks - provides enterprise-grade predictability, raw block speed, and automated lifecycle management.

The core issue with openSUSE Tumbleweed is problem with native HDMI Monitors support. OverlayFS may be setup on F45 Server on top of mixed BTRFS/XFS layout as shown below and you would have an option with Btrfs low level CLI (mv,snaphot commands) to rollback the main instance to previous safe state via Overlay instances available as options provided by Grub submenu. However, BTRFS support on F45 will stay as experimental feature vs Enterprise grade solutions provided by Suse flavors headless servers.

Final disk layout ( bare metal case )

root@server45fedora:~# hostnamectl
     Static hostname: server45fedora
           Icon name: computer-desktop
             Chassis: desktop
   Chassis Asset Tag: To be filled by O.E.M.
          Machine ID: c97053a998854caa9178705b31914bc3
             Boot ID: 17bfefb60d0b4623a61b0bc44157819b
        Product UUID: af95be8a-eace-8515-aaa1-00d861d9318b
    Operating System: Fedora Linux 45 (Server Edition)
         CPE OS Name: cpe:/o:fedoraproject:fedora:45
      OS Support End: Wed 2027-11-24
OS Support Remaining: 1y 1month
              Kernel: Linux 7.2.11-300.fc45.x86_64
        Architecture: x86-64
     Hardware Vendor: Micro-Star International Co., Ltd.
      Hardware Model: MS-7C37
     Hardware Serial: To be filled by O.E.M.
    Hardware Version: 3.0
    Firmware Version: H.60
       Firmware Date: Wed 2019-11-06
        Firmware Age: 6y 11month 2w 3d
root@server45fedora:~# findmnt -t btrfs
TARGET   SOURCE                   FSTYPE OPTIONS
/        /dev/nvme0n1p4[/@-0]     btrfs  rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=260,subvol=/@-0
├─/.snapshots
│        /dev/nvme0n1p4[/snapshots]
│                                 btrfs  rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/snapsho
├─/etc/libvirt/qemu
│        /dev/nvme0n1p4[/@qemu]   btrfs  rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=267,subvol=/@qemu
├─/home  /dev/nvme0n1p4[/@home]   btrfs  rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=257,subvol=/@home
├─/var/cache
│        /dev/nvme0n1p4[/@cache]  btrfs  rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=259,subvol=/@cache
└─/var/log
         /dev/nvme0n1p4[/@log]    btrfs  rw,relatime,seclabel,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/@log
root@server45fedora:~# lsblk -f
NAME                               FSTYPE      FSVER    LABEL UUID                                   FSAVAIL FSUSE% MOUNTPOINTS
sr0
zram0                              swap        1        zram0 10432693-2e2c-4c5c-8f6e-01f787c76c37                  [SWAP]
nvme1n1  ( Tumbleweed drive )
├─nvme1n1p1                        vfat        FAT32          61BC-A4BF
├─nvme1n1p2                        ext4        1.0            c6a99ff4-3bf8-4d50-809f-db6259935a55
├─nvme1n1p3                        LVM2_member LVM2 001       aMgkVz-72ia-dHzo-8Q2p-haHX-VoKU-bv5Bce
│ ├─vg_suse-tp_suse_kvm_tmeta
│ │ └─vg_suse-tp_suse_kvm-tpool
│ │   ├─vg_suse-tp_suse_kvm
│ │   └─vg_suse-vol_suse_thin      xfs                        0f543732-acde-4d36-bc88-b75e9e8c8cc4
│ └─vg_suse-tp_suse_kvm_tdata
│   └─vg_suse-tp_suse_kvm-tpool
│     ├─vg_suse-tp_suse_kvm
│     └─vg_suse-vol_suse_thin      xfs                        0f543732-acde-4d36-bc88-b75e9e8c8cc4
└─nvme1n1p4                        btrfs                      800c9083-38bd-4fab-b916-0bce438442ab
nvme0n1  ( F45 Server's drive )
├─nvme0n1p1                        vfat        FAT32          E005-797D                               590.9M     1% /boot/efi
├─nvme0n1p2                        ext4        1.0            d2c18aab-3ead-4abe-b1a4-9ef47b6b61a4      1.2G    29% /boot
├─nvme0n1p3                        LVM2_member LVM2 001       EuwX4s-zJJ3-eL0g-IsAi-AmJC-Z7XI-bxomP6
│ ├─vg_fedora-tp_fedora_kvm_tmeta
│ │ └─vg_fedora-tp_fedora_kvm-tpool
│ │   │
│ │   ├─vg_fedora-tp_fedora_kvm
│ │   └─vg_fedora-vol_fedora_thin  xfs                        f13011c1-1835-4347-addc-8f37b23475a6    142.3G     6% /var/lib/libvirt/images
│ └─vg_fedora-tp_fedora_kvm_tdata
│   └─vg_fedora-tp_fedora_kvm-tpool
│     │
│     ├─vg_fedora-tp_fedora_kvm
│     └─vg_fedora-vol_fedora_thin  xfs                        f13011c1-1835-4347-addc-8f37b23475a6    142.3G     6% /var/lib/libvirt/images
└─nvme0n1p4                        btrfs                      ff4229bc-cf09-4b53-8edc-52611c83c4f5    248.9G     8% /var/log
                                                                                                                    /var/cache
                                                                                                                    /home
                                                                                                                    /etc/libvirt/qemu
                                                                                                                    /.snapshots
#########################################################
Convertion XFS partition been setup via Anaconda deployment phase into Thin XFS provisioned LVM  via reinitializing  #########################################################
 
# 1. Ensure the target NVMe partition is completely clean
sudo umount /dev/nvme0n1p3 2>/dev/null
sudo wipefs --all /dev/nvme0n1p3

# 2. Bind the NVMe partition to LVM with 500M metadata room
sudo pvcreate --metadatasize 500M /dev/nvme0n1p3

# 3. Create your dedicated vg_fedora Volume Group container
sudo vgcreate vg_fedora /dev/nvme0n1p3

# 4. Instantiate the Thin Pool (Using 152G to respect 80/20 strategy buffer)
sudo lvcreate -L 152G --chunksize 128K --thinpool tp_fedora_kvm vg_fedora

# 5. Carve out the virtual thin volume named 'vol_fedora_thin'
sudo lvcreate -n vol_fedora_thin -V 152G --thinpool vg_fedora/tp_fedora_kvm

# Format the thin volume enabling fast VFS-level copy-on-write pointer sharing
sudo mkfs.xfs -K -m reflink=1,crc=1 /dev/vg_fedora/vol_fedora_thin


sudo mkdir -p /var/lib/libvirt/images

# Append your optimized thin block map permanently into the layout tables
echo '/dev/vg_fedora/vol_fedora_thin   /var/lib/libvirt/images xfs defaults,noatime,logbufs=8,logbsize=256k 0 0' | sudo tee -a /etc/fstab

# Rebuild systemd configuration targets and mount the new thin pool volume
sudo systemctl daemon-reload
sudo mount -a

##################################################                                     Create the target nested storage hierarchy
##################################################                                                                
 
Apply strict operational permissions for the libvirt engine
sudo chown root:root /var/lib/libvirt
sudo chmod 711 /var/lib/libvirt
sudo chmod 711 /var/lib/libvirt/images

# Apply SELinux context rules for KVM operation on Fedora
sudo semanage fcontext -a -t virt_image_t "/var/lib/libvirt/images(/.*)?" 2>/dev/null || true
sudo restorecon -R -v /var/lib/libvirt/images


System reboot
 
########################################################
Creating /etc/libvirt/hooks/qemu and service lvm-thin-snapshot.service                                                  ########################################################
 
root@fedoraSRV:~# cat /etc/libvirt/hooks/qemu
#!/bin/bash
GUEST_NAME="$1"
ACTION="$2"
PHASE="$3"
STORAGE_DIR="/var/lib/libvirt/images"
BACKUP_DIR="${STORAGE_DIR}/.snapshots_${GUEST_NAME}"
 
[ ! -d "$STORAGE_DIR" ] && exit 0
 
if [ "$ACTION" = "prepare" ] || [ "$ACTION" = "stopped" ]; then
    /usr/bin/systemctl start lvm-thin-snapshot.service &
 
    (
        /usr/bin/sleep 2
        /usr/bin/mkdir -p "$BACKUP_DIR"
 
        for disk_path in "${STORAGE_DIR}"/*"${GUEST_NAME}"*.qcow2; do
            [ ! -f "$disk_path" ] && continue
            disk_file=$(/usr/bin/basename "$disk_path")
            timestamp=$(/usr/bin/date +%Y%m%d_%H%M%S)
 
            /usr/bin/cp --reflink=always "$disk_path" "${BACKUP_DIR}/${disk_file}.snap_${timestamp}"
 
            # FEDORA FIX: Match Fedora execution user account permissions
            /usr/bin/chown qemu:qemu "${BACKUP_DIR}/${disk_file}.snap_${timestamp}" 2>/dev/null
        done
 
        /usr/bin/ls -tp "${BACKUP_DIR}"/* | /usr/bin/grep -v '/$' | /usr/bin/tail -n +6 | /usr/bin/xargs -I {} /usr/bin/rm -- "{}" 2>/dev/null
    ) & disown
 
elif [ "$ACTION" = "define" ] || [ "$ACTION" = "undefine" ]; then
    /usr/bin/systemctl start lvm-thin-snapshot.service &
fi
 
exit 0

 
root@fedoraSRV:~# cat /etc/systemd/system/lvm-thin-snapshot.service
[Unit]
Description=Rotate Pristine LVM Thin Pool Snapshot on Libvirt Changes
After=local-fs.target
 
[Service]
Type=oneshot
User=root
Environment=VG_NAME=vg_fedora
Environment=SNAP_NAME=snap_working_vms
Environment=THIN_VOL=vol_fedora_thin
Environment=IMG_DIR=/var/lib/libvirt/images
 
ExecStart=/bin/bash -c '\
    echo "Current allocated qcow2 image count: $(ls -1 ${IMG_DIR}/*.qcow2 2>/dev/null | wc -l)"; \
    echo "Removing old snapshot if present..."; \
    /usr/sbin/lvremove -f "${VG_NAME}/${SNAP_NAME}" 2>/dev/null || true; \
    echo "Creating pristine baseline snapshot..."; \
    /usr/sbin/lvcreate -s --name "${SNAP_NAME}" "${VG_NAME}/${THIN_VOL}"'
 
[Install]
WantedBy=multi-user.target 
 
################################
Healthy system status verification
################################
root@server45fedora:~# ls -Zla  /var/lib/libvirt/images
total 6730604
drwxr-xr-x. 4 root root system_u:object_r:virt_image_t:s0           112 Oct 23 11:32 .
drwx--x--x. 1 root root system_u:object_r:virt_var_lib_t:s0         118 Sep 28 14:43 ..
-rw-------. 1 qemu qemu system_u:object_r:virt_image_t:s0   21478375424 Oct 23 11:59 ArchPlasma.qcow2
-rw-------. 1 qemu qemu system_u:object_r:virt_image_t:s0   21478375424 Oct 23 11:31 ArchServer.qcow2
drwxr-xr-x. 2 root root system_u:object_r:virt_image_t:s0          4096 Oct 23 11:59 .snapshots_ArchPlasma
drwxr-xr-x. 2 root root system_u:object_r:virt_image_t:s0          4096 Oct 23 11:31 .snapshots_ArchServer 
 
############################################################
FEDORA 45 UNIVERSAL GLOBAL DISASTER RECOVERY ENGINE  (ZERO XML EDITS) 
############################################################ 
 #!/bin/bash

set -e
 
TARGET_MNT="/var/lib/libvirt/images"
VG_NAME="vg_fedora"
THIN_VOL="vol_fedora_thin"
SNAP_NAME="snap_working_vms"
GUEST_NAME="ArchServer" # Simply toggle this variable to match your target VM name
 
echo "=== PHASE 1: RE-ARM IMMUNE STORAGE MATRIX BASELINE ==="
sudo lvremove -f ${VG_NAME}/${SNAP_NAME} 2>/dev/null || true
sudo lvcreate -s --name ${SNAP_NAME} ${VG_NAME}/${THIN_VOL}
sudo lvs ${VG_NAME}
 
echo "=== PHASE 2: CATASTROPHIC DISASTER SIMULATION ==="
sudo virsh destroy ${GUEST_NAME} 2>/dev/null || true
 
echo "==> Waiting for hook script background backup copy to complete..."
while pgrep -f "cp --reflink=always" >/dev/null || systemctl is-active --quiet lvm-thin-snapshot.service; do
    sleep 2
done
echo "==> Hook script finished! Storage layer is quiet."

 
# Dynamically find and corrupt ONLY the disks belonging to the targeted VM
for disk in ${TARGET_MNT}/*"${GUEST_NAME}"*.qcow2; do
    [ ! -f "$disk" ] && continue
    echo "==> Corrupting: $(basename "$disk")"
    sudo dd if=/dev/urandom of="$disk" bs=1M count=500 conv=notrunc
done
 
echo "=== PHASE 3: SYNCHRONOUS WAIT-FOR-EXIT CODE FENCE ==="
sudo systemctl stop virtqemud.socket virtqemud-ro.socket virtqemud-admin.socket virtqemud.service 2>/dev/null || true
sudo pkill -9 qemu 2>/dev/null || true
sudo fuser -kM "${TARGET_MNT}" 2>/dev/null || true
 
echo "==> Executing standard physical unmount loop..."
COUNTER=1
while [ $COUNTER -le 20 ]; do
    if sudo umount ${TARGET_MNT} 2>/dev/null; then
        echo "==> SUCCESS: Filesystem detached cleanly with exit code 0."
        break
    fi
    sleep 3
    COUNTER=$((COUNTER + 1))
done
 
if [ $COUNTER -gt 20 ]; then
    exit 1
fi
 
echo "=== PHASE 4: ATOMIC METADATA MERGE ==="
sudo lvconvert --merge ${VG_NAME}/${SNAP_NAME}


echo "=== PHASE 5: PURGE TRANSIENT GRAPH FILES & FIX SECURITY ==="
sudo mount ${TARGET_MNT}
 
# 1. Force the kernel and storage layers to settle after the merge mount
sudo udevadm settle
sync
 
# 2. ISOLATED CLEANUP: Clear background tokens ONLY for the targeted guest
sudo rm -f ${TARGET_MNT}/*"${GUEST_NAME}"*.lock 2>/dev/null
sudo rm -f /var/run/libvirt/qemu/"${GUEST_NAME}".xml 2>/dev/null
sudo rm -rf /var/cache/libvirt/qemu/"${GUEST_NAME}" 2>/dev/null
sudo rm -rf /var/lib/libvirt/qemu/remembered/"${GUEST_NAME}" 2>/dev/null
 
# 3. FIXED & COMPREHENSIVE XATTR PURGE: Completely strip ALL hidden libvirt security keys
echo "==> Clearing all hidden libvirt security locks from ${GUEST_NAME}..."
for disk in ${TARGET_MNT}/*"${GUEST_NAME}"*.qcow2; do
    [ ! -f "$disk" ] && continue
    # This loops through and aggressively drops any trusted.libvirt keys frozen in the file metadata
    sudo getfattr -m "trusted.libvirt.security" --dump "$disk" 2>/dev/null | grep -oP '^trusted\.libvirt\.security\.[a-z_]+' | while read -r attr; do
        sudo setfattr -x "$attr" "$disk" 2>/dev/null || true
    done
done
 
# 4. TARGETED SECURITY REMAPPING: Process ONLY the targeted VM files
echo "==> Resetting permissions and SELinux contexts strictly for ${GUEST_NAME}..."
for disk in ${TARGET_MNT}/*"${GUEST_NAME}"*.qcow2; do
    [ ! -f "$disk" ] && continue
    sudo chown qemu:qemu "$disk"
    sudo chmod 600 "$disk"
 
    # Restore standard system-defined defaults so Libvirt can assign dynamic labels cleanly
    sudo restorecon -Fv "$disk"
done

 
# Restart systemd cgroup containment tracking profiles safely
sudo systemctl restart systemd-machined
sleep 2
 
# Re-activate the modular listener sockets
sudo systemctl start virtqemud.socket virtqemud-ro.socket virtqemud-admin.socket virtqemud.service
sleep 1
 
echo "=== PHASE 6: POWER ON RECOVERED GUEST ==="
# Verify if the domain state registry dropped during the socket tear-down
if sudo virsh list --all | grep -qw "${GUEST_NAME}"; then
    sudo virsh start ${GUEST_NAME}
else
    echo "==> Re-registering transient XML map for ${GUEST_NAME}..."
    sudo virsh define /etc/libvirt/qemu/"${GUEST_NAME}".xml 2>/dev/null || true
    sudo virsh start ${GUEST_NAME}
fi