202 Create Ansible client templates and clone DHCP Ansible client jails¶
Extending example 200 Create Ansible client templates and clone Ansible client jails.
Use case¶
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_101/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
Default 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
├── 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.yml -> ../../../../playbooks/pb-iocage-ansible-clients.yml
├── pb-iocage-template -> ../../../../playbooks/pb-iocage-template
├── pb-iocage-template.yml -> ../../../../playbooks/pb-iocage-template.yml
└── pb-test-01.yml
Synopsis¶
On two iocage hosts:
iocage_01
iocage_02
In the playbook 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.pkgng to install packages.
ansible.posix.authorized_key to configure public keys.
ansible.builtin.lineinfile to configure /etc/rc.conf and /usr/local/etc/sudoers
configure dhclient hooks.
In the playbook 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.
On all created jails:
In the playbook pb-test-01.yml:
connect created jails
display basic configuration of the jails.
Requirements¶
root privilege on the iocage hosts
activated iocage
fetched releases
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.
See also
Configuration ansible.cfg¶
[defaults]
gathering = explicit
callback_result_format = yaml
[connection]
pipelining = true
host_vars/iocage_01/iocage.yml¶
ansible_python_interpreter: /usr/local/bin/python3.9
ansible_become_password: admin
freebsd_iocage_pool: pool2
freebsd_iocage_pool_mount: /mnt/pool2
freebsd_iocage_mount: "{{ freebsd_iocage_pool_mount }}/iocage"
templates:
ansible_client:
release: 13.4-RELEASE
properties:
ip4_addr: 'vnet0|10.1.0.198/24'
dhclient: "{{ act_dhclient | dict2items }}"
rcconf: "{{ act_rcconf | dict2items }}"
# ansible client template defaults
act_pkg:
- security/sudo
- lang/python39
act_user: admin
act_pk: pk_admins.txt
act_sudo: true
act_rcconf:
iocage_enable: '"YES"'
sshd_enable: '"YES"'
act_dhclient:
dhclient-enter-hooks: |
echo "dhclient-enter-hooks: $interface $reason new_ip_address=$new_ip_address" >> /var/log/dhclient-hook.log
# logger "dhclient-enter-hooks: $interface $reason new_ip_address=$new_ip_address"
dhclient-exit-hooks: |
echo "dhclient-exit-hooks: $interface $reason new_ip_address=$new_ip_address" >> /var/log/dhclient-hook.log
# logger "dhclient-exit-hooks: $interface $reason new_ip_address=$new_ip_address"
case "$reason" in
"BOUND"|"REBIND"|"REBOOT"|"RENEW")
echo $new_ip_address > /var/db/dhclient-hook.address.$interface
;;
esac
clones:
test_101: {clone_from: ansible_client}
test_102: {clone_from: ansible_client}
test_103: {clone_from: ansible_client}
start: "{{ clones.keys() }}"
swarms: {}
# clones default properties
properties:
bpf: 1
dhcp: 1
vnet: 1
notes: "vmm={{ inventory_hostname }}"
Hint
The option hooks_results expects the pool to be mounted to /pool. In the above host_vars the pool pool2 is mounted to /mnt/pool2. For hooks_results to work properly, create symlink in the root directory
pool2 -> /mnt/pool2
host_vars/iocage_02/iocage.yml¶
ansible_python_interpreter: /usr/local/bin/python3.11
freebsd_iocage_runner_env:
CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
freebsd_iocage_pool: zroot
freebsd_iocage_pool_mount: /zroot
freebsd_iocage_mount: "{{ freebsd_iocage_pool_mount }}/iocage"
templates:
ansible_client:
release: 14.1-RELEASE
properties:
ip4_addr: 'em0|10.1.0.199/24'
dhclient: "{{ act_dhclient | dict2items }}"
rcconf: "{{ act_rcconf | dict2items }}"
# ansible client template defaults
act_pkg:
- security/sudo
- lang/python311
act_user: admin
act_pk: pk_admins.txt
act_sudo: true
act_rcconf:
iocage_enable: '"YES"'
sshd_enable: '"YES"'
act_dhclient:
dhclient-enter-hooks: |
echo "dhclient-enter-hooks: $interface $reason new_ip_address=$new_ip_address" >> /var/log/dhclient-hook.log
# logger "dhclient-enter-hooks: $interface $reason new_ip_address=$new_ip_address"
dhclient-exit-hooks: |
echo "dhclient-exit-hooks: $interface $reason new_ip_address=$new_ip_address" >> /var/log/dhclient-hook.log
# logger "dhclient-exit-hooks: $interface $reason new_ip_address=$new_ip_address"
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 }}"
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.
Warning
The user act_user must exist on the iocage host. Otherwise, the module ansible.posix.authorized_key will crash. See 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
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-template.yml¶
- name: Create Ansible client templates.
hosts: iocage
environment: "{{ freebsd_iocage_runner_env | d({}) }}"
vars:
debug: false
debug2: false
tasks:
- name: Setup
tags: [always, setup]
ansible.builtin.import_tasks: pb-iocage-template/setup.yml
- name: Create jails
tags: create
ansible.builtin.import_tasks: pb-iocage-template/create.yml
- name: Start jails
tags: start
ansible.builtin.import_tasks: pb-iocage-template/start.yml
- name: Install packages
when: act_pkg | length > 0
tags: pkg
ansible.builtin.import_tasks: pb-iocage-template/pkg.yml
- name: Create user
when: act_user | length > 0
tags: user
ansible.builtin.import_tasks: pb-iocage-template/user.yml
- name: Configure public keys
when: act_pk | length > 0
tags: pk
ansible.builtin.import_tasks: pb-iocage-template/pk.yml
- name: Configure sudo
when: act_sudo | bool
tags: sudo
ansible.builtin.import_tasks: pb-iocage-template/sudo.yml
- name: Configure dhclient
when: act_dhclient | length > 0
tags: dhclient
ansible.builtin.import_tasks: pb-iocage-template/dhclient.yml
- name: Configure /etc/rc.conf
when: act_rcconf | length > 0
tags: rcconf
ansible.builtin.import_tasks: pb-iocage-template/rc_conf.yml
- name: Stop jails
tags: stop
ansible.builtin.import_tasks: pb-iocage-template/stop.yml
- name: Convert jails to templates
tags: template
ansible.builtin.import_tasks: pb-iocage-template/template.yml
Playbook output¶
(env) > ansible-playbook pb-iocage-template.yml -i iocage-hosts.ini -e debug=true
...
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 |
+------+----------------+------+-------+----------+-----------------+-------------------+-----+----------+----------+
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 clone \
-e clone=true
PLAY [Create and start jails. Optionally stop and destroy jails.] ******************************
TASK [Create clones from template] *************************************************************
changed: [iocage_01] => (item=test_101 ansible_client)
changed: [iocage_01] => (item=test_102 ansible_client)
changed: [iocage_02] => (item=test_111 ansible_client)
changed: [iocage_01] => (item=test_103 ansible_client)
changed: [iocage_02] => (item=test_112 ansible_client)
changed: [iocage_02] => (item=test_113 ansible_client)
TASK [Debug clones from template debug2=False] *************************************************
skipping: [iocage_01]
skipping: [iocage_02]
TASK [Start clones] ****************************************************************************
changed: [iocage_01] => (item=test_101)
changed: [iocage_01] => (item=test_102)
changed: [iocage_02] => (item=test_111)
changed: [iocage_01] => (item=test_103)
changed: [iocage_02] => (item=test_112)
changed: [iocage_02] => (item=test_113)
TASK [Debug start clones debug2=False] *********************************************************
skipping: [iocage_01]
skipping: [iocage_02]
PLAY RECAP *************************************************************************************
iocage_01: ok=2 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
iocage_02: ok=2 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 |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 38 | test_101 | off | up | jail | 13.4-RELEASE-p2 | epair0b|10.1.0.130 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 39 | test_102 | off | up | jail | 13.4-RELEASE-p2 | epair0b|10.1.0.245 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 40 | test_103 | off | up | jail | 13.4-RELEASE-p2 | epair0b|10.1.0.180 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
List jails at iocage_02¶
[iocage_02]# iocage list -l
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
| 44 | test_111 | off | up | jail | 14.1-RELEASE-p6 | epair0b|10.1.0.174 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 45 | test_112 | off | up | jail | 14.1-RELEASE-p6 | epair0b|10.1.0.147 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
| 46 | test_113 | off | up | jail | 14.1-RELEASE-p6 | epair0b|10.1.0.231 | - | ansible_client | no |
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
Inventory hosts/01_iocage.yml¶
plugin: vbotka.freebsd.iocage
host: 10.1.0.18
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')
Inventory 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')
Inventory hosts/03_constructed.yml¶
plugin: ansible.builtin.constructed
groups:
test: inventory_hostname.startswith('test')
Display inventory¶
(env) > ansible-inventory -i hosts --graph
@all:
|--@ungrouped:
|--@test_01:
| |--test_101
| |--test_102
| |--test_103
|--@test_02:
| |--test_111
| |--test_112
| |--test_113
|--@test:
| |--test_101
| |--test_102
| |--test_103
| |--test_111
| |--test_112
| |--test_113
Playbook pb-test-01.yml¶
- name: Connect to the group test.
hosts: test
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_hooks={{ iocage_hooks }}"
Playbook output¶
(env) > ansible-playbook pb-test-01.yml -i hosts
PLAY [Connect to the group test.] *************************************************************
TASK [ansible.builtin.command] ****************************************************************
changed: [test_102]
changed: [test_101]
changed: [test_103]
changed: [test_112]
changed: [test_111]
changed: [test_113]
TASK [ansible.builtin.debug] ******************************************************************
ok: [test_101] =>
msg: ansible_host=10.1.0.130 iocage_hooks=['10.1.0.130']
ok: [test_102] =>
msg: ansible_host=10.1.0.245 iocage_hooks=['10.1.0.245']
ok: [test_103] =>
msg: ansible_host=10.1.0.180 iocage_hooks=['10.1.0.180']
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']
PLAY RECAP ************************************************************************************
test_101: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_102: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_103: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
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
Hint
The below command stops and destroys the cloned jails
ansible-playbook pb-iocage-ansible-clients.yml -i iocage-hosts.ini \
-t clone_destroy \
-e clone_destroy=true