dict merge - with_dict expects a dict

Hi,

So here’s the scenario - I’m given a data structure that when converted to yaml looks like this (names & numbers invented).

`
host_routes:
route-oob:
device: bond.123
routes:
10.118.0.0/16: ‘10.118.255.254’
route-int:
device: bond.456
routes:
172.168.0.0/23: ‘172.168.1.254’

`

I can use that with a template to create the network-script route files needed, that’s easy enough. But in real life there can be up to several OOB routes that this host will need depending on which datacentre / hall / security zone it resides in. e.g.

standard_routes: route-oob: device: bond.123 routes: 10.198.0.0/24: '10.118.255.254'

I’d like to add these additional routes to the appropriate group_var file and then merge them together when creating the route files. I can’t get this to work though. I’ve tried all the following:

`
with_dict: host_routes|union(standard_routes)

with_dict: union(host_routes, standard_routes)

with_dict: host_routes + standard_routes

`

and get the same error every time …

fatal: [localhost] => with_dict expects a dict

Is there another filter or some other way of doing this? I don’t want to change the default hash_behaviour to merge just for this one use case as that has much wider implications.

Thanks, John

So the reason why I tried using union was this github issue ( https://github.com/ansible/ansible/issues/7495 ) which implied it should work (assuming the PR got actioned).

But looking at the code ( lib/ansible/runner/filter_plugins/mathstuff.py ) it doesn’t look like that’s the case as union() doesn’t match what this article (http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-in-a-single-expression) describes as the ‘pythonic way’.

z = x.copy()
z.update(y)

So I tried to emulate that with …

with_dict: "{% set r = host_routes.copy() %}{% r.update(standard_routes) %}{{ r }}"

resulting in …

`
fatal: [localhost] => template error while templating string: unknown tag ‘r’

`

I’ve also scanned all the Jinja2 filters ( http://jinja.pocoo.org/docs/dev/templates ) looking for a potential candidate and can’t see anything that looks suitable.

I really am an Ansible & jinja2 pseudo-novice, so I’m kind of stumbling around a bit here :slight_smile:

Would you mind sharing the full body of the task that pertains to this as well as the template file?

so I’ve got a working sample for you. This is the exact test I did.

`

Hi,

For just processing host_routes I had.

`

  • name: Write network routes
    template: >
    src=route.j2
    dest={{ net_path}}/route-{{ item.value.device }}
    mode=0644 owner=root group=root
    with_dict: host_routes
    when: item.value.routes is defined
    tags: netdev

`

And the template was …

`
{% for network,gateway in item.value.routes.iteritems() %}
{{ network }} via {{ gateway }} dev {{ item.value.device }}
{% endfor %}

`

I needed the ‘when’ conditional because the dict sometimes contains something like this instead to ensure that old/retired/changed network-script route files must be removed:

routes-ext: device: 'eth6.111' absent: true routes-other: device: 'eth6.222' absent: true

And I cater for that with …

`

  • name: Remove ‘absent’ route files
    file: path={{ net_path}}/route-{{ item.value.device }} state=absent
    with_dict: host_routes
    when: item.value.absent is defined
    tags: netdev

`

Does my example above help you as far as the templating piece?

Personally I would abstract out the desired state for the route-file status into it’s own dict to smth like this:

`
route_file_state:
routes-other: present
routes-ext: absent

`

would prevent that extra attribute from interfering with my jinja example and needing to add more logic.

Hi again. Thanks … I got so hung up on using with_dict I didn’t consider taking another tack.

I’m reconsidering the decision to not to use hash_behaviour=merge as that solves my problem + would also come in handy elsewhere, like combining default sysctl parameter changes with additional application & environment requirements. If I don’t then it would be an interesting exercise for me to write a custom module to merge dictionaries, or even add a new ansible filter.

Cheers, J

Yes, I can work with that.

Thanks for your help.