206 Create DHCP and fixed IP jails¶
Extending example 203 Create DHCP jails with auto UUID and iocage_tags.
Use case¶
In the inventory plugin vbotka.freebsd.iocage configuration file, use the option
hooks_results to get the DHCP IP address. This option is common for all jails in this example
hooks_results:
- /var/db/dhclient-hook.address.epair0b
It will silently fail in jails with fixed IP addresses. If the item fails, the result is the dash character ‘-’
iocage_hooks:
- '-'
This use case demonstrates the advantage of silently ignoring failed items over the potential
explicit error handling. Let the option compose pick what is needed
compose:
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
Fixed IP
One jail with fixed IP will be created from the template ansible_client in this example
clones:
test_131:
clone_from: ansible_client
properties:
ip4_addr: "em0|10.1.0.131/24"
notes: "swarm=sw_01"
Automatically generated UUID
Two DHCP jails with generated UUID will be created from the template ansible_client
swarms:
sw_01:
count: 3
template: ansible_client
properties:
bpf: 1
dhcp: 1
vnet: 1
Note
The clone test_131 belongs to the swarm sw_01. Set count: 3 to create two more jails
in the swarm sw_01.
The module vbotka.freebsd.iocage doesn’t work with multiple names. We will use
ansible.builtin.command instead. Anyway, such a task is not idempotent if the UUID is generated
automatically. Example of the commands
shell> iocage create --short --template ansible_client --count 2 bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_02 swarm=sw_01"
shell> iocage start cd31c2a2 d254f889
The variable iocage_tags
The inventory plugin composes the variable iocage_tags
iocage_tags: dict(iocage_properties.notes | split | map('split', '='))
For example,
iocage_tags:
vmm: iocage_04
swarm: sw_01
This dictionary is used to create groups
keyed_groups:
- prefix: swarm
key: iocage_tags.swarm
- prefix: vmm
key: iocage_tags.vmm
Tree¶
shell> tree .
.
├── ansible.cfg
├── hosts
│ └── 04_iocage.yml
├── host_vars
│ └── iocage_04
│ └── iocage.yml
├── iocage.ini
└── pb-test.yml
Synopsis¶
At one managed node:
iocage_04
In the playbook vbotka.freebsd.pb_iocage_ansible_clients.yml, use:
module vbotka.freebsd.iocage to:
create one jail with fixed IP
start the jail
module
ansible.builtin.commandto:create two DHCP jails with generated UUID
start the jails
At all created jails:
In the playbook
pb-test.yml:connect to the created jails
display basic configuration of the jails.
Requirements¶
root privilege in the managed nodes
templates created in 202 Create iocage templates. Clone DHCP jails.
Notes¶
Templates created in 202 Create iocage templates. Clone DHCP jails. are used in this example.
The dash “-” is used in binary iocage to represent a missing value. See for example:
inventory plugin vbotka.freebsd.iocage uses it too
if iocage_ip4_dict['ip4']: iocage_ip4 = ','.join([d['ip'] for d in iocage_ip4_dict['ip4']]) else: iocage_ip4 = '-'
See also
Templates at iocage_04¶
[iocage_04]# iocage list -lt
+------+----------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+======+================+======+=======+==========+=================+====================+=====+==========+==========+
| None | ansible_client | off | down | template | 14.3-RELEASE-p1 | DHCP (not running) | - | - | no |
+------+----------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
ansible.cfg¶
[defaults]
gathering = explicit
callback_result_format = yaml
display_skipped_hosts = false
[connection]
pipelining = true
Inventory iocage.ini¶
iocage_02 ansible_host=10.1.0.73
iocage_04 ansible_host=10.1.0.29
[iocage]
iocage_04
[iocage:vars]
ansible_user=admin
ansible_become=true
ansible_python_interpreter=auto_silent
host_vars¶
properties:
notes: "vmm={{ inventory_hostname }}"
clones:
test_131:
clone_from: ansible_client
properties:
bpf: 1
vnet: 1
ip4_addr: 'vnet0|10.1.0.131/24'
notes: "swarm=sw_01"
swarms:
sw_01:
count: 3
template: ansible_client
properties:
bpf: 1
dhcp: 1
vnet: 1
start: [test_131]
Create and start clones¶
(env) > ansible-playbook vbotka.freebsd.pb_iocage_ansible_clients.yml -i iocage.ini \
-t clone -e clone=true
PLAY [Create and start jails. Optionally stop and destroy jails.] **************
TASK [Create clones from template] *********************************************
changed: [iocage_04] => (item=test_131 ansible_client)
TASK [Start clones] ************************************************************
changed: [iocage_04] => (item=test_131)
PLAY RECAP *********************************************************************
iocage_04 : ok=2 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
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_04]
TASK [Debug cmd_create debug=true] *********************************************
ok: [iocage_04] => (item=sw_01) =>
msg: |-
iocage create --short --template ansible_client --count 2 notes="vmm=iocage_04 swarm=sw_01" bpf=1 dhcp=1 vnet=1
TASK [Create swarms] ***********************************************************
changed: [iocage_04] => (item=sw_01)
TASK [Debug create swarms debug=true] ******************************************
ok: [iocage_04] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- create
- --short
- --template
- ansible_client
- --count
- '2'
- notes=vmm=iocage_04 swarm=sw_01
- bpf=1
- dhcp=1
- vnet=1
delta: '0:00:00.931977'
end: '2025-08-07 22:49:53.853083'
failed: false
invocation:
module_args:
_raw_params: |-
iocage create --short --template ansible_client --count 2 notes="vmm=iocage_04 swarm=sw_01" bpf=1 dhcp=1 vnet=1
_uses_shell: false
argv: null
chdir: null
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
properties:
bpf: 1
dhcp: 1
vnet: 1
template: ansible_client
msg: ''
rc: 0
start: '2025-08-07 22:49:52.921106'
stderr: ''
stderr_lines: []
stdout: |-
158c7d67 successfully created!
0c534698 successfully created!
stdout_lines:
- 158c7d67 successfully created!
- 0c534698 successfully created!
skipped: false
TASK [Get iocage facts] ********************************************************
ok: [iocage_04]
TASK [Debug cmd_start debug=true] **********************************************
ok: [iocage_04] => (item=sw_01) =>
msg: |-
iocage start 0c534698 158c7d67
TASK [Start swarms] ************************************************************
changed: [iocage_04] => (item=sw_01)
TASK [Debug start swarms debug=true] *******************************************
ok: [iocage_04] =>
out:
changed: true
msg: All items completed
results:
- ansible_loop_var: item
changed: true
cmd:
- iocage
- start
- 0c534698
- 158c7d67
delta: '0:00:08.730278'
end: '2025-08-07 22:50:07.034368'
failed: false
invocation:
module_args:
_raw_params: |-
iocage start 0c534698 158c7d67
_uses_shell: false
argv: null
chdir: null
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
properties:
bpf: 1
dhcp: 1
vnet: 1
template: ansible_client
msg: ''
rc: 0
start: '2025-08-07 22:49:58.304090'
stderr: |-
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.
stdout: |-
* Starting 0c534698
+ Started OK
+ Using devfs_ruleset: 1007 (iocage generated default)
+ Configuring VNET OK
+ Using IP options: vnet
+ Starting services OK
+ Executing poststart OK
+ DHCP Address: 10.1.0.204/24
* Starting 158c7d67
+ Started OK
+ Using devfs_ruleset: 1009 (iocage generated default)
+ Configuring VNET OK
+ Using IP options: vnet
+ Starting services OK
+ Executing poststart OK
+ DHCP Address: 10.1.0.122/24
stdout_lines:
- '* Starting 0c534698'
- ' + Started OK'
- ' + Using devfs_ruleset: 1007 (iocage generated default)'
- ' + Configuring VNET OK'
- ' + Using IP options: vnet'
- ' + Starting services OK'
- ' + Executing poststart OK'
- ' + DHCP Address: 10.1.0.204/24'
- '* Starting 158c7d67'
- ' + Started OK'
- ' + Using devfs_ruleset: 1009 (iocage generated default)'
- ' + Configuring VNET OK'
- ' + Using IP options: vnet'
- ' + Starting services OK'
- ' + Executing poststart OK'
- ' + DHCP Address: 10.1.0.122/24'
skipped: false
PLAY RECAP *********************************************************************
iocage_04 : ok=8 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Jails at iocage_04¶
[iocage_04]# iocage list -l
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+=====+==========+======+=======+======+=================+=====================+=====+================+==========+
| 80 | 0c534698 | off | up | jail | 14.3-RELEASE-p1 | epair0b|10.1.0.204 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
| 81 | 158c7d67 | off | up | jail | 14.3-RELEASE-p1 | epair0b|10.1.0.122 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
| 79 | test_131 | off | up | jail | 14.3-RELEASE-p1 | vnet0|10.1.0.131/24 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+---------------------+-----+----------------+----------+
Inventory hosts¶
plugin: vbotka.freebsd.iocage
host: 10.1.0.29
user: admin
get_properties: True
hooks_results:
- /var/db/dhclient-hook.address.epair0b
compose:
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
iocage_tags: dict(iocage_properties.notes | split | map('split', '='))
keyed_groups:
- prefix: swarm
key: iocage_tags.swarm
- prefix: vmm
key: iocage_tags.vmm
Note
The option “get_properties: True” is needed to compose the dictionary iocage_tags
Display inventory¶
(env) > ansible-inventory -i hosts --graph
@all:
|--@ungrouped:
|--@swarm_sw_01:
| |--0c534698
| |--158c7d67
| |--test_131
|--@vmm_iocage_04:
| |--0c534698
| |--158c7d67
| |--test_131
Playbook pb-test.yml¶
- hosts: swarm_sw_01
remote_user: admin
vars:
ansible_python_interpreter: auto_silent
tasks:
- ansible.builtin.command: hostname
register: out
- ansible.builtin.debug:
msg: |
out.stdout: {{ out.stdout }}
ansible_host: {{ ansible_host }}
iocage_hooks: {{ iocage_hooks }}
iocage_tags:
{{ iocage_tags | to_nice_yaml(2) | indent(2) }}
Playbook output - Display jails in the swarm¶
(env) > ansible-playbook pb-test.yml -i hosts
PLAY [swarm_sw_01] *************************************************************
TASK [ansible.builtin.command] *************************************************
changed: [158c7d67]
changed: [0c534698]
changed: [test_131]
TASK [ansible.builtin.debug] ***************************************************
ok: [0c534698] =>
msg: |-
out.stdout: 0c534698
ansible_host: 10.1.0.204
iocage_hooks: ['10.1.0.204']
iocage_tags:
swarm: sw_01
vmm: iocage_04
ok: [158c7d67] =>
msg: |-
out.stdout: 158c7d67
ansible_host: 10.1.0.122
iocage_hooks: ['10.1.0.122']
iocage_tags:
swarm: sw_01
vmm: iocage_04
ok: [test_131] =>
msg: |-
out.stdout: test-131
ansible_host: 10.1.0.131
iocage_hooks: ['-']
iocage_tags:
swarm: sw_01
vmm: iocage_04
PLAY RECAP *********************************************************************
0c534698 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
158c7d67 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_131 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Hint
The below command 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