# Kcli Plan Authoring

> Guides creation of kcli plan files for deploying VMs, networks, and infrastructure. Use when writing YAML plans with Jinja2 templating or debugging plan execution issues.

- Skill: `karmab/kcli-plan-authoring` (Agent Skill)
- Install (CLI): `npx skillmds@latest add karmab/kcli-plan-authoring`
- Raw SKILL.md: https://api.skillmd.com/api/skills/karmab/kcli-plan-authoring/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: karmab (https://skillmd.com/u/karmab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/karmab/kcli-plan-authoring

---


# kcli Plan Authoring

## Plan File Structure

Plans are YAML files with Jinja2 templating. Resources are defined as top-level keys with a `type` field.

```yaml
parameters:
  param1: value1
  param2: value2

resourcename:
  type: resourcetype
  key1: value1
  key2: {{ param1 }}
```

## Resource Types

### VM (default if no type specified)
```yaml
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
```yaml
myprofile:
  type: profile
  image: centos9stream
  memory: 2048
  numcpus: 2
  disks:
    - 10
  nets:
    - default
```

### Network
```yaml
mynetwork:
  type: network
  cidr: 192.168.100.0/24
  dhcp: true
  nat: true
  domain: mylab.local
```

### Image
```yaml
myimage:
  type: image
  url: https://example.com/image.qcow2
  pool: default
```

### Container
```yaml
mycontainer:
  type: container
  image: nginx:latest
  ports:
    - 8080:80
```

## Jinja2 Templating

### Parameter Substitution
```yaml
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
```yaml
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:
```yaml
cluster_name: prod
worker_count: 5
memory: 8192
```

Override at runtime:
```bash
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

```bash
# 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

1. **Validate YAML syntax** - Use `python -c "import yaml; yaml.safe_load(open('plan.yml'))"`
2. **Check Jinja2 rendering** - Look for unbalanced `{{` `}}` or `{% %}`
3. **Run with debug** - `kcli -d create plan -f plan.yml test`
4. **Check dependencies** - Ensure images/networks exist before VMs reference them

## Example: Multi-VM Plan

```yaml
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
```

