Repository: ansible-collections/community.general

Issue:
module redis can't be used to set the parameter save 
##### SUMMARY
redis command=config can't be used to change the save config parameter. seems to be a problem with handling of a string with spaces in it. quoting the value does returns the same error with a quoted string

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
redis

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```
ansible 2.10.2
  config file = None
  configured module search path = ['/home/XXXXX/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/XXXXX/ansible/lib/python3.7/site-packages/ansible
  executable location = /home/XXXXX/bin/ansible
  python version = 3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
ANSIBLE_NOCOLOR(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = False
ANSIBLE_NOCOWS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
ANSIBLE_PIPELINING(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
ANSIBLE_SSH_ARGS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = -o PasswordAuthentication=no -o ControlMaster=auto -o ControlP
COLLECTIONS_PATHS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/collections']
COMMAND_WARNINGS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
DEFAULT_ACTION_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/action
DEFAULT_BECOME_USER(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = root
DEFAULT_CACHE_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/cache']
DEFAULT_CALLBACK_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/call
DEFAULT_CONNECTION_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/co
DEFAULT_FILTER_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/filter
DEFAULT_FORCE_HANDLERS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
DEFAULT_FORKS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = 5
DEFAULT_GATHERING(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = smart
DEFAULT_GATHER_SUBSET(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['all', '!ohai']
DEFAULT_HOST_LIST(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/ansible/vagrant/inventory']
DEFAULT_LOG_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = /home/XXXXXX/git/devops/ansible/vagrant/ansible.log
DEFAULT_LOOKUP_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/lookup
DEFAULT_MANAGED_STR(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = Ansible managed file {file} modified on %Y-%m-%d %H:%M:%S b
DEFAULT_MODULE_NAME(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = command
DEFAULT_MODULE_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/modules']
DEFAULT_POLL_INTERVAL(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = 15
DEFAULT_ROLES_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/roles']
DEFAULT_SCP_IF_SSH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
DEFAULT_STDOUT_CALLBACK(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = yaml
DEFAULT_STRATEGY_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/stra
DEFAULT_TEST_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/test']
DEFAULT_TIMEOUT(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = 10
DEFAULT_VARS_PLUGIN_PATH(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ['/home/XXXXXX/git/devops/lib/ansible/plugins/vars']
DEPRECATION_WARNINGS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
DISPLAY_SKIPPED_HOSTS(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = True
DUPLICATE_YAML_DICT_KEY(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = ignore
HOST_KEY_CHECKING(/home/XXXXXX/git/devops/ansible/vagrant/ansible.cfg) = False

```

##### OS / ENVIRONMENT
Host and target machine debian buster

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```sh
ansible -m community.general.redis -b -a"command=config name=save value='900 1 300 10 60 10000'" target
```

```yaml
- name: Ensure Redis save runtime settings are correct
  redis:
    command: config
    name: save
    value: "900 1 300 10 60 10000"
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
that the config value is changed

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
returns an error.
```
"unable to write config: Invalid argument '900' for CONFIG SET 'save'"
```
<!--- Paste verbatim command output between quotes -->
```paste below
<aio.test> (1, b'\n{"exception": "Traceback (most recent call last):\\n  File \\"/tmp/ansible_community.general.redis_payload_jub2ula8/ansible_community.general.redis_payload.zip/ansible_collections/community/general/plugins/modules/redis.py\\", line 301, in main\\n  File \\"/usr/lib/python3/dist-packages/redis/client.py\\", line 906, in config_set\\n    return self.execute_command(\'CONFIG SET\', name, value)\\n  File \\"/usr/lib/python3/dist-packages/redis/client.py\\", line 775, in execute_command\\n    return self.parse_response(connection, command_name, **options)\\n  File \\"/usr/lib/python3/dist-packages/redis/client.py\\", line 789, in parse_response\\n    response = connection.read_response()\\n  File \\"/usr/lib/python3/dist-packages/redis/connection.py\\", line 642, in read_response\\n    raise response\\nredis.exceptions.ResponseError: Invalid argument \'900\' for CONFIG SET \'save\'\\n", "failed": true, "msg": "unable to write config: Invalid argument \'900\' for CONFIG SET \'save\'", "invocation": {"module_args": {"command": "config", "name": "save", "value": "900 1 300 10 60 10000", "login_host": "localhost", "login_port": 6379, "slave_mode": "slave", "flush_mode": "all", "login_password": null, "master_host": null, "master_port": null, "db": null}}}\n', b'')
<aio.test> Failed to connect to the host via ssh: 
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_community.general.redis_payload_jub2ula8/ansible_community.general.redis_payload.zip/ansible_collections/community/general/plugins/modules/redis.py", line 301, in main
  File "/usr/lib/python3/dist-packages/redis/client.py", line 906, in config_set
    return self.execute_command('CONFIG SET', name, value)
  File "/usr/lib/python3/dist-packages/redis/client.py", line 775, in execute_command
    return self.parse_response(connection, command_name, **options)
  File "/usr/lib/python3/dist-packages/redis/client.py", line 789, in parse_response
    response = connection.read_response()
  File "/usr/lib/python3/dist-packages/redis/connection.py", line 642, in read_response
    raise response
redis.exceptions.ResponseError: Invalid argument '900' for CONFIG SET 'save'
aio | FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "command": "config",
            "db": null,
            "flush_mode": "all",
            "login_host": "localhost",
            "login_password": null,
            "login_port": 6379,
            "master_host": null,
            "master_port": null,
            "name": "save",
            "slave_mode": "slave",
            "value": "900 1 300 10 60 10000"
        }
    },
    "msg": "unable to write config: Invalid argument '900' for CONFIG SET 'save'"
}
```



Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
I think you mean the `redis` module, not the (non-existing) `redis_config` module?
> I think you mean the `redis` module, not the (non-existing) `redis_config` module?

yes you are right
for additional information with a clean ansible 2.9 install:
- using `community.general.redis` fails the same way
- using `redis` from ansible self works like expected

reverting the following difference between the 2 redis.py versions fixes the issue
```
@@ -272,7 +275,11 @@
                 module.fail_json(msg="Unable to flush '%d' database" % db)
     elif command == 'config':
         name = module.params['name']
-        value = module.params['value']
+
+        try:  # try to parse the value as if it were the memory size
+            value = str(human_to_bytes(module.params['value'].upper()))
+        except ValueError:
+            value = module.params['value']
 
         r = redis.StrictRedis(host=login_host, port=login_port, password=login_password)
 
```




Resolving patch:
diff --git a/plugins/modules/database/misc/redis.py b/plugins/modules/database/misc/redis.py
--- a/plugins/modules/database/misc/redis.py
+++ b/plugins/modules/database/misc/redis.py
@@ -131,6 +131,7 @@
 from ansible.module_utils.basic import AnsibleModule, missing_required_lib
 from ansible.module_utils.common.text.formatters import human_to_bytes
 from ansible.module_utils._text import to_native
+import re
 
 
 # Redis module specific support methods.
@@ -277,7 +278,10 @@ def main():
         name = module.params['name']
 
         try:  # try to parse the value as if it were the memory size
-            value = str(human_to_bytes(module.params['value'].upper()))
+            if re.match(r'^\s*(\d*\.?\d*)\s*([A-Za-z]+)?\s*$', module.params['value'].upper()):
+                value = str(human_to_bytes(module.params['value'].upper()))
+            else:
+                value = module.params['value']
         except ValueError:
             value = module.params['value']
 


Test patch: