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
At a managed node:
In the playbook vbotka.freebsd.pb_iocage_plugins.yml:
Fetch the iocage plugin
ansible-zero
In the playbook vbotka.freebsd.pb_iocage_ansible_clients.yml:
Clone jails from the iocage plugin
ansible-zero
Create dynamic inventory to connect the jails by connection plugin vbotka.freebsd.jailexec
At all cloned jails:
In the playbook
pb-test.yml:Connect to the cloned jails
Display basic configuration of the jails.
Requirements
iocage plugin
ansible-zeroplaybook vbotka.freebsd.pb_iocage_plugins.yml
root privilege in the managed nodes
Notes
The iocage plugin
ansible-zerois used in the playbook vbotka.freebsd.pb_iocage_ansible_clients.yml to create theswarm.
See also
Example 050 Connection jailexec
GitHub repository iocage plugins
ansible.cfg
[defaults]
callback_result_format = yaml
deprecation_warnings=false
display_skipped_hosts = false
gathering = explicit
[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
properties:
bpf: 1
dhcp: 1
vnet: 1
notes: "vmm={{ inventory_hostname }}"
type: jail
swarms:
sw_01:
count: 3
plugin: ansible-zero
plugins:
ansible-zero:
git: https://github.com/vbotka/iocage-plugins-devel
branch: main
properties:
bpf: 1
dhcp: 1
vnet: 1
boot: 0
Note
By default, the jails cloned from the plugins inherit the iocage property type
pluginv2. Change it to jail.
Playbook output - Fetch plugins
(env) > ansible-playbook vbotka.freebsd.pb_iocage_plugins.yml \
-i iocage.ini \
-t swarm_plugins \
-e debug=true
PLAY [Fetch and stop iocage plugins.] ******************************************
TASK [One tag is required.] ****************************************************
ok: [iocage_05]
TASK [Test the tag is known.] **************************************************
ok: [iocage_05]
TASK [Get already fetched plugins.] ********************************************
ok: [iocage_05]
TASK [Debug fetched plugins debug=true] ****************************************
ok: [iocage_05] =>
msg: |-
iocage_plugins:
ansible-zero:
boot: 'off'
doc_url: '-'
ip4: '-'
ip4_dict:
ip4: []
msg: DHCP (not running)
ip6: '-'
jid: None
portal: '-'
release: 15.0-RELEASE
state: down
template: '-'
type: pluginv2
plugins:
ansible-zero:
branch: main
git: https://github.com/vbotka/iocage-plugins-devel
properties:
boot: 0
bpf: 1
dhcp: 1
vnet: 1
PLAY RECAP *********************************************************************
iocage_05 : ok=4 changed=0 unreachable=0 failed=0 skipped=5 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 | off | 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 notes="vmm=iocage_05 swarm=sw_01" type=jail vnet=1
TASK [Create swarms] ***********************************************************
ok: [iocage_05] => (item=sw_01)
TASK [Debug create swarms debug=true] ******************************************
ok: [iocage_05] =>
out:
changed: false
msg: All items completed
results:
- ansible_loop_var: item
changed: false
cmd:
- iocage
- clone
- ansible-zero
- --newmac
- --count
- '3'
- bpf=1
- dhcp=1
- notes=vmm=iocage_05 swarm=sw_01
- type=jail
- vnet=1
delta: '0:00:00.945023'
end: '2026-05-13 11:20:33.799872'
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 notes="vmm=iocage_05 swarm=sw_01" type=jail vnet=1
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-05-13 11:20:32.854849'
stderr: ''
stderr_lines: []
stdout: |-
53fa80de-b537-462f-9925-a3b17a122271 successfully cloned!
67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae successfully cloned!
a87e3dfe-ab10-46b9-9921-1f1adc2011fe successfully cloned!
stdout_lines:
- 53fa80de-b537-462f-9925-a3b17a122271 successfully cloned!
- 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae successfully cloned!
- a87e3dfe-ab10-46b9-9921-1f1adc2011fe 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 53fa80de-b537-462f-9925-a3b17a122271 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae a87e3dfe-ab10-46b9-9921-1f1adc2011fe
TASK [Start swarms] ************************************************************
ok: [iocage_05] => (item=sw_01)
TASK [Debug start swarms debug=true] *******************************************
ok: [iocage_05] =>
out:
changed: false
msg: All items completed
results:
- ansible_loop_var: item
changed: false
cmd:
- iocage
- start
- 53fa80de-b537-462f-9925-a3b17a122271
- 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae
- a87e3dfe-ab10-46b9-9921-1f1adc2011fe
delta: '0:00:07.520716'
end: '2026-05-13 11:20:48.315514'
failed: false
invocation:
module_args:
_raw_params: null
_uses_shell: false
argv: null
chdir: null
cmd: |-
iocage start 53fa80de-b537-462f-9925-a3b17a122271 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae a87e3dfe-ab10-46b9-9921-1f1adc2011fe
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-05-13 11:20:40.794798'
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 53fa80de-b537-462f-9925-a3b17a122271
+ 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.180/24
* Starting 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae
+ 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.181/24
* Starting a87e3dfe-ab10-46b9-9921-1f1adc2011fe
+ 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.182/24
stdout_lines:
- '* Starting 53fa80de-b537-462f-9925-a3b17a122271'
- ' + 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.180/24'
- '* Starting 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae'
- ' + 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.181/24'
- '* Starting a87e3dfe-ab10-46b9-9921-1f1adc2011fe'
- ' + 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.182/24'
skipped: false
PLAY RECAP *********************************************************************
iocage_05 : ok=10 changed=0 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 |
+======+======================================+======+=======+==========+==============+======================+=====+==============+==========+
| 39 | 53fa80de-b537-462f-9925-a3b17a122271 | off | up | jail | 15.0-RELEASE | epair0b|10.10.99.180 | - | ansible-zero | yes |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| 40 | 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae | off | up | jail | 15.0-RELEASE | epair0b|10.10.99.181 | - | ansible-zero | yes |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| 41 | a87e3dfe-ab10-46b9-9921-1f1adc2011fe | off | up | jail | 15.0-RELEASE | epair0b|10.10.99.182 | - | ansible-zero | yes |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
| None | ansible-zero | off | down | pluginv2 | 15.0-RELEASE | DHCP (not running) | - | - | yes |
+------+--------------------------------------+------+-------+----------+--------------+----------------------+-----+--------------+----------+
Inventory hosts
plugin: vbotka.freebsd.iocage
host: iocage_05
user: admin
sudo: true
get_properties: true
compose:
ansible_connection: "'vbotka.freebsd.jailexec'"
ansible_jail_host: dict(iocage_properties.notes | regex_findall('(\w+)=([\w\-]+)')).vmm | d('none')
ansible_jail_name: iocage_jid
ansible_jail_privilege_escalation: "'sudo'"
ansible_user: "'admin'"
ansible_python_interpreter: "'auto_silent'"
iocage_tags: dict(iocage_properties.notes | regex_findall('(\w+)=([\w\-]+)'))
Note
iocage name doesn’t work with ansible_jail_name. iocage jid must be used
instead.
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:
| |--53fa80de-b537-462f-9925-a3b17a122271
| |--67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae
| |--a87e3dfe-ab10-46b9-9921-1f1adc2011fe
|--@vmm_iocage_05:
| |--53fa80de-b537-462f-9925-a3b17a122271
| |--67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae
| |--a87e3dfe-ab10-46b9-9921-1f1adc2011fe
Playbook pb-test.yml
- name: Test the connection plugin jailexec.
hosts: swarm_sw_01
gather_facts: false
tasks:
- name: Get uname # noqa: no-changed-when
ansible.builtin.command: uname -a
register: out
- name: Debug
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 }}
uname:
{{ out.stdout }}
Playbook output - Test connection plugin jailexec
(env) > ansible-playbook pb-test.yml -i hosts
PLAY [Test the connection plugin jailexec.] ************************************
TASK [Get uname] ***************************************************************
changed: [53fa80de-b537-462f-9925-a3b17a122271]
changed: [67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae]
changed: [a87e3dfe-ab10-46b9-9921-1f1adc2011fe]
TASK [Debug] *******************************************************************
ok: [53fa80de-b537-462f-9925-a3b17a122271] =>
msg: |-
ansible_connection: vbotka.freebsd.jailexec
ansible_host: 53fa80de-b537-462f-9925-a3b17a122271
ansible_user: admin
ansible_jail_host: iocage_05
ansible_jail_privilege_escalation: sudo
iocage_ip4: 10.10.99.180
iocage_tags: {'vmm': 'iocage_05', 'swarm': 'sw_01'}
uname:
FreeBSD 53fa80de-b537-462f-9925-a3b17a122271 15.0-RELEASE FreeBSD 15.0-RELEASE releng/15.0-n280995-7aedc8de6446 GENERIC amd64
ok: [67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae] =>
msg: |-
ansible_connection: vbotka.freebsd.jailexec
ansible_host: 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae
ansible_user: admin
ansible_jail_host: iocage_05
ansible_jail_privilege_escalation: sudo
iocage_ip4: 10.10.99.181
iocage_tags: {'vmm': 'iocage_05', 'swarm': 'sw_01'}
uname:
FreeBSD 67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae 15.0-RELEASE FreeBSD 15.0-RELEASE releng/15.0-n280995-7aedc8de6446 GENERIC amd64
ok: [a87e3dfe-ab10-46b9-9921-1f1adc2011fe] =>
msg: |-
ansible_connection: vbotka.freebsd.jailexec
ansible_host: a87e3dfe-ab10-46b9-9921-1f1adc2011fe
ansible_user: admin
ansible_jail_host: iocage_05
ansible_jail_privilege_escalation: sudo
iocage_ip4: 10.10.99.182
iocage_tags: {'vmm': 'iocage_05', 'swarm': 'sw_01'}
uname:
FreeBSD a87e3dfe-ab10-46b9-9921-1f1adc2011fe 15.0-RELEASE FreeBSD 15.0-RELEASE releng/15.0-n280995-7aedc8de6446 GENERIC amd64
PLAY RECAP *********************************************************************
53fa80de-b537-462f-9925-a3b17a122271 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
67f95bed-b2b8-41e3-a7b1-d7476cb5a5ae : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
a87e3dfe-ab10-46b9-9921-1f1adc2011fe : ok=2 changed=1 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