202 Create iocage templates. Clone DHCP jails.

Extending example 200 Create iocage templates. Clone jails..

Use case

Create iocage templates for Ansible clients. Get the IP addresses by DHCP. Create the dhclient-exit-hooks. For example, the below hook

shell> cat /zroot/iocage/templates/ansible_client/root/etc/dhclient-exit-hooks
case "$reason" in
    "BOUND"|"REBIND"|"REBOOT"|"RENEW")
    echo $new_ip_address > /var/db/dhclient-hook.address.$interface
    ;;
esac

creates files. For example,

shell> cat /zroot/iocage/jails/test_131/root/var/db/dhclient-hook.address.epair0b
10.1.0.130

Read the files created by the hooks and use the IP addresses to compose the variable ansible_host

shell> cat hosts/01_iocage.yml
plugin: vbotka.freebsd.iocage
...
hooks_results:
  - /var/db/dhclient-hook.address.epair0b
compose:
  ansible_host: iocage_hooks.0

In the below declaration, the variable ansible_host defaults to iocage_ip4 if the hook is not available

compose:
  ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)

Tree

shell> tree .
.
├── ansible.cfg
├── files
│   ├── pk_admins.txt
│   └── pkgs.json
├── hosts
│   ├── 02_iocage.yml
│   ├── 04_iocage.yml
│   └── 99_constructed.yml
├── host_vars
│   ├── iocage_02
│   │   └── iocage.yml
│   └── iocage_04
│       └── iocage.yml
├── iocage.ini
└── pb-test.yml

Synopsis

  • At two iocage hosts:

    • iocage_02

    • iocage_04

    In the playbook vbotka.freebsd.pb_iocage_template.yml, use the modules:

    • vbotka.freebsd.iocage to create, start, stop, and convert jails to templates.

    • vbotka.freebsd.iocage exec to create a user and set .ssh ownership.

    • community.general.sysrc to configure /etc/rc.conf

    • ansible.posix.authorized_key to configure public keys.

    • ansible.builtin.lineinfile to configure /usr/local/etc/sudoers

    • configure dhclient hooks

    In the playbook vbotka.freebsd.pb_iocage_ansible_clients.yml, use the module vbotka.freebsd.iocage to:

    • create jails from the Ansible client templates

    • start all jails

    • optionally, display the lists of jails.

  • At all created jails:

    In the playbook pb-test.yml:

    • connect created jails

    • display basic jails’ configuration.

Requirements

Notes

  • The option hooks_results expects the poolname of a jail to be mounted to /poolname. For example, if you activate the pool zroot this plugin expects to find the hooks_results items in the path /zroot/iocage/jails/<name>/root. If you mount the poolname to a different path the easiest remedy is to create a symlink.

ansible.cfg

[defaults]
gathering = explicit
callback_result_format = yaml
display_skipped_hosts = false
host_key_checking = 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_02
iocage_04

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

files

files/pkgs.json
{
    "pkgs": [
        "python311",
        "sudo"
        ]
}

host_vars

host_vars/iocage_02/iocage.yml
freebsd_iocage_pool: zroot
freebsd_iocage_pool_mount: /zroot
freebsd_iocage_mount: /zroot/iocage

templates:
  ansible_client:
    release: 14.3-RELEASE
    properties:
      bpf: 1
      dhcp: 1
      vnet: 1
    dhclient: "{{ act_dhclient | dict2items }}"
    rcconf: "{{ act_rcconf | dict2items }}"
    pkglist: /tmp/ansible/ansible_client/pkgs.json

# ansible client template defaults
act_user: admin
act_pk: pk_admins.txt
act_sudo: true
act_rcconf:
  iocage_enable: "YES"
  sshd_enable: "YES"
act_dhclient:
  dhclient-exit-hooks: |
    case "$reason" in
        "BOUND"|"REBIND"|"REBOOT"|"RENEW")
        echo $new_ip_address > /var/db/dhclient-hook.address.$interface
        ;;
    esac

clones:
  test_111: {clone_from: ansible_client}
  test_112: {clone_from: ansible_client}
  test_113: {clone_from: ansible_client}
start: "{{ clones.keys() }}"
swarms: {}

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

iocage_env:
  CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
host_vars/iocage_04/iocage.yml
freebsd_iocage_pool: iocage
freebsd_iocage_pool_mount: /iocage
freebsd_iocage_mount: /iocage/iocage

templates:
  ansible_client:
    release: 15.0-RELEASE
    properties:
      bpf: 1
      dhcp: 1
      vnet: 1
    dhclient: "{{ act_dhclient | dict2items }}"
    rcconf: "{{ act_rcconf | dict2items }}"
    pkglist: /tmp/ansible/ansible_client/pkgs.json

# ansible client template defaults
act_user: admin
act_pk: pk_admins.txt
act_sudo: true
act_rcconf:
  iocage_enable: "YES"
  sshd_enable: "YES"
act_dhclient:
  dhclient-exit-hooks: |
    case "$reason" in
        "BOUND"|"REBIND"|"REBOOT"|"RENEW")
        echo $new_ip_address > /var/db/dhclient-hook.address.$interface
        ;;
    esac

clones:
  test_131: {clone_from: ansible_client}
  test_132: {clone_from: ansible_client}
  test_133: {clone_from: ansible_client}
start: "{{ clones.keys() }}"
swarms: {}

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

Hint

The minimal required hook is

act_dhclient:
  dhclient-exit-hooks: |
    case "$reason" in
        "BOUND"|"REBIND"|"REBOOT"|"RENEW")
        echo $new_ip_address > /var/db/dhclient-hook.address.$interface
        ;;
    esac

Note

The variables act_* are used to configure ansible_client template

  • The dhclient hooks act_dhclient will be created in /etc.

  • The user act_user will be created in the template.

  • The user act_user will serve as Ansible remote_user.

  • The file act_pk provides the public keys allowed to ssh to act_user in the jail.

Warning

  • The user act_user must exist on the iocage host. Otherwise, the module ansible.posix.authorized_key will crash. See playbooks/pb_iocage_template/pk.yml

  • The file files/pk_admins.txt was sanitized. Fit the public keys to your needs

    shell> cat files/pk_admins.txt
    ssh-rsa <sanitized> admin@controller
    

Playbook output - Create templates

(env) > ansible-playbook vbotka.freebsd.pb_iocage_template.yml -i iocage.ini
PLAY [Create Ansible client templates.] ****************************************

TASK [Setup: Get iocage list of templates.] ************************************
ok: [iocage_04]
ok: [iocage_02]

TASK [Setup: Set dictionary iocage_templates] **********************************
ok: [iocage_04]
ok: [iocage_02]

TASK [Pkglist: Create directories for pkglist files.] **************************
ok: [iocage_04] => (item=ansible_client /tmp/ansible/ansible_client/pkgs.json)
ok: [iocage_02] => (item=ansible_client /tmp/ansible/ansible_client/pkgs.json)

TASK [Pkglist: Copy pkglist files.] ********************************************
ok: [iocage_02] => (item=ansible_client /tmp/ansible/ansible_client/pkgs.json)
ok: [iocage_04] => (item=ansible_client /tmp/ansible/ansible_client/pkgs.json)

TASK [Create: Create templates.] ***********************************************
changed: [iocage_04] => (item=ansible_client 15.0-RELEASE)
changed: [iocage_02] => (item=ansible_client 14.3-RELEASE)

TASK [Start: Start jails.] *****************************************************
ok: [iocage_02]
ok: [iocage_04]

TASK [User: Create user.] ******************************************************
changed: [iocage_04] => (item=ansible_client admin)
changed: [iocage_02] => (item=ansible_client admin)

TASK [Pk: The keys are valid.] *************************************************
ok: [iocage_02] => 
    changed: false
    msg: All assertions passed
ok: [iocage_04] => 
    changed: false
    msg: All assertions passed

TASK [Pk: Configure public keys.] **********************************************
changed: [iocage_04] => (item=ansible_client admin)
changed: [iocage_02] => (item=ansible_client admin)

TASK [Pk: Chown .ssh] **********************************************************
ok: [iocage_04] => (item=ansible_client admin)
ok: [iocage_02] => (item=ansible_client admin)

TASK [Sudo: Configure sudo.] ***************************************************
changed: [iocage_04] => (item=ansible_client admin)
changed: [iocage_02] => (item=ansible_client admin)

TASK [Dhclient: Configure hooks.] **********************************************
changed: [iocage_04] => (item=ansible_client dhclient-exit-hooks)
changed: [iocage_02] => (item=ansible_client dhclient-exit-hooks)

TASK [Rcconf: Configure /etc/rc.conf] ******************************************
changed: [iocage_02] => (item=ansible_client iocage_enable YES)
changed: [iocage_04] => (item=ansible_client iocage_enable YES)
changed: [iocage_04] => (item=ansible_client sshd_enable YES)
changed: [iocage_02] => (item=ansible_client sshd_enable YES)

TASK [Stop: Stop jails.] *******************************************************
ok: [iocage_04]
ok: [iocage_02]

TASK [Template: Set template.] *************************************************
ok: [iocage_04] => (item=ansible_client)
ok: [iocage_02] => (item=ansible_client)

PLAY RECAP *********************************************************************
iocage_02                  : ok=15   changed=6    unreachable=0    failed=0    skipped=27   rescued=0    ignored=0   
iocage_04                  : ok=15   changed=6    unreachable=0    failed=0    skipped=27   rescued=0    ignored=0   

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.3-RELEASE-p8 | DHCP (not running) | -   | -        | no       |
+------+----------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+

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 | 15.0-RELEASE-p3 | DHCP (not running) | -   | -        | no       |
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| None | ansible_client_apache | off  | down  | template | 15.0-RELEASE-p3 | DHCP (not running) | -   | -        | no       |
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+
| None | ansible_client_pull   | off  | down  | template | 15.0-RELEASE-p3 | DHCP (not running) | -   | -        | no       |
+------+-----------------------+------+-------+----------+-----------------+--------------------+-----+----------+----------+

Playbook output - Clone and start jails

(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)
changed: [iocage_04] => (item=test_132 ansible_client)
changed: [iocage_04] => (item=test_133 ansible_client)
changed: [iocage_02] => (item=test_111 ansible_client)
changed: [iocage_02] => (item=test_112 ansible_client)
changed: [iocage_02] => (item=test_113 ansible_client)

TASK [Start clones] ************************************************************
changed: [iocage_04]
changed: [iocage_02]

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

Jails at iocage_02

[iocage_02]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID |   NAME   | BOOT | STATE | TYPE |     RELEASE     |        IP4         | IP6 |    TEMPLATE    | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 79  | test_111 | off  | up    | jail | 14.3-RELEASE-p8 | epair0b|10.1.0.174 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 80  | test_112 | off  | up    | jail | 14.3-RELEASE-p8 | epair0b|10.1.0.147 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 81  | test_113 | off  | up    | jail | 14.3-RELEASE-p8 | epair0b|10.1.0.231 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+

Jails at iocage_04

[iocage_04]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID |   NAME   | BOOT | STATE | TYPE |     RELEASE     |        IP4         | IP6 |    TEMPLATE    | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 58  | test_131 | off  | up    | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.224 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 60  | test_132 | off  | up    | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.185 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 59  | test_133 | off  | up    | jail | 15.0-RELEASE-p3 | epair0b|10.1.0.225 | -   | ansible_client | no       |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+

Inventory hosts

hosts/02_iocage.yml
plugin: vbotka.freebsd.iocage
host: 10.1.0.73
user: admin
env:
  CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
hooks_results:
  - /var/db/dhclient-hook.address.epair0b
compose:
  ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
groups:
  test_02: inventory_hostname.startswith('test')
hosts/04_iocage.yml
plugin: vbotka.freebsd.iocage
host: 10.1.0.29
user: admin
hooks_results:
  - /var/db/dhclient-hook.address.epair0b
compose:
  ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
groups:
  test_01: inventory_hostname.startswith('test')
hosts/99_constructed.yml
plugin: ansible.builtin.constructed
groups:
    test: inventory_hostname.startswith('test')

Display inventory

(env) > ansible-inventory -i hosts --graph
@all:
  |--@ungrouped:
  |--@test_02:
  |  |--test_111
  |  |--test_112
  |  |--test_113
  |--@test_01:
  |  |--test_131
  |  |--test_132
  |  |--test_133
  |--@test:
  |  |--test_111
  |  |--test_112
  |  |--test_113
  |  |--test_131
  |  |--test_132
  |  |--test_133

Playbook pb-test.yml

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

  vars:

    ansible_python_interpreter: auto_silent
    
  tasks:

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

Playbook output - Display list iocage_hooks

(env) > ansible-playbook pb-test.yml -i hosts
PLAY [Connect to the group test.] **********************************************

TASK [ansible.builtin.command] *************************************************
changed: [test_131]
changed: [test_132]
changed: [test_133]
changed: [test_111]
changed: [test_112]
changed: [test_113]

TASK [ansible.builtin.debug] ***************************************************
ok: [test_111] => 
    msg: ansible_host=10.1.0.174 iocage_hooks=['10.1.0.174']
ok: [test_112] => 
    msg: ansible_host=10.1.0.147 iocage_hooks=['10.1.0.147']
ok: [test_113] => 
    msg: ansible_host=10.1.0.231 iocage_hooks=['10.1.0.231']
ok: [test_131] => 
    msg: ansible_host=10.1.0.224 iocage_hooks=['10.1.0.224']
ok: [test_132] => 
    msg: ansible_host=10.1.0.185 iocage_hooks=['10.1.0.185']
ok: [test_133] => 
    msg: ansible_host=10.1.0.225 iocage_hooks=['10.1.0.225']

PLAY RECAP *********************************************************************
test_111                   : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
test_112                   : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
test_113                   : 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   
test_132                   : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
test_133                   : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Hint

The below command stops and destroys the cloned jails

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