SUMMARY
I can’t find an example in the role argument validation documentation for how to document/validate a dict that contains dicts (nested dict)
For example:
{
“firewalld_zones”: {
“public”: {
“target”: “default”,
“services”: [
“audit”,
“bacula-client”,
“ssh”,
“snmp”,
“ldap”,
“ldaps”,
“nfs”,
“ntp”,
“pop3”,
“pop3s”,
“smtp”,
“smtps”,
“smtp-submission”,
“snmp”,
“snmptrap”
],
“rich_rules”: [
“rule family="ipv4" source address="192.168.1.0/24" port port="22" protocol="tcp" accept”
],
“ports”: [
“80/tcp”
],
“protocols”: [
“tcp”
],
“forward”: false,
“masquerade”: false,
“sources”: [
“192.168.1.0/24”
],
“source_ports”: [
“80/tcp”
],
“interfaces”: [
“ens192”
],
“forward_ports”: [
{
“port”: 443,
“proto”: “tcp”,
“toport”: 8000,
“toaddr”: “10.0.13.0”
}
],
“icmp-block-inversion”: false,
“icmp_blocks”: [
“echo-request”
]
}
}
}
firewalld_zones:
public:
target: default
services:
- audit
- bacula-client
- ssh
- snmp
- ldap
- ldaps
- nfs
- ntp
- pop3
- pop3s
- smtp
- smtps
- smtp-submission
- snmp
- snmptrap
rich_rules:
- rule family=“ipv4” source address=“192.168.1.0/24” port port=“22” protocol=“tcp”
accept
ports:
- 80/tcp
protocols:
- tcp
forward: false
masquerade: false
sources:
- 192.168.1.0/24
source_ports:
- 80/tcp
interfaces:
- ens192
forward_ports:
- port: 443
proto: tcp
toport: 8000
toaddr: 10.0.13.0
icmp-block-inversion: false
icmp_blocks:
- echo-request
…The problem I’m having is that the name for the option is a key so it will be different every time but it has options as well.
firewalld_zones:
type: dict
description: ‘A dictionary containing the zones to be managed.’
options:
WHAT_GOES_HERE: (can’t be ‘public’, ‘drop’, etc since those are free-form dictionary keys.
type: dict
…
First please note that you should format the data as code with three backticks, otherwise it’s pretty much unreadable.
The problem I’m having is that the name for the option is a key so it will be different every time but it has options as well.
There’s no way to validate that with role (and module/plugin) argument spec validation.
The best way is to rearrange the data to not have a list of dictionaries, where name
(or something else) is one key in the element dictionaries. Like
firewalld_zones:
- name: public
target: default
services:
...
There’s no way to validate that with role (and module/plugin) argument spec validation.
Why exactly can’t it be done if all the dictionary keys are listed in the argument spec? I think I have done this for a few roles…
If the dictionary keys can be anything, they can’t be listed in a static argument spec (for roles or modules). It’s the same limitation described in New role argument spec doesn't support arbitrary keys · Issue #74001 · ansible/ansible · GitHub, the feature has never existed.
Another option could be validating it’s a dictionary in the argument spec:
# meta/argument_specs.yml
argument_specs:
main:
firewalld_zones:
type: dict
default: {} # documentation only, matches the value in defaults/main.yml
and use a dynamic argument spec with a validate_argument_spec task in the role entrypoint:
# tasks/main.yml
- name: Validate firewalld_zones
validate_argument_spec:
argument_spec: "{{ dynamic_spec }}"
vars:
dynamic_spec:
firewalld_zones:
type: dict
options: "{{ dict(firewalld_zones | zip([filewalld_zone_value_spec] * (firewalld_zones | length))) }}"
filewalld_zone_value_spec:
type: dict
options:
forward:
type: bool
forward_ports:
type: list
elements: dict
options:
port:
type: int
proto:
type: str
choices:
- tcp
toport:
type: int
toaddr:
type: str
# etc...
First please note that you should format the data as code with three backticks, otherwise it’s pretty much unreadable.
I’m aware. I went to all that effort in the Github issue and a lot of good it did me so I was a bit annoyed when I posted it. Not to take it out on you guys
Good suggestions, thank you! I will look at them all and see what I can do. I thought about literally adding every possible zone, at least the ones that are OOTB but didn’t like that idea. I also thought of just saying it’s a dictionary and leaving it at that but didn’t like that either. I also considered changing the data structure but was hoping to not have to do that because it means I have to go back over the role and change that where it applies. Could be a lot of work. I was not aware of the dynamic argument spec option, I’ll to read up on that.
FWIW I have a role that has a dictionary for Apache VirtualHosts and a argument spec for this dictionary and the dictionaries are validated using this task.