203 Create DHCP jails from template. Auto UUID, iocage_tags

Extending example 202 Create Ansible client templates and clone DHCP Ansible client jails.

Use case

Automatically generated UUID

Generate automatically the jails UUID names. At each iocage host three jails will be created from the template ansible_client

swarms:
  sw_01:
    count: 3
    template: ansible_client

The module vbotka.freebsd.iocage doesn’t work with multiple names. Use ansible.builtin.command instead. If the UUID is generated automatically such a task is not idempotent anyway. Example of the commands

iocage create --short --template ansible_client --count 3  bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_01 swarm=sw_01"
iocage start cd31c2a2 d254f889 158ef36d

The variable iocage_tags

In the inventory plugin, compose the variable iocage_tags

iocage_tags: dict(iocage_properties.notes | split | map('split', '='))

For example,

iocage_tags:
  vmm: iocage_01
  swarm: sw_01

Create groups from iocage_tags

keyed_groups:
- prefix: swarm
  key: iocage_tags.swarm
- prefix: vmm
  key: iocage_tags.vmm

Tree

shell> tree .
.
├── ansible.cfg
├── group_vars
│   └── all
│       └── iocage.yml
├── hosts
│   ├── 01_iocage.yml
│   ├── 02_iocage.yml
│   └── 03_constructed.yml
├── host_vars
│   ├── iocage_01
│   │   └── iocage.yml
│   └── iocage_02
│       └── iocage.yml
├── iocage-hosts.ini
├── pb-iocage-ansible-clients -> ../../../../playbooks/pb-iocage-ansible-clients
├── pb-iocage-ansible-clients.yml -> ../../../../playbooks/pb-iocage-ansible-clients.yml
└── pb-test-01.yml

Synopsis

  • On two iocage hosts:

    • iocage_01

    • iocage_02

    In the playbook pb-iocage-ansible-clients.yml, use the module:

    • vbotka.freebsd.iocage to:

      • create facts only

    • ansible.builtin.command to:

      • create jails

      • start jails

      • optionally stop and destroy the jails.

  • On all created jails:

    In the playbook pb-test-01.yml:

    • connect created jails

    • display basic configuration of the jails.

Requirements

Notes

See also

List templates at iocage_01

[iocage_01]# iocage list -lt
+------+----------------+------+-------+----------+-----------------+---------------------+-----+----------+----------+
| JID  |      NAME      | BOOT | STATE |   TYPE   |     RELEASE     |         IP4         | IP6 | TEMPLATE | BASEJAIL |
+======+================+======+=======+==========+=================+=====================+=====+==========+==========+
| None | ansible_client | off  | down  | template | 13.4-RELEASE-p2 | vnet0|10.1.0.198/24 | -   | -        | no       |
+------+----------------+------+-------+----------+-----------------+---------------------+-----+----------+----------+

List templates at iocage_02

[iocage_02]# iocage list -lt
| JID  |      NAME      | BOOT | STATE |   TYPE   |     RELEASE     |        IP4        | IP6 | TEMPLATE | BASEJAIL |
+======+================+======+=======+==========+=================+===================+=====+==========+==========+
| None | ansible_client | off  | down  | template | 14.1-RELEASE-p6 | em0|10.1.0.199/24 | -   | -        | no       |
+------+----------------+------+-------+----------+-----------------+-------------------+-----+----------+----------+

Configuration ansible.cfg

[defaults]
gathering = explicit
callback_result_format = yaml

[connection]
pipelining = true

group_vars/all/iocage.yml

properties:
  bpf: 1
  dhcp: 1
  vnet: 1
  notes: "vmm={{ inventory_hostname }}"

swarms:
  sw_01:
    count: 3
    template: ansible_client

host_vars/iocage_01/iocage.yml

ansible_python_interpreter: /usr/local/bin/python3.9
ansible_become_password: admin

host_vars/iocage_02/iocage.yml

ansible_python_interpreter: /usr/local/bin/python3.11

freebsd_iocage_runner_env:
  CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1

Inventory iocage-hosts.ini

iocage_01 ansible_host=10.1.0.18
iocage_02 ansible_host=10.1.0.73

[iocage]
iocage_01
iocage_02

[iocage:vars]
ansible_user=admin
ansible_become=true

Playbook pb-iocage-ansible-clients.yml

- name: Create and start jails. Optionally stop and destroy jails.
  hosts: iocage
  environment: "{{ freebsd_iocage_runner_env | d({}) }}"

  vars:

    debug: false
    debug2: false
    dry_run: false

    swarm: false
    swarm_destroy: false
    clone: false
    clone_destroy: false

    _tags: "{{ dict(iocage_jails.keys()
                    | zip(iocage_jails.values()
                          | map(attribute='properties.notes')
                          | map('split')
                          | map('map', 'split', '=')
                          | map('community.general.dict'))) }}"
    _started: "{{ iocage_jails | dict2items
                               | selectattr('value.state', 'eq', 'up')
                               | map(attribute='key') }}"

  tasks:

    - name: Create and start swarms
      when: swarm | bool
      tags: swarm
      ansible.builtin.import_tasks: pb-iocage-ansible-clients/swarm.yml

    - name: Stop and destroy swarms
      when: swarm_destroy | bool
      tags: swarm_destroy
      ansible.builtin.import_tasks: pb-iocage-ansible-clients/swarm_destroy.yml

    - name: Create and start clones
      when: clone | bool
      tags: clone
      ansible.builtin.import_tasks: pb-iocage-ansible-clients/clone.yml

    - name: Stop and destroy clones
      when: clone_destroy | bool
      tags: clone_destroy
      ansible.builtin.import_tasks: pb-iocage-ansible-clients/clone_destroy.yml

    - name: Display iocage_jails
      when: debug | bool or debug2 | bool
      tags: list
      ansible.builtin.import_tasks: pb-iocage-ansible-clients/list.yml

Playbook output

(env) > ansible-playbook pb-iocage-ansible-clients.yml -i iocage-hosts.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_01]
ok: [iocage_02]

TASK [Debug iocage_jails] **********************************************************************
skipping: [iocage_01]
skipping: [iocage_02]

TASK [Debug _tags] *****************************************************************************
ok: [iocage_01] => 
    _tags: {}
ok: [iocage_02] => 
    _tags: {}

TASK [Debug cmd_create] ************************************************************************
ok: [iocage_01] => (item=sw_01) => 
    msg: |-
        iocage create --short --template ansible_client --count 3  bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_01 swarm=sw_01"
ok: [iocage_02] => (item=sw_01) => 
    msg: |-
        iocage create --short --template ansible_client --count 3  bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_02 swarm=sw_01"

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

TASK [Debug create swarms] *********************************************************************
ok: [iocage_01] => 
    out:
        changed: true
        msg: All items completed
        results:
        -   ansible_loop_var: item
            changed: true
            cmd:
            - iocage
            - create
            - --short
            - --template
            - ansible_client
            - --count
            - '3'
            - bpf=1
            - dhcp=1
            - vnet=1
            - notes=vmm=iocage_01 swarm=sw_01
            delta: '0:00:03.604835'
            end: '2025-02-03 11:41:15.297467'
            failed: false
            invocation:
                module_args:
                    _raw_params: |-
                        iocage create --short --template ansible_client --count 3  bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_01 swarm=sw_01"
                    _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
                    template: ansible_client
            msg: ''
            rc: 0
            start: '2025-02-03 11:41:11.692632'
            stderr: ''
            stderr_lines: []
            stdout: |-
                158ef36d successfully created!
                d254f889 successfully created!
                cd31c2a2 successfully created!
            stdout_lines:
            - 158ef36d successfully created!
            - d254f889 successfully created!
            - cd31c2a2 successfully created!
        skipped: false
ok: [iocage_02] => 
    out:
        changed: true
        msg: All items completed
        results:
        -   ansible_loop_var: item
            changed: true
            cmd:
            - iocage
            - create
            - --short
            - --template
            - ansible_client
            - --count
            - '3'
            - bpf=1
            - dhcp=1
            - vnet=1
            - notes=vmm=iocage_02 swarm=sw_01
            delta: '0:00:04.505011'
            end: '2025-02-03 11:41:08.692741'
            failed: false
            invocation:
                module_args:
                    _raw_params: |-
                        iocage create --short --template ansible_client --count 3  bpf=1 dhcp=1 vnet=1 notes="vmm=iocage_02 swarm=sw_01"
                    _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
                    template: ansible_client
            msg: ''
            rc: 0
            start: '2025-02-03 11:41:04.187730'
            stderr: ''
            stderr_lines: []
            stdout: |-
                83707231 successfully created!
                2746353a successfully created!
                08daa493 successfully created!
            stdout_lines:
            - 83707231 successfully created!
            - 2746353a successfully created!
            - 08daa493 successfully created!
        skipped: false

TASK [Get iocage facts] ************************************************************************
ok: [iocage_01]
ok: [iocage_02]

TASK [Debug iocage_jails] **********************************************************************
skipping: [iocage_01]
skipping: [iocage_02]

TASK [Debug cmd_start] *************************************************************************
ok: [iocage_01] => (item=sw_01) => 
    msg: |-
        iocage start cd31c2a2 d254f889 158ef36d
ok: [iocage_02] => (item=sw_01) => 
    msg: |-
        iocage start 08daa493 2746353a 83707231

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

TASK [Debug start swarms] **********************************************************************
ok: [iocage_01] => 
    out:
        changed: true
        msg: All items completed
        results:
        -   ansible_loop_var: item
            changed: true
            cmd:
            - iocage
            - start
            - cd31c2a2
            - d254f889
            - 158ef36d
            delta: '0:00:35.361078'
            end: '2025-02-03 11:42:12.694764'
            failed: false
            invocation:
                module_args:
                    _raw_params: |-
                        iocage start cd31c2a2 d254f889 158ef36d
                    _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
                    template: ansible_client
            msg: ''
            rc: 0
            start: '2025-02-03 11:41:37.333686'
            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 cd31c2a2
                  + 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.1.0.142/24
                * Starting d254f889
                  + 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.1.0.137/24
                * Starting 158ef36d
                  + 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.1.0.115/24
            stdout_lines:
            - '* Starting cd31c2a2'
            - '  + 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.1.0.142/24'
            - '* Starting d254f889'
            - '  + 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.1.0.137/24'
            - '* Starting 158ef36d'
            - '  + 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.1.0.115/24'
        skipped: false
ok: [iocage_02] => 
    out:
        changed: true
        msg: All items completed
        results:
        -   ansible_loop_var: item
            changed: true
            cmd:
            - iocage
            - start
            - 08daa493
            - 2746353a
            - '83707231'
            delta: '0:00:36.656088'
            end: '2025-02-03 11:42:06.493072'
            failed: false
            invocation:
                module_args:
                    _raw_params: |-
                        iocage start 08daa493 2746353a 83707231
                    _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
                    template: ansible_client
            msg: ''
            rc: 0
            start: '2025-02-03 11:41:29.836984'
            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 08daa493
                  + 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.1.0.114/24
                * Starting 2746353a
                  + 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.1.0.187/24
                * Starting 83707231
                  + 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.1.0.233/24
            stdout_lines:
            - '* Starting 08daa493'
            - '  + 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.1.0.114/24'
            - '* Starting 2746353a'
            - '  + 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.1.0.187/24'
            - '* Starting 83707231'
            - '  + 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.1.0.233/24'
        skipped: false

PLAY RECAP *************************************************************************************
iocage_01: ok=9    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
iocage_02: ok=9    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

List jails at iocage_01

[iocage_01]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID |   NAME   | BOOT | STATE | TYPE |     RELEASE     |        IP4         | IP6 |    TEMPLATE    | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 60  | 158ef36d | off  | up    | jail | 13.4-RELEASE-p2 | epair0b|10.1.0.115 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 58  | cd31c2a2 | off  | up    | jail | 13.4-RELEASE-p2 | epair0b|10.1.0.142 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 59  | d254f889 | off  | up    | jail | 13.4-RELEASE-p2 | epair0b|10.1.0.137 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+

List jails at iocage_02

[iocage_02]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID |   NAME   | BOOT | STATE | TYPE |     RELEASE     |        IP4         | IP6 |    TEMPLATE    | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 64  | 08daa493 | off  | up    | jail | 14.1-RELEASE-p6 | epair0b|10.1.0.114 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 65  | 2746353a | off  | up    | jail | 14.1-RELEASE-p6 | epair0b|10.1.0.187 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 66  | 83707231 | off  | up    | jail | 14.1-RELEASE-p6 | epair0b|10.1.0.233 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+

Inventory hosts/01_iocage.yml

plugin: vbotka.freebsd.iocage
host: 10.1.0.18
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', '='))

Inventory hosts/02_iocage.yml

plugin: vbotka.freebsd.iocage
host: 10.1.0.73
user: admin
env:
  CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
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', '='))

Note

The option “get_properties: True” is needed to get the dictionary iocage_properties

Inventory hosts/03_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:
  |--@swarm_sw_01:
  |  |--158ef36d
  |  |--cd31c2a2
  |  |--d254f889
  |  |--08daa493
  |  |--2746353a
  |  |--83707231
  |--@vmm_iocage_01:
  |  |--158ef36d
  |  |--cd31c2a2
  |  |--d254f889
  |--@vmm_iocage_02:
  |  |--08daa493
  |  |--2746353a
  |  |--83707231

Playbook pb-test-01.yml

- name: Connect to the group test.
  hosts: swarm_sw_01
  remote_user: admin
  gather_facts: false

  vars:

    ansible_python_interpreter: auto_silent
    
  tasks:

    - ansible.builtin.command: hostname
      register: out_host
    
    - ansible.builtin.debug:
        msg: "ansible_host={{ ansible_host }} iocage_tags={{ iocage_tags }}"

Playbook output

(env) > ansible-playbook pb-test-01.yml -i hosts

PLAY [Connect to the group test.] *************************************************************

TASK [ansible.builtin.command] ****************************************************************
changed: [158ef36d]
changed: [cd31c2a2]
changed: [d254f889]
changed: [2746353a]
changed: [08daa493]
changed: [83707231]

TASK [ansible.builtin.debug] ******************************************************************
ok: [158ef36d] => 
    msg: 'ansible_host=10.1.0.115 iocage_tags={''vmm'': ''iocage_01'', ''swarm'': ''sw_01''}'
ok: [cd31c2a2] => 
    msg: 'ansible_host=10.1.0.142 iocage_tags={''vmm'': ''iocage_01'', ''swarm'': ''sw_01''}'
ok: [d254f889] => 
    msg: 'ansible_host=10.1.0.137 iocage_tags={''vmm'': ''iocage_01'', ''swarm'': ''sw_01''}'
ok: [08daa493] => 
    msg: 'ansible_host=10.1.0.114 iocage_tags={''vmm'': ''iocage_02'', ''swarm'': ''sw_01''}'
ok: [2746353a] => 
    msg: 'ansible_host=10.1.0.187 iocage_tags={''vmm'': ''iocage_02'', ''swarm'': ''sw_01''}'
ok: [83707231] => 
    msg: 'ansible_host=10.1.0.233 iocage_tags={''vmm'': ''iocage_02'', ''swarm'': ''sw_01''}'

PLAY RECAP ************************************************************************************
08daa493: ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
158ef36d: ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
2746353a: ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
83707231: ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
cd31c2a2: ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
d254f889: 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 pb-iocage-ansible-clients.yml -i iocage-hosts.ini \
                                               -t swarm_destroy \
                                               -e swarm_destroy=true