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 

 

No comments:

Post a Comment