kcli Plan Authoring
Plan File Structure
Plans are YAML files with Jinja2 templating. Resources are defined as top-level keys with a type field.
parameters: param1: value1 param2: value2
resourcename: type: resourcetype key1: value1 key2: {{ param1 }}
Resource Types
VM (default if no type specified)
myvm: image: fedora40 memory: 4096 numcpus: 2 disks: - size: 20 - size: 10 pool: otherpool nets: - name: default - name: mynet ip: 192.168.1.10 netmask: 255.255.255.0 gateway: 192.168.1.1 cmds: - echo hello > /tmp/test files: - path: /etc/myconfig content: | key=value
Profile
myprofile: type: profile image: centos9stream memory: 2048 numcpus: 2 disks: - 10 nets: - default
Network
mynetwork: type: network cidr: 192.168.100.0/24 dhcp: true nat: true domain: mylab.local
Image
myimage: type: image url: https://example.com/image.qcow2 pool: default
Container
mycontainer: type: container image: nginx:latest ports: - 8080:80
Jinja2 Templating
Parameter Substitution
parameters: cluster_name: mycluster worker_count: 3
{{ cluster_name }}-master: image: rhcos
{% for i in range(worker_count) %} {{ cluster_name }}-worker-{{ i }}: image: rhcos {% endfor %}
Conditionals
parameters: enable_storage: true
myvm: image: fedora40 {% if enable_storage %} disks: - size: 100 {% endif %}
Custom Filters
kcli provides custom Jinja2 filters in kvirt/jinjafilters/jinjafilters.py :
Path/File Filters:
-
basename
-
Get filename from path
-
dirname
-
Get directory from path
-
diskpath
-
Convert to /dev/ path if needed
-
exists
-
Check if file/path exists
-
pwd_path
-
Handle workdir paths in containers
-
real_path
-
Get real/absolute path
-
read_file
-
Read file contents
String/Data Filters:
-
none
-
Return empty string if None
-
type
-
Return type name (string, int, dict, list)
-
base64
-
Base64 encode value
-
certificate
-
Wrap in BEGIN/END CERTIFICATE if needed
-
count
-
Count occurrences of character
Kubernetes/Cluster Filters:
-
kubenodes
-
Generate node names for cluster
-
defaultnodes
-
Generate default node list
-
has_ctlplane
-
Check if list has ctlplane/master entries
Version/Release Filters:
-
github_version
-
Get latest version from GitHub releases
-
min_ocp_version
-
Compare OpenShift versions (minimum)
-
max_ocp_version
-
Compare OpenShift versions (maximum)
Network Filters:
-
local_ip
-
Get local IP for network interface
-
network_ip
-
Get IP from network CIDR
-
ipv6_wrap
-
Wrap IPv6 addresses in brackets
Utility Filters:
-
kcli_info
-
Get VM info via kcli command
-
find_manifests
-
Find YAML manifests in directory
-
wait_crd
-
Generate wait script for CRD creation
-
wait_csv
-
Generate wait script for CSV readiness
-
filter_bgp_peers
-
Filter BGP peer list
Standard Jinja2 filters (default, join, upper, lower, etc.) also work
Parameter Files
Create kcli_parameters.yml alongside your plan:
cluster_name: prod worker_count: 5 memory: 8192
Override at runtime:
kcli create plan -f myplan.yml -P worker_count=10 myplan
Common VM Parameters
Parameter Default Description
numcpus
2 Number of CPUs
memory
512 Memory in MB
pool
default Storage pool
image
None Base image name
nets
[default] Network list
disks
[{size:10}] Disk list
cmds
[] Post-boot commands
files
[] Files to inject
keys
[] SSH public keys
start
true Auto-start VM
cloudinit
true Enable cloud-init
Plan Execution
Create plan
kcli create plan -f myplan.yml myplanname
Create with parameter overrides
kcli create plan -f myplan.yml -P memory=4096 -P image=fedora40 myplanname
List plans
kcli list plan
Get plan info
kcli info plan myplanname
Delete plan (and all its resources)
kcli delete plan myplanname
Update existing plan
kcli update plan -f myplan.yml myplanname
Debugging Plans
-
Validate YAML syntax - Use python -c "import yaml; yaml.safe_load(open('plan.yml'))"
-
Check Jinja2 rendering - Look for unbalanced {{ }} or {% %}
-
Run with debug - kcli -d create plan -f plan.yml test
-
Check dependencies - Ensure images/networks exist before VMs reference them
Example: Multi-VM Plan
parameters: domain: lab.local base_image: centos9stream
labnetwork: type: network cidr: 10.0.0.0/24 dhcp: true domain: {{ domain }}
webserver: image: {{ base_image }} memory: 2048 nets: - labnetwork cmds: - dnf -y install nginx - systemctl enable --now nginx
database: image: {{ base_image }} memory: 4096 disks: - size: 20 - size: 50 nets: - labnetwork cmds: - dnf -y install postgresql-server - postgresql-setup --initdb