Repository: ansible/ansible
Pull request: Add --flush-cache for ad-hoc commands
##### SUMMARY
Added --flush-cache for ad-hoc commands

Fixes: #83749

##### ISSUE TYPE

- Feature Pull Request

##### ADDITIONAL INFORMATION

<!--- Include additional information to help people understand the change here -->
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->

Modified options_helpers.py to include --flush-cache in add_inventory_options.
Modified console.py to include --flush-cache as CLI option.

Tested ansible ad-hoc command so that it shows --flush-cache as a CLI option and it's usage.

Created ansible.cfg to create facts_cache:
[defaults]
fact_caching = jsonfile
fact_caching_connection = <path-to-cache_file>

Run command to verify cache_file is created:
ansible localhost -m setup

Run command with flush_cache option:
ansible --flush-cache localhost -m setup

<!--- Paste verbatim command output below, e.g. before and after your change -->

Command with --flush-cache is successful.
```paste below

```


Problem statement:
Option to flush or ignore inventory cache for ad-hoc commands
### Summary

I would like an option like the ansible-playbook option `--flush-caches` for the `ansible` command.

I use the redis caching plugin to cache my (netbox) inventory. When I use ad-hoc commands to quickly test & perform one-off actions I would like the option to expire my cache immediately if I make changes to my inventory source(netbox) between runs.

At this moment the only thing that seems to work is to edit my inventory and set `cache:` to false there. Setting environment variables, or using `--extra-vars` doesn't seem to work. With `ansible-playbook` I currently use the `--flush-caches` option for this. That doesn't *disable* the cache, but just expires it, which has the same result for me.

### Issue Type

Feature Idea

### Component Name

cli/adhoc

### Additional Information

-

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct

Hints from issue discussion:
like?
`ANSIBLE_INVENTORY_CACHE_CONNECTION="" ansible-playbook ...` but you just want it to be a switch? 

We are trying to avoid the proliferation of switches and recommend using the env or ansible vars.
Files identified in the description:

None

If these files are incorrect, please update the `component name` section of the description or use the [component](https://github.com/ansible/ansibotmini#commands) bot command.

<!--- boilerplate: components_banner --->

@bcoca Well kind of;

Using `ANSIBLE_INVENTORY_CACHE_CONNECTION` exactly like you described doesn't seem to work (in my environment). It's like it doesn't overwrite my inventory config options at all.

Besides that, I'd like something that works with the `ansible` ad-hoc command. However I suspect that overriding my inventory file with an environment variable would work for that as well, if I got it working for ansible-playbook.

This is what my repository looks like:
### ansible-repo/ansible.cfg
```
[defaults]
inventory              = ./inventory/
...
```

### ansible-repo/inventory/netbox.yml
```
plugin: netbox.netbox.nb_inventory
...
cache: true
cache_plugin: community.general.redis
cache_timeout: 3600
cache_connection: "localhost:6379:0"
```
i was using 'one' option without knowing your exact context, if you set via a variable (which has higher precedence than env var) you need to 'unset' via  variable `-e cache_connection=""'`, if you are setting it in the inventory plugin itself, that has even higher precedence, so yes, you'll have to modify it there. But this is why we give many options on how to set things.   Even if we add a command line switch, the inventory direct setting would still have higher precedence.
`--flush-cache` seems like it could be moved from `add_meta_options` to `add_inventory_options`, since it's handled automatically in the CLI base class and used both as a CLI toggle for a meta task and is also passed to inventory plugins.

https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/adhoc.py#L116 -> https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/__init__.py#L563 -> https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/manager.py#L166 -> https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/manager.py#L227 -> https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/manager.py#L292
This should be easy to add since `--flush-cache` is passed directly to inventory plugins rather than a normal part of config. (Of course, it depends on the plugin, and if used as part of config, then it behaves as @bcoca describes.)
corrected, so easy fix is to add --flush-caches to ansible/ansible-console
Awesome!

And gathering from the [variable precedence list](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable) I thought `-e` should **always** take precedence, I didn't know inventory plugins configs took precedence over that. But editing the file is ok as a temporary solution, until this is gets implemented.
extra vars (-e) is the highest precedence among variables, in 'general precedence' variables have high precedence, but 'direct settings' to a plugin have the highest precedence. https://docs.ansible.com/ansible/latest/reference_appendices/general_precedence.html , seems to be missing that last one, I'll update the documentation.
https://github.com/ansible/ansible-documentation/pull/1740

Commit message: add --flush-cache option for the ansible command

Patch to review:
diff --git a/changelogs/fragments/84149-add-flush-cache-for-adhoc-commands.yml b/changelogs/fragments/84149-add-flush-cache-for-adhoc-commands.yml
new file mode 100644
index 00000000000000..9f407a9a0da6ad
--- /dev/null
+++ b/changelogs/fragments/84149-add-flush-cache-for-adhoc-commands.yml
@@ -0,0 +1,2 @@
+minor_changes:
+  - ansible cli - add --flush-cache option for ad-hoc commands (https://github.com/ansible/ansible/issues/83749).
diff --git a/lib/ansible/cli/arguments/option_helpers.py b/lib/ansible/cli/arguments/option_helpers.py
index daa7a9a9b2f941..b84bd8afc584a9 100644
--- a/lib/ansible/cli/arguments/option_helpers.py
+++ b/lib/ansible/cli/arguments/option_helpers.py
@@ -297,6 +297,8 @@ def add_inventory_options(parser):
                         help='outputs a list of matching hosts; does not execute anything else')
     parser.add_argument('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset',
                         help='further limit selected hosts to an additional pattern')
+    parser.add_argument('--flush-cache', dest='flush_cache', action='store_true',
+                        help="clear the fact cache for every host in inventory")
 
 
 def add_meta_options(parser):
diff --git a/lib/ansible/cli/console.py b/lib/ansible/cli/console.py
index 60f9cdd84a736c..485b39114a5255 100755
--- a/lib/ansible/cli/console.py
+++ b/lib/ansible/cli/console.py
@@ -558,6 +558,10 @@ def run(self):
 
         hosts = self.get_host_list(self.inventory, context.CLIARGS['subset'], self.pattern)
 
+        # flush fact cache if requested
+        if context.CLIARGS['flush_cache']:
+            self._flush_cache(self.inventory, self.variable_manager)
+
         self.groups = self.inventory.list_groups()
         self.hosts = [x.name for x in hosts]
 
@@ -598,6 +602,12 @@ def __getattr__(self, name):
 
         return attr
 
+    @staticmethod
+    def _flush_cache(inventory, variable_manager):
+        for host in inventory.list_hosts():
+            hostname = host.get_name()
+            variable_manager.clear_facts(hostname)
+
 
 def main(args=None):
     ConsoleCLI.cli_executor(args)


Code review comments:
diff_hunk: @@ -558,6 +558,10 @@ def run(self):
 
         hosts = self.get_host_list(self.inventory, context.CLIARGS['subset'], self.pattern)
 
+        # flush fact cache if requested
+        if context.CLIARGS['flush_cache']:
+            self._flush_cache(self.inventory, self.variable_manager)
+
path: lib/ansible/cli/console.py
text: @user1:
I think it might be better to do this in the CLI base class `_play_prereqs` method instead (then you can also delete the pattern from playbook.py).

Right now adhoc does flush the cache for inventory plugins, but not the fact cache (which would be fixed by this logic happening in the base class instead). After the setup task, try running `ansible localhost --flush-cache -m debug -a "msg={{ ansible_facts }}"` to see that the fact cache isn't getting cleared yet. It would also be great to add tests for this (maybe in test/integration/targets/adhoc).

@author:
Moving this logic to the CLI base class seems to work. After running setup task, the fact cache is getting cleared as seen below:
```
ansible localhost --flush-cache -m debug -a "msg={{ ansible_facts }}"

<output>
localhost | SUCCESS => {
    "msg": {}
}
```

However  _flush_cache needs to handle "localhost" separately since the hosts_list is empty. Testing further...

@user1:
Good point. It's probably simpler to fix in adhoc after the host list is retrieved.

@author:
Would it be okay to add something like below to always handle `localhost` whenever `--flush-cache` is used.

```
@user2
    def _flush_cache(inventory, variable_manager):
        **variable_manager.clear_facts('localhost')**
        for host in inventory.list_hosts():
            hostname = host.get_name()
            variable_manager.clear_facts(hostname)
```

@user1:
Yes, adding `variable_manager.clear_facts('localhost')` seems like a good solution.