443 Plugin ansible-zero

Use case

Clone multiple jails from the iocage plugin ansible-zero. Use the connection plugin vbotka.freebsd.jailexec to connect the jails.

Tree

shell> tree .
.
├── ansible.cfg
├── hosts
│   ├── 05_iocage.yml
│   └── 99_constructed.yml
├── host_vars
│   └── iocage_05.yml
├── iocage.ini
└── pb-test.yml

Synopsis

Requirements

Notes

See also

ansible.cfg

[defaults]
gathering = explicit
callback_result_format = yaml
display_skipped_hosts = false
deprecation_warnings=false

[connection]
pipelining = true

Inventory iocage.ini

iocage_05

[iocage]
iocage_05

[iocage:vars]
ansible_user=admin
ansible_become=true
ansible_python_interpreter=auto_silent

host_vars

host_vars/iocage_05.yml
properties:
  bpf: 1
  dhcp: 1
  vnet: 1
  notes: "vmm={{ inventory_hostname }}"

swarms:
  sw_01:
    count: 3
    plugin: ansible-zero

plugins:
  ansible-zero:
    git: https://github.com/vbotka/iocage-plugins
    options: bpf=1 dhcp=1 vnet=1

Playbook output - Fetch plugins

(env) > ansible-playbook vbotka.freebsd.pb_iocage_plugins.yml \
                         -i iocage.ini \
                         -t required_plugins \
                         -e debug=true
PLAY [Fetch and stop iocage plugins.] ******************************************

TASK [Get already fetched plugins.] ********************************************
ok: [iocage_05]

TASK [Debug fetched plugins debug=true] ****************************************
ok: [iocage_05] => 
    msg: |-
        iocage_plugins:
          {}

        plugins:
          ansible-zero:
            git: https://github.com/vbotka/iocage-plugins
            options: bpf=1 dhcp=1 vnet=1

TASK [Debug required plugins debug=true] ***************************************
ok: [iocage_05] => 
    msg: |-
        required_plugins:
          - ansible-zero

        selected_plugins:
          -   key: ansible-zero
            value:
                git: https://github.com/vbotka/iocage-plugins
                options: bpf=1 dhcp=1 vnet=1

TASK [Check all required plugins are included in the dictionary plugins.] ******
ok: [iocage_05]

TASK [Debug commands debug=true] ***********************************************
ok: [iocage_05] => (item=ansible-zero) => 
    msg: |-
        iocage fetch --plugin-name ansible-zero --git_repository https://github.com/vbotka/iocage-plugins bpf=1 dhcp=1 vnet=1

TASK [Fetch required plaugins.] ************************************************
changed: [iocage_05] => (item=ansible-zero)

TASK [Stop fetched plugins.] ***************************************************
changed: [iocage_05]

PLAY RECAP *********************************************************************
iocage_05                  : ok=7    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Note

The “Testing ansible-zero’s DNSSEC response to pkg.FreeBSD.org” by iocage fetch may take some time.

Plugins at iocage_05

[iocage_05]# iocage list -P
+------+--------------+------+-------+----------+--------------+--------------------+-----+----------+--------+---------+
| JID  |     NAME     | BOOT | STATE |   TYPE   |   RELEASE    |        IP4         | IP6 | TEMPLATE | PORTAL | DOC_URL |
+======+==============+======+=======+==========+==============+====================+=====+==========+========+=========+
| None | ansible-zero | on   | down  | pluginv2 | 15.0-RELEASE | DHCP (not running) | -   | -        | -      | -       |
+------+--------------+------+-------+----------+--------------+--------------------+-----+----------+--------+---------+

Playbook output - Create and start swarms

(env) > ansible-playbook vbotka.freebsd.pb_iocage_ansible_clients.yml \
                         -i iocage.ini \
                         -t swarm \
                         -e swarm=true \
                         -e debug=true
PLAY [Create and start jails. Optionally stop and destroy jails.] **************

TASK [Get iocage facts] ********************************************************
ok: [iocage_05]

TASK [Reject jails with empty notes] *******************************************
ok: [iocage_05]

TASK [Debug cmd_create debug=true] *********************************************
ok: [iocage_05] => (item=sw_01) => 
    msg: |-
        iocage clone ansible-zero --newmac --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_05 swarm=sw_01"

TASK [Create swarms] ***********************************************************
changed: [iocage_05] => (item=sw_01)

TASK [Debug create swarms debug=true] ******************************************
ok: [iocage_05] => 
    out:
        changed: true
        msg: All items completed
        results:
        -   ansible_loop_var: item
            changed: true
            cmd:
            - iocage
            - clone
            - ansible-zero
            - --newmac
            - --count
            - '3'
            - bpf=1
            - dhcp=1
            - vnet=1
            - notes=vmm=iocage_05 swarm=sw_01
            delta: '0:00:00.897302'
            end: '2026-04-23 09:40:13.174121'
            failed: false
            invocation:
                module_args:
                    _raw_params: null
                    _uses_shell: false
                    argv: null
                    chdir: null
                    cmd: |-
                        iocage clone ansible-zero --newmac --count 3 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_05 swarm=sw_01"
                    creates: null
                    executable: null
                    expand_argument_vars: true
                    removes: null
                    stdin: null
                    stdin_add_newline: true
                    strip_empty_ends: true
            item:
                key: sw_01
                value:
                    count: 3
                    plugin: ansible-zero
            msg: ''
            rc: 0
            start: '2026-04-23 09:40:12.276819'
            stderr: ''
            stderr_lines: []
            stdout: |-
                4ddecd35-83a8-43fe-97c0-26ce93a9fb51 successfully cloned!
                a59dc6c6-fda8-432a-95ae-fedb9bf2313e successfully cloned!
                bd21d433-2685-4b53-be96-4b45cbb77ddc successfully cloned!
            stdout_lines:
            - 4ddecd35-83a8-43fe-97c0-26ce93a9fb51 successfully cloned!
            - a59dc6c6-fda8-432a-95ae-fedb9bf2313e successfully cloned!
            - bd21d433-2685-4b53-be96-4b45cbb77ddc successfully cloned!
        skipped: false

TASK [Get iocage facts] ********************************************************
ok: [iocage_05]

TASK [Reject jails with empty notes] *******************************************
ok: [iocage_05]

TASK [Debug cmd_start debug=true] **********************************************
ok: [iocage_05] => (item=sw_01) => 
    msg: |-
        iocage start a59dc6c6-fda8-432a-95ae-fedb9bf2313e 4ddecd35-83a8-43fe-97c0-26ce93a9fb51 bd21d433-2685-4b53-be96-4b45cbb77ddc

TASK [Start swarms] ************************************************************
changed: [iocage_05] => (item=sw_01)

TASK [Debug start swarms debug=true] *******************************************
ok: [iocage_05] => 
    out:
        changed: true
        msg: All items completed
        results:
        -   ansible_loop_var: item
            changed: true
            cmd:
            - iocage
            - start
            - a59dc6c6-fda8-432a-95ae-fedb9bf2313e
            - 4ddecd35-83a8-43fe-97c0-26ce93a9fb51
            - bd21d433-2685-4b53-be96-4b45cbb77ddc
            delta: '0:00:07.051195'
            end: '2026-04-23 09:40:26.189221'
            failed: false
            invocation:
                module_args:
                    _raw_params: null
                    _uses_shell: false
                    argv: null
                    chdir: null
                    cmd: |-
                        iocage start a59dc6c6-fda8-432a-95ae-fedb9bf2313e 4ddecd35-83a8-43fe-97c0-26ce93a9fb51 bd21d433-2685-4b53-be96-4b45cbb77ddc
                    creates: null
                    executable: null
                    expand_argument_vars: true
                    removes: null
                    stdin: null
                    stdin_add_newline: true
                    strip_empty_ends: true
            item:
                key: sw_01
                value:
                    count: 3
                    plugin: ansible-zero
            msg: ''
            rc: 0
            start: '2026-04-23 09:40:19.138026'
            stderr: |-
                No default gateway found for ipv6.
                No default gateway found for ipv6.
                No default gateway found for ipv6.
            stderr_lines:
            - No default gateway found for ipv6.
            - No default gateway found for ipv6.
            - No default gateway found for ipv6.
            stdout: |-
                * Starting a59dc6c6-fda8-432a-95ae-fedb9bf2313e
                  + Started OK
                  + Using devfs_ruleset: 1000 (iocage generated default)
                  + Configuring VNET OK
                  + Using IP options: vnet
                  + Starting services OK
                  + Executing poststart OK
                  + DHCP Address: 10.10.99.120/24
                * Starting 4ddecd35-83a8-43fe-97c0-26ce93a9fb51
                  + Started OK
                  + Using devfs_ruleset: 1001 (iocage generated default)
                  + Configuring VNET OK
                  + Using IP options: vnet
                  + Starting services OK
                  + Executing poststart OK
                  + DHCP Address: 10.10.99.121/24
                * Starting bd21d433-2685-4b53-be96-4b45cbb77ddc
                  + Started OK
                  + Using devfs_ruleset: 1002 (iocage generated default)
                  + Configuring VNET OK
                  + Using IP options: vnet
                  + Starting services OK
                  + Executing poststart OK
                  + DHCP Address: 10.10.99.122/24
            stdout_lines:
            - '* Starting a59dc6c6-fda8-432a-95ae-fedb9bf2313e'
            - '  + Started OK'
            - '  + Using devfs_ruleset: 1000 (iocage generated default)'
            - '  + Configuring VNET OK'
            - '  + Using IP options: vnet'
            - '  + Starting services OK'
            - '  + Executing poststart OK'
            - '  + DHCP Address: 10.10.99.120/24'
            - '* Starting 4ddecd35-83a8-43fe-97c0-26ce93a9fb51'
            - '  + Started OK'
            - '  + Using devfs_ruleset: 1001 (iocage generated default)'
            - '  + Configuring VNET OK'
            - '  + Using IP options: vnet'
            - '  + Starting services OK'
            - '  + Executing poststart OK'
            - '  + DHCP Address: 10.10.99.121/24'
            - '* Starting bd21d433-2685-4b53-be96-4b45cbb77ddc'
            - '  + Started OK'
            - '  + Using devfs_ruleset: 1002 (iocage generated default)'
            - '  + Configuring VNET OK'
            - '  + Using IP options: vnet'
            - '  + Starting services OK'
            - '  + Executing poststart OK'
            - '  + DHCP Address: 10.10.99.122/24'
        skipped: false

PLAY RECAP *********************************************************************
iocage_05                  : ok=10   changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   

Jails at iocage_05

[iocage_05]# iocage list -l
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| JID  |                 NAME                 | BOOT | STATE |   TYPE   |   RELEASE    |         IP4          | IP6 |   TEMPLATE   | BASEJAIL |
+======+======================================+======+=======+==========+==============+======================+=====+==============+==========+
| 6    | 4ddecd35-83a8-43fe-97c0-26ce93a9fb51 | on   | up    | pluginv2 | 15.0-RELEASE | epair0b|10.10.99.121 | -   | ansible-zero | yes      |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| 5    | a59dc6c6-fda8-432a-95ae-fedb9bf2313e | on   | up    | pluginv2 | 15.0-RELEASE | epair0b|10.10.99.120 | -   | ansible-zero | yes      |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| None | ansible-zero                         | on   | down  | pluginv2 | 15.0-RELEASE | DHCP (not running)   | -   | -            | yes      |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| 7    | bd21d433-2685-4b53-be96-4b45cbb77ddc | on   | up    | pluginv2 | 15.0-RELEASE | epair0b|10.10.99.122 | -   | ansible-zero | yes      |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+

Inventory hosts

hosts/05_iocage.yml
plugin: vbotka.freebsd.iocage
host: iocage_05
user: admin
sudo: true
get_properties: true
hooks_results:
  - /var/db/dhclient-hook.address.epair0b
compose:
  ansible_connection: "'vbotka.freebsd.jailexec'"
  ansible_jail_host: dict(iocage_properties.notes | regex_findall('(\w+)=(\w+)')).vmm | d('none')
  ansible_jail_privilege_escalation: "'sudo'"
  ansible_user: "'admin'"
  ansible_python_interpreter: "'auto_silent'"
  iocage_tags: dict(iocage_properties.notes | regex_findall('(\w+)=(\w+)'))
hosts/99_constructed.yml
plugin: ansible.builtin.constructed
keyed_groups:
  - prefix: swarm
    key: iocage_tags.swarm
  - prefix: vmm
    key: iocage_tags.vmm

Display inventory

(env) > ansible-inventory -i hosts --graph
@all:
  |--@ungrouped:
  |  |--ansible-zero
  |--@swarm_sw_01:
  |  |--4ddecd35-83a8-43fe-97c0-26ce93a9fb51
  |  |--a59dc6c6-fda8-432a-95ae-fedb9bf2313e
  |  |--bd21d433-2685-4b53-be96-4b45cbb77ddc
  |--@vmm_iocage_05:
  |  |--4ddecd35-83a8-43fe-97c0-26ce93a9fb51
  |  |--a59dc6c6-fda8-432a-95ae-fedb9bf2313e
  |  |--bd21d433-2685-4b53-be96-4b45cbb77ddc

Playbook pb-test.yml

- name: Test the connection plugin jailexec.
  hosts: swarm_sw_01
  gather_facts: false
    
  tasks:
    
    - ansible.builtin.debug:
        msg: |
          ansible_connection: {{ ansible_connection }}
          ansible_host: {{ ansible_host }}
          ansible_user: {{ ansible_user }}

          ansible_jail_host: {{ ansible_jail_host }}
          ansible_jail_privilege_escalation: {{ ansible_jail_privilege_escalation }}

          iocage_ip4: {{ iocage_ip4 }}
          iocage_tags: {{ iocage_tags }}

Playbook output - Test jailexec connection plugin

(env) > ansible-playbook pb-test.yml -i hosts
PLAY [Test the connection plugin jailexec.] ************************************

TASK [ansible.builtin.debug] ***************************************************
ok: [4ddecd35-83a8-43fe-97c0-26ce93a9fb51] => 
    msg: |-
        ansible_connection: vbotka.freebsd.jailexec
        ansible_host: 4ddecd35-83a8-43fe-97c0-26ce93a9fb51
        ansible_user: admin

        ansible_jail_host: iocage_05
        ansible_jail_privilege_escalation: sudo

        iocage_ip4: 10.10.99.121
        iocage_tags: {'vmm': 'iocage_05', 'swarm': 'sw_01'}
ok: [bd21d433-2685-4b53-be96-4b45cbb77ddc] => 
    msg: |-
        ansible_connection: vbotka.freebsd.jailexec
        ansible_host: bd21d433-2685-4b53-be96-4b45cbb77ddc
        ansible_user: admin

        ansible_jail_host: iocage_05
        ansible_jail_privilege_escalation: sudo

        iocage_ip4: 10.10.99.122
        iocage_tags: {'vmm': 'iocage_05', 'swarm': 'sw_01'}
ok: [a59dc6c6-fda8-432a-95ae-fedb9bf2313e] => 
    msg: |-
        ansible_connection: vbotka.freebsd.jailexec
        ansible_host: a59dc6c6-fda8-432a-95ae-fedb9bf2313e
        ansible_user: admin

        ansible_jail_host: iocage_05
        ansible_jail_privilege_escalation: sudo

        iocage_ip4: 10.10.99.120
        iocage_tags: {'vmm': 'iocage_05', 'swarm': 'sw_01'}

PLAY RECAP *********************************************************************
4ddecd35-83a8-43fe-97c0-26ce93a9fb51 : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
a59dc6c6-fda8-432a-95ae-fedb9bf2313e : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
bd21d433-2685-4b53-be96-4b45cbb77ddc : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Hint

The below play stops and destroys the jails in swarms

ansible-playbook vbotka.freebsd.pb_iocage_ansible_clients.yml \
                 -i iocage.ini \
                 -t swarm_destroy \
                 -e swarm_destroy=true