Persistent set_fact

Hi

I was thinking about extending set_fact to be able to write the facts given as a local facts file. Something like this:

    set_fact: var=value persistent_file=myvars

To create /etc/ansible/facts.d/myvars.fact as a json with the given vars.

Is this something that might be considered for core? Do you guys have opinions on semantics (separate to a different module maybe)?

Thanks

This is a pretty interesting way to do it and I don’t think I’d have an objection to it.

It would probably need to call the “copy” module in the action_plugin to make it work, using the “content” parameter.

Having it the same module seems to make sense to me.

Pull request would definitely be welcome!

Is there a reason to keep it an action plugin and not a normal module? I’m thinking performance, is there something else?

If you look at set_fact now, it’s completely an action plugin, since it only operates on the host running ansible and not on each individual host (others like this are add_host/group_by, template and fetch modules, which work entirely through action plugins). This saves a lot of effort in having to re-implement common functionality like copying a file out to a host.

To me, it sounds like this new module would be dumping the contents of the vars_cache[hostname] to a file, or perhaps just a subset of those variables?

One gotcha I just realized is local facts show up as “ansible_local.factname” to ensure they never override another fact, kind of as a safety feature.

I would think it would write just the one variable to a facts.d/variable_name file, but it should probably raise an error with persistent=yes if the variable name didn’t start with “ansible_local”, since otherwise it would be set and referenced differently when retrieved the second time.

Like so:

works

set_fact: name=ansible_local.foo value=1234 persistent=yes

raises error: “when using persistent=yes, the variable must be prefixed with ‘ansible_local’”

set_fact: name=foo value=1234 persistent=yes

works fine but doesn’t save

set_fact: name=foo value=1234

And then in the docs we could make a note of it.

One gotcha I just realized is local facts show up as “ansible_local.factname” to ensure they never override another fact, kind of as a safety feature.

I would think it would write just the one variable to a facts.d/variable_name file, but it should probably raise an error with persistent=yes if the variable name didn’t start with “ansible_local”, since otherwise it would be set and referenced differently when retrieved the second time.

Like so:

works

set_fact: name=ansible_local.foo value=1234 persistent=yes

This is different than how set_fact is used now - you pass args as key=value, not as name=key value=value. I think that will also make it harder to create more complex facts:

set_fact: name=ansible_local.foo value={{ {key: value, key2:value2} }} persistent=yes # ugh

BTW do people use local facts for single values? I didn’t think about it until now, and the docs don’t give this usage in the examples.

Another option, which may be a bit magic-y, is to set the facts in the ansible_local namespace if they’re persistent:

set_fact: myfact1=123 myfact2=asd # Will set myfact1=123, myfact2=asd

set_fact: myfact=123 myfact2=asd persistent=hkariti # Will set ansible_local.hkariti={myfact1: 123, myfact2: asd} and save

This doesn’t work for single values though.

One gotcha I just realized is local facts show up as
"ansible_local.factname" to ensure they never override another fact, kind
of as a safety feature.

I would think it would write just the one variable to a
facts.d/variable_name file, but it should probably raise an error with
persistent=yes if the variable name didn't start with "ansible_local",
since otherwise it would be set and referenced differently when retrieved
the second time.

Like so:

# works
set_fact: name=ansible_local.foo value=1234 persistent=yes

This is different than how set_fact is used now - you pass args as
key=value, not as name=key value=value. I think that will also make it
harder to create more complex facts:

Sorry, this was just me tying too quickly, conceptually, I mean the same
thing.

  set_fact: name=ansible_local.foo value={{ {key: value, key2:value2} }}
persistent=yes # ugh

This isn't a thing with setting a hash like this in a one liner, BTW. You
must use the long form.

set_fact:
    key: { key: value, key2: value2 }

And if you want variables, you can template inside, etc.

Another option, which may be a bit magic-y, is to set the facts in the
ansible_local namespace if they're persistent:

  set_fact: myfact1=123 myfact2=asd # Will set myfact1=123, myfact2=asd

I'd like to avoid this, because then the usage of the variable would have
to be accessed by ansible_local.foo, which would be unclear
to readers of the playbook because it was set one way and is accessed
another.