[SOLVED] Accessing the UUID of a config item in (Jinja2) templates

Started by ndejong, August 22, 2018, 02:52:03 AM

Previous topic - Next topic
I'm having a challenge in figuring out out to obtain the UUID of an item when rendering templates - by way of example I have this configuration structure:-

<VerbNetworks>
  <autossh version="0.1.0">
    <tunnels>
      <tunnel uuid="490829b9-f1b0-42ad-8dad-182de0c06afb">
        <user>myuser</name>
        <host>panic.verbnetworks.com</type>
      </tunnel>
    </tunnels>
  </autossh>
</VerbNetworks>


I note the template helper functions `getUUID()` and `getUUIDtag()` in `template_helpers.py` that take a UUID as an input parameter and return data associated with that UUID - this is not what I want despite the `getUUID()` function having a correct sounding name.

What (I think) I want to write is something like the following:-

{% for tunnel_uuid, tunnel_item in 'VerbNetworks.autossh.tunnels.tunnel'.iteritems() %}
[autossh_{{ tunnel_uuid }}]
{% endfor %}

It is not clear to me how to write or express the tag-name that gets passed to `.iteritems()` and thus the above code fails.

In a maybe-perfect world it might be nice if you could obtain a list of UUIDs for a tag-name using a helper function that I imagine might be called "getUUIDs()"

{% for tunnel_uuid in helpers.getUUIDs('VerbNetworks.autossh.tunnels.tunnel') %}
{% set tunnel = helpers.getUUID(tunnel_uuid) %}
Host {{ tunnel_uuid }}
  User {{ tunnel.user }}
  HostName {{ tunnel.host }}
{% endfor %}


Before I set about trying to write and introduce such a new helper function is there another way of achieving what I'm setting out to do here?


Hi Nicholas,

I think you just want to iterate the items in the tunnel section, normally you should be able to do something similar to this:


{%  if helpers.exists('VerbNetworks.autossh.tunnels.tunnel') %}
{%    for tunnel in helpers.toList('VerbNetworks.autossh.tunnels.tunnel') %}
{{tunnel.user}} {{tunnel.host}}
{%    endfor %}
{%  endif %}



The core repository contains quite some examples of constructions like these, for example
https://github.com/opnsense/core/blob/master/src/opnsense/service/templates/OPNsense/Filter/filter_tables.conf

If you need the uuid of the item itself, I'll have to check if/how you can reach that. We normally write the unique identification for the record in a model field as well if we need it.

Best regards,

Ad

@ad the code I've linked does the reverse. If you iterate over a list of elements without a previous reference, you have no designed to use way to get the uuid of the current node. In my case I use the uuid to reference them.

ahh - I just took this up as an issue in between this being posted - you guys are awesome.

https://github.com/opnsense/core/issues/2664

N

.... actually, there is a subtle but important difference, that is being able to access the UUID - the `toList()` approach does not allow you to access the item UUID

If I understand things correctly, the Nginx workaround approach looks dangerous for the same reason as I describe in the Issue, that is, you can very easily have a name collision among plugins.

As in the Nginx example, it would be pretty easy for another plugin to use a tag-name "server"

It is not dangerous - it compares the object with each other which means that it compares the internal references of the interpreter. This means that it easily breaks if I would get a copy of the original object (understand it as something similar like comparing C pointers).  server is my current element in the loop - not an element name. It is just a pice of code that should never be in a template but there is currently no alternative.

Ahh, thanks for helping me see though this - I missed where the that `server` variable was coming from - putting a PR together now for this then:-


    def getUUIDs(self, tag):
        """ retrieve a list of uuids from the item list returned by tag
        :param tag: tag in dot notation (section.item)
        :return: []
        """
        uuids = []
        for item in self.toList(tag):
            for uuid in self._template_in_data['__uuid__']:
                if self._template_in_data['__uuid__'][uuid] == item:
                    uuids.append(uuid)
        return uuids


Pull request here:-
https://github.com/opnsense/core/pull/2665

In other news I need help de-tangling my commits, this PR includes previous commits - gack - will figure it out tomorrow I guess

you should change it anyway - UUIDs are unique. If you have found it, you can return.

there is a return uuids statement missing at the end of my cut-n-paste - chat on the PR about it

Resolution on this has been that @AdSchellevis added the UUID to items via a `@uuid` parameter, thus enabling the ability to write as such:

{{item['@uuid']}}


Reference to the discussion on Github:-
https://github.com/opnsense/core/issues/2664#issuecomment-415675706

Thanks @AdSchellevis !!