Read list type of environment variables 45/7945/1
authorTimo Tietavainen <timo.tietavainen@nokia.com>
Mon, 14 Mar 2022 05:27:30 +0000 (07:27 +0200)
committerTimo Tietavainen <timo.tietavainen@nokia.com>
Mon, 14 Mar 2022 05:31:41 +0000 (07:31 +0200)
DBAAS Helm Charts appconfig has been changed to have individual ports
and master names for each SDL cluster due to problems seen with DBAAS
upgrade and rollback caused by re-allocation of the same address/port
of Redis to some other SDL cluster. Implement support for new list type
environment variables reading in SDL:
 * DBAAS_SERVICE_PORT
 * DBAAS_SERVICE_SENTINEL_PORT
 * DBAAS_MASTER_NAME

Issue-Id: RIC-698

Signed-off-by: Timo Tietavainen <timo.tietavainen@nokia.com>
Change-Id: Id75e53c3542f2c2b24a2fd815a5583b1394d8d79

docs/release-notes.rst
ricsdl-package/examples/notify.py
ricsdl-package/examples/sync.py
ricsdl-package/ricsdl/__init__.py
ricsdl-package/ricsdl/backend/redis.py
ricsdl-package/ricsdl/configuration.py
ricsdl-package/ricsdl/syncstorage.py
ricsdl-package/tests/backend/test_fake_dict_db.py
ricsdl-package/tests/backend/test_redis.py
ricsdl-package/tests/test_configuration.py

index 4013a07..2e1477b 100644 (file)
@@ -1,6 +1,6 @@
 ..
 ..  Copyright (c) 2019 AT&T Intellectual Property.
-..  Copyright (c) 2019 Nokia.
+..  Copyright (c) 2019-2022 Nokia.
 ..
 ..  Licensed under the Creative Commons Attribution 4.0 International
 ..  Public License (the "License"); you may not use this file except
@@ -33,6 +33,10 @@ This document provides the release notes of the ricsdl library.
 Version history
 ---------------
 
+[3.1.0] - 2022-03-14
+
+* Enable redis/sentinel port and sentinel master name configuration
+
 [3.0.2] - 2022-01-20
 
 * Bump Redis client version to 4.1.1 and replace deprecated Redis client '_compat'
index 006001f..47d9b04 100755 (executable)
@@ -38,13 +38,16 @@ Execution of  these examples requires:
  * Following environment variables are needed to set to the pod/container where the application
    utilizing SDL is going to be run.
      DBAAS_SERVICE_HOST = [DB service address]
-     DBAAS_SERVICE_PORT= [DB service port]
-     DBAAS_MASTER_NAME = [DB name]. Needed to set only if Redis sentinel is used to provide high
-     availability for Redis DB solution.
-     DBAAS_SERVICE_SENTINEL_PORT = [Redis sentinel port number]. Needed to set only if Redis
-     sentinel is in use.
-     DBASS_CLUSTER_ADDR_LIST = [list of DB service addresses]. Is set only if more than one
-     Redis sentinel groups are in use.
+     DBAAS_SERVICE_PORT= [Comma separated list of DB service ports]. Only one port supported in
+     RIC deployments, Nokia SEP deployments can have multiple ports.
+     DBAAS_MASTER_NAME = [Comma separated list of DB names]. Needed to set only if Redis
+     sentinel is used to provide high availability for Redis DB solution. Only one DB name
+     supported in RIC deployments, Nokia SEP deployments can have multiple DB names.
+     DBAAS_SERVICE_SENTINEL_PORT = [Comma separated list of Redis sentinel port number]. Needed
+     to set only if Redis sentinel is in use. Only one port supported in RIC deployments, Nokia
+     SEP deployments can have multiple ports.
+     DBASS_CLUSTER_ADDR_LIST = [Comma separated list of DB service addresses]. Is set only if
+     more than one Redis sentinel groups are in use. Only in use in Nokia SEP deployments.
    In official RIC deployments four first environment variables are defined in Helm configMaps
    of the DBaaS and these configurations can be loaded automatically as environment variables
    into application pods via `envFrom dbaas-appconfig` statement in an application Helm Charts.
index 618586d..ce6ad20 100755 (executable)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -40,18 +40,22 @@ Execution of  these examples requires:
  * Following environment variables are needed to set to the pod/container where the application
    utilizing SDL is going to be run.
      DBAAS_SERVICE_HOST = [DB service address]
-     DBAAS_SERVICE_PORT= [DB service port]
-     DBAAS_MASTER_NAME = [DB name]. Needed to set only if Redis sentinel is used to provide high
-     availability for Redis DB solution.
-     DBAAS_SERVICE_SENTINEL_PORT = [Redis sentinel port number]. Needed to set only if Redis
-     sentinel is in use.
-     DBASS_CLUSTER_ADDR_LIST = [list of DB service addresses]. Is set only if more than one
-     Redis sentinel groups are in use.
+     DBAAS_SERVICE_PORT= [Comma separated list of DB service ports]. Only one port supported in
+     RIC deployments, Nokia SEP deployments can have multiple ports.
+     DBAAS_MASTER_NAME = [Comma separated list of DB names]. Needed to set only if Redis
+     sentinel is used to provide high availability for Redis DB solution. Only one DB name
+     supported in RIC deployments, Nokia SEP deployments can have multiple DB names.
+     DBAAS_SERVICE_SENTINEL_PORT = [Comma separated list of Redis sentinel port number]. Needed
+     to set only if Redis sentinel is in use. Only one port supported in RIC deployments, Nokia
+     SEP deployments can have multiple ports.
+     DBASS_CLUSTER_ADDR_LIST = [Comma separated list of DB service addresses]. Is set only if
+     more than one Redis sentinel groups are in use. Only in use in Nokia SEP deployments.
    In official RIC deployments four first environment variables are defined in Helm configMaps
    of the DBaaS and these configurations can be loaded automatically as environment variables
    into application pods via `envFrom dbaas-appconfig` statement in an application Helm Charts.
    The last environment variable is not for time being in use in official RIC deployments, only
    in Nokia SEP deployments.
+
 """
 from ricsdl.syncstorage import SyncStorage
 from ricsdl.exceptions import RejectedByBackend, NotConnected, BackendError
index d8e421e..fc607ca 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ from .exceptions import (
 )
 
 
-__version__ = '3.0.2'
+__version__ = '3.1.0'
 
 
 __all__ = [
index 3ebc8cb..822afcf 100755 (executable)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -370,25 +370,22 @@ class RedisBackend(DbBackendAbc):
     def __create_redis_clients(self, config):
         clients = list()
         cfg_params = config.get_params()
-        if cfg_params.db_cluster_addr_list is None:
-            clients.append(self.__create_legacy_redis_client(cfg_params))
-        else:
-            for addr in cfg_params.db_cluster_addr_list.split(","):
-                client = self.__create_redis_client(cfg_params, addr)
-                clients.append(client)
-        return clients
+        for i, addr in enumerate(cfg_params.db_cluster_addrs):
+            port = cfg_params.db_ports[i] if i < len(cfg_params.db_ports) else ""
+            sport = cfg_params.db_sentinel_ports[i] if i < len(cfg_params.db_sentinel_ports) else ""
+            name = cfg_params.db_sentinel_master_names[i] if i < len(cfg_params.db_sentinel_master_names) else ""
 
-    def __create_legacy_redis_client(self, cfg_params):
-        return self.__create_redis_client(cfg_params, cfg_params.db_host)
+            client = self.__create_redis_client(addr, port, sport, name)
+            clients.append(client)
+        return clients
 
-    def __create_redis_client(self, cfg_params, addr):
+    def __create_redis_client(self, addr, port, sentinel_port, master_name):
         new_sentinel = None
         new_redis = None
-        if cfg_params.db_sentinel_port is None:
-            new_redis = Redis(host=addr, port=cfg_params.db_port, db=0, max_connections=20)
+        if len(sentinel_port) == 0:
+            new_redis = Redis(host=addr, port=port, db=0, max_connections=20)
         else:
-            sentinel_node = (addr, cfg_params.db_sentinel_port)
-            master_name = cfg_params.db_sentinel_master_name
+            sentinel_node = (addr, sentinel_port)
             new_sentinel = Sentinel([sentinel_node])
             new_redis = new_sentinel.master_for(master_name)
 
index e99e68e..99cde0f 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -33,9 +33,9 @@ class DbBackendType(Enum):
 
 class _Configuration():
     """This class implements Shared Data Layer (SDL) configurability."""
-    Params = namedtuple('Params', ['db_host', 'db_port', 'db_sentinel_port',
-                                   'db_sentinel_master_name',
-                                   'db_cluster_addr_list', 'db_type'])
+    Params = namedtuple('Params', ['db_host', 'db_ports', 'db_sentinel_ports',
+                                   'db_sentinel_master_names',
+                                   'db_cluster_addrs', 'db_type'])
 
     def __init__(self, fake_db_backend):
         self.params = self._read_configuration(fake_db_backend)
@@ -44,10 +44,10 @@ class _Configuration():
         return str(
             {
                 "DB host": self.params.db_host,
-                "DB port": self.params.db_port,
-                "DB master sentinel": self.params.db_sentinel_master_name,
-                "DB sentinel port": self.params.db_sentinel_port,
-                "DB cluster address list": self.params.db_cluster_addr_list,
+                "DB ports": self.params.db_ports,
+                "DB master sentinels": self.params.db_sentinel_master_names,
+                "DB sentinel ports": self.params.db_sentinel_ports,
+                "DB cluster addresses": self.params.db_cluster_addrs,
                 "DB type": self.params.db_type.name,
             }
         )
@@ -69,14 +69,48 @@ class _Configuration():
                 raise ValueError(msg)
 
             backend_type = DbBackendType.FAKE_DICT
+        host = os.getenv('DBAAS_SERVICE_HOST', "")
 
-        return _Configuration.Params(db_host=os.getenv('DBAAS_SERVICE_HOST'),
-                                     db_port=os.getenv('DBAAS_SERVICE_PORT'),
-                                     db_sentinel_port=os.getenv('DBAAS_SERVICE_SENTINEL_PORT'),
-                                     db_sentinel_master_name=os.getenv('DBAAS_MASTER_NAME'),
-                                     db_cluster_addr_list=os.getenv('DBAAS_CLUSTER_ADDR_LIST'),
+        port_env = os.getenv('DBAAS_SERVICE_PORT')
+        ports = port_env.split(",") if port_env is not None else list()
+
+        sentinel_port_env = os.getenv('DBAAS_SERVICE_SENTINEL_PORT')
+        sentinel_ports = sentinel_port_env.split(",") if sentinel_port_env is not None else list()
+
+        sentinel_name_env = os.getenv('DBAAS_MASTER_NAME')
+        sentinel_names = sentinel_name_env.split(",") if sentinel_name_env is not None else list()
+
+        addr_env = os.getenv('DBAAS_CLUSTER_ADDR_LIST')
+        addrs = addr_env.split(",") if addr_env is not None else list()
+
+        if len(addrs) == 0 and len(host) > 0:
+            addrs.append(host)
+
+        addrs, ports, sentinel_ports, sentinel_names = cls._complete_configuration(
+            addrs, ports, sentinel_ports, sentinel_names)
+
+        return _Configuration.Params(db_host=host,
+                                     db_ports=ports,
+                                     db_sentinel_ports=sentinel_ports,
+                                     db_sentinel_master_names=sentinel_names,
+                                     db_cluster_addrs=addrs,
                                      db_type=backend_type)
 
+    @classmethod
+    def _complete_configuration(cls, addrs, ports, sentinel_ports, sentinel_names):
+        if len(sentinel_ports) == 0:
+            if len(addrs) > len(ports) and len(ports) > 0:
+                for i in range(len(ports), len(addrs)):
+                    ports.append(ports[i - 1])
+        else:
+            if len(addrs) > len(sentinel_ports):
+                for i in range(len(sentinel_ports), len(addrs)):
+                    sentinel_ports.append(sentinel_ports[i - 1])
+            if len(addrs) > len(sentinel_names) and len(sentinel_names) > 0:
+                for i in range(len(sentinel_names), len(addrs)):
+                    sentinel_names.append(sentinel_names[i - 1])
+        return addrs, ports, sentinel_ports, sentinel_names
+
     @classmethod
     def get_event_separator(cls):
         return "___"
index b15365a..62e3a7f 100755 (executable)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -119,6 +119,7 @@ class SyncStorage(SyncStorageAbc):
     """
     def __init__(self, fake_db_backend=None) -> None:
         super().__init__()
+        self.__dbbackend = None
         self.__configuration = _Configuration(fake_db_backend)
         self.event_separator = self.__configuration.get_event_separator()
         self.__dbbackend = ricsdl.backend.get_backend_instance(self.__configuration)
@@ -141,7 +142,8 @@ class SyncStorage(SyncStorageAbc):
             return False
 
     def close(self):
-        self.__dbbackend.close()
+        if self.__dbbackend:
+            self.__dbbackend.close()
 
     @func_arg_checker(SdlTypeError, 1, ns=str, data_map=dict)
     def set(self, ns: str, data_map: Dict[str, bytes]) -> None:
index 6c5fd88..67b6ac1 100755 (executable)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -50,10 +50,10 @@ def fake_dict_backend_fixture(request):
 
     request.cls.configuration = Mock()
     mock_conf_params = _Configuration.Params(db_host=None,
-                                             db_port=None,
-                                             db_sentinel_port=None,
-                                             db_sentinel_master_name=None,
-                                             db_cluster_addr_list=None,
+                                             db_ports=None,
+                                             db_sentinel_ports=None,
+                                             db_sentinel_master_names=None,
+                                             db_cluster_addrs=None,
                                              db_type=DbBackendType.FAKE_DICT)
     request.cls.configuration.get_params.return_value = mock_conf_params
     request.cls.db = ricsdl.backend.get_backend_instance(request.cls.configuration)
@@ -327,10 +327,10 @@ def fake_dict_backend_lock_fixture(request):
 
     request.cls.configuration = Mock()
     mock_conf_params = _Configuration.Params(db_host=None,
-                                             db_port=None,
-                                             db_sentinel_port=None,
-                                             db_sentinel_master_name=None,
-                                             db_cluster_addr_list=None,
+                                             db_ports=None,
+                                             db_sentinel_ports=None,
+                                             db_sentinel_master_names=None,
+                                             db_cluster_addrs=None,
                                              db_type=DbBackendType.FAKE_DICT)
     request.cls.configuration.get_params.return_value = mock_conf_params
     request.cls.lock = ricsdl.backend.get_backend_lock_instance(request.cls.configuration,
index 100795e..744f927 100755 (executable)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -32,26 +32,26 @@ EVENT_SEPARATOR = "___"
 
 def get_test_sdl_standby_config():
     return _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
-                                 db_port=6379,
-                                 db_sentinel_port=None,
-                                 db_sentinel_master_name=None,
-                                 db_cluster_addr_list=None,
+                                 db_ports=['6379'],
+                                 db_sentinel_ports=[],
+                                 db_sentinel_master_names=[],
+                                 db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt'],
                                  db_type=DbBackendType.REDIS)
 
 def get_test_sdl_sentinel_config():
     return _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
-                                 db_port=6379,
-                                 db_sentinel_port=26379,
-                                 db_sentinel_master_name='dbaasmaster',
-                                 db_cluster_addr_list=None,
+                                 db_ports=['6379'],
+                                 db_sentinel_ports=['26379'],
+                                 db_sentinel_master_names=['dbaasmaster'],
+                                 db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt'],
                                  db_type=DbBackendType.REDIS)
 
 def get_test_sdl_sentinel_cluster_config():
     return _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
-                                 db_port=6379,
-                                 db_sentinel_port=26379,
-                                 db_sentinel_master_name='dbaasmaster',
-                                 db_cluster_addr_list='service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt',
+                                 db_ports=['6379','6380'],
+                                 db_sentinel_ports=['26379','26380'],
+                                 db_sentinel_master_names=['dbaasmaster-cluster-0','dbaasmaster-cluster-1'],
+                                 db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt','service-ricplt-dbaas-tcp-cluster-1.ricplt'],
                                  db_type=DbBackendType.REDIS)
 
 @pytest.fixture()
@@ -105,7 +105,7 @@ def redis_backend_fixture(request, redis_backend_common_fixture):
             request.cls.mock_pubsub_thread.is_alive.return_value = False
         request.cls.db = db
 
-        mock_redis.assert_called_once_with(db=0, host=cfg.db_host, max_connections=20, port=cfg.db_port)
+        mock_redis.assert_called_once_with(db=0, host=cfg.db_host, max_connections=20, port=cfg.db_ports[0])
         mock_pubsub.assert_called_once_with(EVENT_SEPARATOR, request.cls.mock_redis.connection_pool,
                                             ignore_subscribe_messages=True)
         assert request.cls.mock_redis.set_response_callback.call_count == 2
@@ -124,8 +124,8 @@ def redis_backend_fixture(request, redis_backend_common_fixture):
             request.cls.mock_pubsub_thread.is_alive.return_value = False
         request.cls.db = db
 
-        mock_sentinel.assert_called_once_with([(cfg.db_host, cfg.db_sentinel_port)])
-        mock_sentinel.master_for.called_once_with(cfg.db_sentinel_master_name)
+        mock_sentinel.assert_called_once_with([(cfg.db_host, cfg.db_sentinel_ports[0])])
+        mock_sentinel.master_for.called_once_with(cfg.db_sentinel_master_names[0])
         mock_pubsub.assert_called_once_with(EVENT_SEPARATOR, request.cls.mock_redis.connection_pool,
                                             ignore_subscribe_messages=True)
         assert request.cls.mock_redis.set_response_callback.call_count == 2
@@ -146,12 +146,12 @@ def redis_backend_fixture(request, redis_backend_common_fixture):
 
         assert mock_sentinel.call_count == 2
         mock_sentinel.assert_has_calls([
-            call([('service-ricplt-dbaas-tcp-cluster-0.ricplt', cfg.db_sentinel_port)]),
-            call([('service-ricplt-dbaas-tcp-cluster-1.ricplt', cfg.db_sentinel_port)]),
+            call([('service-ricplt-dbaas-tcp-cluster-0.ricplt', '26379')]),
+            call([('service-ricplt-dbaas-tcp-cluster-1.ricplt', '26380')]),
         ], any_order=True)
         assert mock_sentinel.return_value.master_for.call_count == 2
         mock_sentinel.return_value.master_for.assert_has_calls(
-            [call(cfg.db_sentinel_master_name), call(cfg.db_sentinel_master_name)], any_order=True,
+            [call('dbaasmaster-cluster-0'), call('dbaasmaster-cluster-1')], any_order=True,
         )
         assert mock_pubsub.call_count == 2
         mock_pubsub.assert_has_calls([
@@ -728,10 +728,10 @@ def redis_backend_lock_fixture(request, mock_redis_lock):
 
     request.cls.configuration = Mock()
     mock_conf_params = _Configuration.Params(db_host=None,
-                                             db_port=None,
-                                             db_sentinel_port=None,
-                                             db_sentinel_master_name=None,
-                                             db_cluster_addr_list=None,
+                                             db_ports=None,
+                                             db_sentinel_ports=None,
+                                             db_sentinel_master_names=None,
+                                             db_cluster_addrs=None,
                                              db_type=DbBackendType.REDIS)
     request.cls.configuration.get_params.return_value = mock_conf_params
 
index 1c89663..28fa90e 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2019 AT&T Intellectual Property.
-# Copyright (c) 2018-2019 Nokia.
+# Copyright (c) 2018-2022 Nokia.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -27,13 +27,12 @@ from ricsdl.configuration import DbBackendType
 @pytest.fixture()
 def config_fixture(request, monkeypatch):
     monkeypatch.setenv('DBAAS_SERVICE_HOST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt')
-    monkeypatch.setenv('DBAAS_SERVICE_PORT', '10000')
-    monkeypatch.setenv('DBAAS_SERVICE_SENTINEL_PORT', '11000')
-    monkeypatch.setenv('DBAAS_MASTER_NAME', 'my-master')
+    monkeypatch.setenv('DBAAS_SERVICE_PORT', '10000,10001')
+    monkeypatch.setenv('DBAAS_SERVICE_SENTINEL_PORT', '11000,11001')
+    monkeypatch.setenv('DBAAS_MASTER_NAME', 'my-master-0,my-master-1')
     monkeypatch.setenv('DBAAS_CLUSTER_ADDR_LIST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt')
     request.cls.config = _Configuration(fake_db_backend=None)
 
-
 @pytest.fixture
 def fake_db_config_fixture(request, monkeypatch):
     monkeypatch.delenv('DBAAS_SERVICE_HOST', raising=False)
@@ -43,22 +42,21 @@ def fake_db_config_fixture(request, monkeypatch):
     monkeypatch.delenv('DBAAS_CLUSTER_ADDR_LIST', raising=False)
     request.cls.config = _Configuration(fake_db_backend='dict')
 
-
 class TestConfiguration:
     def test_get_params_function_returns_read_configuration(self, config_fixture):
         expected_config = _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
-                                                db_port='10000',
-                                                db_sentinel_port='11000',
-                                                db_sentinel_master_name='my-master',
-                                                db_cluster_addr_list='service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt',
+                                                db_ports=['10000','10001'],
+                                                db_sentinel_ports=['11000','11001'],
+                                                db_sentinel_master_names=['my-master-0','my-master-1'],
+                                                db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt','service-ricplt-dbaas-tcp-cluster-1.ricplt'],
                                                 db_type=DbBackendType.REDIS)
         assert expected_config == self.config.get_params()
 
     def test_get_params_function_can_return_fake_db_configuration(self, fake_db_config_fixture):
-        expected_config = _Configuration.Params(db_host=None, db_port=None,
-                                                db_sentinel_port=None,
-                                                db_sentinel_master_name=None,
-                                                db_cluster_addr_list=None,
+        expected_config = _Configuration.Params(db_host='', db_ports=[],
+                                                db_sentinel_ports=[],
+                                                db_sentinel_master_names=[],
+                                                db_cluster_addrs=[],
                                                 db_type=DbBackendType.FAKE_DICT)
         assert expected_config == self.config.get_params()
 
@@ -72,18 +70,61 @@ class TestConfiguration:
 
     def test_configuration_object_string_representation(self, config_fixture):
         expected_config_info = {'DB host': 'service-ricplt-dbaas-tcp-cluster-0.ricplt',
-                                'DB port': '10000',
-                                'DB master sentinel': 'my-master',
-                                'DB sentinel port': '11000',
-                                'DB cluster address list': 'service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt',
+                                'DB ports': ['10000','10001'],
+                                'DB master sentinels': ['my-master-0','my-master-1'],
+                                'DB sentinel ports': ['11000','11001'],
+                                'DB cluster addresses': ['service-ricplt-dbaas-tcp-cluster-0.ricplt','service-ricplt-dbaas-tcp-cluster-1.ricplt'],
                                 'DB type': 'REDIS'}
         assert str(self.config) == str(expected_config_info)
 
     def test_configuration_object_string_representation_if_fake_db(self, fake_db_config_fixture):
-        expected_config_info = {'DB host': None,
-                                'DB port': None,
-                                'DB master sentinel': None,
-                                'DB sentinel port': None,
-                                'DB cluster address list': None,
+        expected_config_info = {'DB host': '',
+                                'DB ports': [],
+                                'DB master sentinels': [],
+                                'DB sentinel ports': [],
+                                'DB cluster addresses': [],
                                 'DB type': 'FAKE_DICT'}
         assert str(self.config) == str(expected_config_info)
+
+    def test_complete_configuration_if_less_ports_than_addresses(self, monkeypatch):
+        monkeypatch.setenv('DBAAS_SERVICE_HOST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt')
+        monkeypatch.setenv('DBAAS_SERVICE_PORT', '10000')
+        monkeypatch.setenv('DBAAS_CLUSTER_ADDR_LIST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt')
+
+        expected_config = _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
+                                                db_ports=['10000','10000'],
+                                                db_sentinel_ports=[],
+                                                db_sentinel_master_names=[],
+                                                db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt','service-ricplt-dbaas-tcp-cluster-1.ricplt'],
+                                                db_type=DbBackendType.REDIS)
+        assert expected_config == _Configuration(fake_db_backend=None).get_params()
+
+    def test_complete_configuration_if_less_sentinel_ports_than_addresses(self, monkeypatch):
+        monkeypatch.setenv('DBAAS_SERVICE_HOST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt')
+        monkeypatch.setenv('DBAAS_SERVICE_PORT', '10000,10001')
+        monkeypatch.setenv('DBAAS_SERVICE_SENTINEL_PORT', '11000')
+        monkeypatch.setenv('DBAAS_MASTER_NAME', 'my-master-0,my-master-1')
+        monkeypatch.setenv('DBAAS_CLUSTER_ADDR_LIST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt')
+
+        expected_config = _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
+                                                db_ports=['10000','10001'],
+                                                db_sentinel_ports=['11000','11000'],
+                                                db_sentinel_master_names=['my-master-0','my-master-1'],
+                                                db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt','service-ricplt-dbaas-tcp-cluster-1.ricplt'],
+                                                db_type=DbBackendType.REDIS)
+        assert expected_config == _Configuration(fake_db_backend=None).get_params()
+
+    def test_complete_configuration_if_less_sentinel_names_than_addresses(self, monkeypatch):
+        monkeypatch.setenv('DBAAS_SERVICE_HOST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt')
+        monkeypatch.setenv('DBAAS_SERVICE_PORT', '10000,10001')
+        monkeypatch.setenv('DBAAS_SERVICE_SENTINEL_PORT', '11000,11001')
+        monkeypatch.setenv('DBAAS_MASTER_NAME', 'my-master-0')
+        monkeypatch.setenv('DBAAS_CLUSTER_ADDR_LIST', 'service-ricplt-dbaas-tcp-cluster-0.ricplt,service-ricplt-dbaas-tcp-cluster-1.ricplt')
+
+        expected_config = _Configuration.Params(db_host='service-ricplt-dbaas-tcp-cluster-0.ricplt',
+                                                db_ports=['10000','10001'],
+                                                db_sentinel_ports=['11000','11001'],
+                                                db_sentinel_master_names=['my-master-0','my-master-0'],
+                                                db_cluster_addrs=['service-ricplt-dbaas-tcp-cluster-0.ricplt','service-ricplt-dbaas-tcp-cluster-1.ricplt'],
+                                                db_type=DbBackendType.REDIS)
+        assert expected_config == _Configuration(fake_db_backend=None).get_params()