Repository: netbox-community/netbox

Issue:
Add Redis username parameter
### NetBox version

v.3.4.0

### Feature type

Change to existing functionality

### Proposed functionality

Allow for specifying an username in the Redis configuration, to be able to connect to Redis servers, which have an redis-username configured.

### Implementation:
* Add optional username parameter to redis-tasks and redis-caching in configuration.py
* Add TASKS_REDIS_USERNAME and CACHING_REDIS_USERNAME to settings.py
* Add username to cache-options and RQ_PARAMS in setting.py

### Use case

When you have an Redis instance with username configured, you are unable to connect.  E.g. Managed-Redis at OVH-cloud forces you to set an redis-user, so this service can't be used for NetBox.

If you could specify an redis user, it would allow users to connect to more redis-instances.

### Database changes

_No response_

### External dependencies

_No response_


Discussion:
I can implement it

Resolving patch:
diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py
--- a/netbox/netbox/configuration_example.py
+++ b/netbox/netbox/configuration_example.py
@@ -31,6 +31,7 @@
         # Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel
         # 'SENTINELS': [('mysentinel.redis.example.com', 6379)],
         # 'SENTINEL_SERVICE': 'netbox',
+        'USERNAME': '',
         'PASSWORD': '',
         'DATABASE': 0,
         'SSL': False,
@@ -44,6 +45,7 @@
         # Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel
         # 'SENTINELS': [('mysentinel.redis.example.com', 6379)],
         # 'SENTINEL_SERVICE': 'netbox',
+        'USERNAME': '',
         'PASSWORD': '',
         'DATABASE': 1,
         'SSL': False,
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
@@ -229,6 +229,7 @@ def _setting(name, default=None):
 ])
 TASKS_REDIS_SENTINEL_SERVICE = TASKS_REDIS.get('SENTINEL_SERVICE', 'default')
 TASKS_REDIS_SENTINEL_TIMEOUT = TASKS_REDIS.get('SENTINEL_TIMEOUT', 10)
+TASKS_REDIS_USERNAME = TASKS_REDIS.get('USERNAME', '')
 TASKS_REDIS_PASSWORD = TASKS_REDIS.get('PASSWORD', '')
 TASKS_REDIS_DATABASE = TASKS_REDIS.get('DATABASE', 0)
 TASKS_REDIS_SSL = TASKS_REDIS.get('SSL', False)
@@ -242,6 +243,8 @@ def _setting(name, default=None):
 CACHING_REDIS_HOST = REDIS['caching'].get('HOST', 'localhost')
 CACHING_REDIS_PORT = REDIS['caching'].get('PORT', 6379)
 CACHING_REDIS_DATABASE = REDIS['caching'].get('DATABASE', 0)
+CACHING_REDIS_USERNAME = REDIS['caching'].get('USERNAME', '')
+CACHING_REDIS_USERNAME_HOST = '@'.join(filter(None, [CACHING_REDIS_USERNAME, CACHING_REDIS_HOST]))
 CACHING_REDIS_PASSWORD = REDIS['caching'].get('PASSWORD', '')
 CACHING_REDIS_SENTINELS = REDIS['caching'].get('SENTINELS', [])
 CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'default')
@@ -251,7 +254,7 @@ def _setting(name, default=None):
 CACHES = {
     'default': {
         'BACKEND': 'django_redis.cache.RedisCache',
-        'LOCATION': f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}',
+        'LOCATION': f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_USERNAME_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}',
         'OPTIONS': {
             'CLIENT_CLASS': 'django_redis.client.DefaultClient',
             'PASSWORD': CACHING_REDIS_PASSWORD,
@@ -636,6 +639,7 @@ def _setting(name, default=None):
     }
 RQ_PARAMS.update({
     'DB': TASKS_REDIS_DATABASE,
+    'USERNAME': TASKS_REDIS_USERNAME,
     'PASSWORD': TASKS_REDIS_PASSWORD,
     'DEFAULT_TIMEOUT': RQ_DEFAULT_TIMEOUT,
 })


Test patch:
diff --git a/netbox/netbox/configuration_testing.py b/netbox/netbox/configuration_testing.py
--- a/netbox/netbox/configuration_testing.py
+++ b/netbox/netbox/configuration_testing.py
@@ -22,6 +22,7 @@
     'tasks': {
         'HOST': 'localhost',
         'PORT': 6379,
+        'USERNAME': '',
         'PASSWORD': '',
         'DATABASE': 0,
         'SSL': False,
@@ -29,6 +30,7 @@
     'caching': {
         'HOST': 'localhost',
         'PORT': 6379,
+        'USERNAME': '',
         'PASSWORD': '',
         'DATABASE': 1,
         'SSL': False,