diff --git a/src/swsssdk/__init__.py b/src/swsssdk/__init__.py index 1989c93937cc..1722bb70182b 100644 --- a/src/swsssdk/__init__.py +++ b/src/swsssdk/__init__.py @@ -7,32 +7,8 @@ logger.setLevel(logging.INFO) logger.addHandler(logging.NullHandler()) -import json -import os - - -def _load_connector_map(): - """ - Get database map from the packaged config. - """ - global _connector_map - db_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'database.json') - - try: - with open(db_file, 'r') as f: - _connector_map = json.load(f) - except (OSError, IOError): - # Missing configuration means we can't configure the database index. Fatal error. - msg = "Could not open database index '{}'".format(db_file) - logger.exception(msg) - raise RuntimeError(msg) - - -_connector_map = {} -_load_connector_map() - try: - from .dbconnector import SonicV2Connector + from .dbconnector import SonicDBConfig, SonicV2Connector from .configdb import ConfigDBConnector except (KeyError, ValueError): msg = "Failed to database connector objects -- incorrect database config schema." diff --git a/src/swsssdk/config/database.json b/src/swsssdk/config/database.json deleted file mode 100644 index 640012f15945..000000000000 --- a/src/swsssdk/config/database.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "SonicV2Connector": { - "db_map": { - "APPL_DB": { - "db": 0, - "separator": ":" - }, - "ASIC_DB": { - "db": 1, - "separator": ":" - }, - "COUNTERS_DB": { - "db": 2, - "separator": ":" - }, - "CONFIG_DB": { - "db": 4, - "separator": "|" - }, - "STATE_DB": { - "db": 6, - "separator": "|" - }, - "SNMP_OVERLAY_DB": { - "db": 7, - "separator": "|" - - } - } - } -} diff --git a/src/swsssdk/config/database_config.json b/src/swsssdk/config/database_config.json new file mode 100644 index 000000000000..b86ae11bba98 --- /dev/null +++ b/src/swsssdk/config/database_config.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + } + }, + "VERSION" : "1.0" +} diff --git a/src/swsssdk/configdb.py b/src/swsssdk/configdb.py index 2fa896f25ab5..fe77c5838a8a 100644 --- a/src/swsssdk/configdb.py +++ b/src/swsssdk/configdb.py @@ -37,11 +37,11 @@ def __init__(self, **kwargs): self.handlers = {} def __wait_for_db_init(self): - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) pubsub = client.pubsub() initialized = client.get(self.INIT_INDICATOR) if not initialized: - pattern = "__keyspace@{}__:{}".format(self.db_map[self.db_name]['db'], self.INIT_INDICATOR) + pattern = "__keyspace@{}__:{}".format(self.get_dbid(self.db_name), self.INIT_INDICATOR) pubsub.psubscribe(pattern) for item in pubsub.listen(): if item['type'] == 'pmessage': @@ -55,7 +55,7 @@ def __wait_for_db_init(self): def db_connect(self, dbname, wait_for_init=False, retry_on=False): self.db_name = dbname - self.KEY_SEPARATOR = self.TABLE_NAME_SEPARATOR = self.db_map[self.db_name]['separator'] + self.KEY_SEPARATOR = self.TABLE_NAME_SEPARATOR = self.get_db_separator(self.db_name) SonicV2Connector.connect(self, self.db_name, retry_on) if wait_for_init: self.__wait_for_db_init() @@ -89,15 +89,15 @@ def __fire(self, table, key, data): def listen(self): """Start listen Redis keyspace events and will trigger corresponding handlers when content of a table changes. """ - self.pubsub = self.redis_clients[self.db_name].pubsub() - self.pubsub.psubscribe("__keyspace@{}__:*".format(self.db_map[self.db_name]['db'])) + self.pubsub = self.get_redis_client(self.db_name).pubsub() + self.pubsub.psubscribe("__keyspace@{}__:*".format(self.get_dbid(self.db_name))) for item in self.pubsub.listen(): if item['type'] == 'pmessage': key = item['channel'].split(':', 1)[1] try: (table, row) = key.split(self.TABLE_NAME_SEPARATOR, 1) if self.handlers.has_key(table): - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) data = self.__raw_to_typed(client.hgetall(key)) self.__fire(table, row, data) except ValueError: @@ -171,7 +171,7 @@ def set_entry(self, table, key, data): Pass None as data will delete the entry. """ key = self.serialize_key(key) - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) _hash = '{}{}{}'.format(table.upper(), self.TABLE_NAME_SEPARATOR, key) if data == None: client.delete(_hash) @@ -193,7 +193,7 @@ def mod_entry(self, table, key, data): Pass None as data will delete the entry. """ key = self.serialize_key(key) - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) _hash = '{}{}{}'.format(table.upper(), self.TABLE_NAME_SEPARATOR, key) if data == None: client.delete(_hash) @@ -210,7 +210,7 @@ def get_entry(self, table, key): Empty dictionary if table does not exist or entry does not exist. """ key = self.serialize_key(key) - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) _hash = '{}{}{}'.format(table.upper(), self.TABLE_NAME_SEPARATOR, key) return self.__raw_to_typed(client.hgetall(_hash)) @@ -223,7 +223,7 @@ def get_keys(self, table, split=True): Returns: List of keys. """ - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) pattern = '{}{}*'.format(table.upper(), self.TABLE_NAME_SEPARATOR) keys = client.keys(pattern) data = [] @@ -250,7 +250,7 @@ def get_table(self, table): or { ('l1_key', 'l2_key', ...): {'column_key': value, ...}, ...} for a multi-key table. Empty dictionary if table does not exist. """ - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) pattern = '{}{}*'.format(table.upper(), self.TABLE_NAME_SEPARATOR) keys = client.keys(pattern) data = {} @@ -274,7 +274,7 @@ def delete_table(self, table): Args: table: Table name. """ - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) pattern = '{}{}*'.format(table.upper(), self.TABLE_NAME_SEPARATOR) keys = client.keys(pattern) data = {} @@ -307,7 +307,7 @@ def get_config(self): ... } """ - client = self.redis_clients[self.db_name] + client = self.get_redis_client(self.db_name) keys = client.keys('*') data = {} for key in keys: diff --git a/src/swsssdk/dbconnector.py b/src/swsssdk/dbconnector.py index ec830028e97c..32c895d37a64 100644 --- a/src/swsssdk/dbconnector.py +++ b/src/swsssdk/dbconnector.py @@ -1,20 +1,190 @@ """ Database connection module for SwSS """ -from . import _connector_map, logger +from . import logger from .interface import DBInterface - +import os +import json # FIXME: Convert to metaclasses when Py2 support is removed. Metaclasses have unique interfaces to Python2/Python3. +class SonicDBConfig(object): + SONIC_DB_CONFIG_FILE = "/var/run/redis/sonic-db/database_config.json" + _sonic_db_config_init = False + _sonic_db_config = {} + + @staticmethod + def load_sonic_db_config(sonic_db_file_path=SONIC_DB_CONFIG_FILE): + """ + Get multiple database config from the database_config.json + """ + if SonicDBConfig._sonic_db_config_init == True: + return + + try: + if os.path.isfile(sonic_db_file_path) == False: + msg = "'{}' is not found, it is not expected in production devices!!".format(sonic_db_file_path) + logger.warning(msg) + sonic_db_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'database_config.json') + with open(sonic_db_file_path, "r") as read_file: + SonicDBConfig._sonic_db_config = json.load(read_file) + except (OSError, IOError): + msg = "Could not open sonic database config file '{}'".format(sonic_db_file_path) + logger.exception(msg) + raise RuntimeError(msg) + SonicDBConfig._sonic_db_config_init = True + + @staticmethod + def db_name_validation(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + if db_name not in SonicDBConfig._sonic_db_config["DATABASES"]: + msg = "{} is not a valid database name in configuration file".format(db_name) + logger.exception(msg) + raise RuntimeError(msg) + + @staticmethod + def inst_name_validation(inst_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + if inst_name not in SonicDBConfig._sonic_db_config["INSTANCES"]: + msg = "{} is not a valid instance name in configuration file".format(inst_name) + logger.exception(msg) + raise RuntimeError(msg) + + @staticmethod + def get_dblist(): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + return SonicDBConfig._sonic_db_config["DATABASES"].keys() + + @staticmethod + def get_instance(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + SonicDBConfig.db_name_validation(db_name) + inst_name = SonicDBConfig._sonic_db_config["DATABASES"][db_name]["instance"] + SonicDBConfig.inst_name_validation(inst_name) + return SonicDBConfig._sonic_db_config["INSTANCES"][inst_name] + + @staticmethod + def get_socket(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + SonicDBConfig.db_name_validation(db_name) + return SonicDBConfig.get_instance(db_name)["unix_socket_path"] + + @staticmethod + def get_hostname(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + SonicDBConfig.db_name_validation(db_name) + return SonicDBConfig.get_instance(db_name)["hostname"] + + @staticmethod + def get_port(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + SonicDBConfig.db_name_validation(db_name) + return SonicDBConfig.get_instance(db_name)["port"] + + @staticmethod + def get_dbid(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + SonicDBConfig.db_name_validation(db_name) + return SonicDBConfig._sonic_db_config["DATABASES"][db_name]["id"] + + @staticmethod + def get_separator(db_name): + if SonicDBConfig._sonic_db_config_init == False: + SonicDBConfig.load_sonic_db_config() + SonicDBConfig.db_name_validation(db_name) + return SonicDBConfig._sonic_db_config["DATABASES"][db_name]["separator"] + class SonicV2Connector(DBInterface): - def __init__(self, **kwargs): + def __init__(self, use_unix_socket_path=True, **kwargs): super(SonicV2Connector, self).__init__(**kwargs) + self.use_unix_socket_path = use_unix_socket_path + for db_name in self.get_db_list(): + # set a database name as a constant value attribute. + setattr(self, db_name, db_name) - pass + def connect(self, db_name, retry_on=True): + if self.use_unix_socket_path == False: + self.redis_kwargs["unix_socket_path"] = self.get_db_socket(db_name) + self.redis_kwargs["host"] = None + self.redis_kwargs["port"] = None + else: + self.redis_kwargs["host"] = self.get_db_hostname(db_name) + self.redis_kwargs["port"] = self.get_db_port(db_name) + self.redis_kwargs["unix_socket_path"] = None + db_id = self.get_dbid(db_name) + super(SonicV2Connector, self).connect(db_id, retry_on) + + def close(self, db_name): + db_id = self.get_dbid(db_name) + super(SonicV2Connector, self).close(db_id) + + def get_db_list(self): + return SonicDBConfig.get_dblist() + def get_db_instance(self, db_name): + return SonicDBConfig.get_instance(db_name) -SonicV2Connector.db_map = _connector_map[SonicV2Connector.__name__]['db_map'] + def get_db_socket(self, db_name): + return SonicDBConfig.get_socket(db_name) -if len(SonicV2Connector.db_map) != len({v['db'] for k, v in SonicV2Connector.db_map.items()}): - raise RuntimeError("Duplicate DB index detected in configuration.") + def get_db_hostname(self, db_name): + return SonicDBConfig.get_hostname(db_name) + + def get_db_port(self, db_name): + return SonicDBConfig.get_port(db_name) + + def get_dbid(self, db_name): + return SonicDBConfig.get_dbid(db_name) + + def get_db_separator(self, db_name): + return SonicDBConfig.get_separator(db_name) + + def get_redis_client(self, db_name): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).get_redis_client(db_id) + + def publish(self, db_name, channel, message): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).publish(db_id, channel, message) + + def expire(self, db_name, key, timeout_sec): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).expire(db_id, key, timeout_sec) + + def exists(self, db_name, key): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).exists(db_id, key) + + def keys(self, db_name, pattern='*', *args, **kwargs): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).keys(db_id, pattern, *args, **kwargs) + + def get(self, db_name, _hash, key, *args, **kwargs): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).get(db_id, _hash, key, *args, **kwargs) + + def get_all(self, db_name, _hash, *args, **kwargs): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).get_all(db_id, _hash, *args, **kwargs) + + def set(self, db_name, _hash, key, val, *args, **kwargs): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).set(db_id, _hash, key, val, *args, **kwargs) + + def delete(self, db_name, key, *args, **kwargs): + db_id = self.get_dbid(db_name) + return super(SonicV2Connector, self).delete(db_id, key, *args, **kwargs) + + def delete_all_by_pattern(self, db_name, pattern, *args, **kwargs): + db_id = self.get_dbid(db_name) + super(SonicV2Connector, self).delete_all_by_pattern(db_id, pattern, *args, **kwargs) + + pass diff --git a/src/swsssdk/interface.py b/src/swsssdk/interface.py index c77530b719ed..a3a13cb1b17a 100644 --- a/src/swsssdk/interface.py +++ b/src/swsssdk/interface.py @@ -18,7 +18,7 @@ def blockable(f): class SonicV2Connector: @blockable - def keys(self, db_name): + def keys(self, db_id): # ... # call with: @@ -29,26 +29,26 @@ def keys(self, db_name): """ @wraps(f) - def wrapped(inst, db_name, *args, **kwargs): + def wrapped(inst, db_id, *args, **kwargs): blocking = kwargs.pop('blocking', False) attempts = 0 while True: try: - ret_data = f(inst, db_name, *args, **kwargs) - inst._unsubscribe_keyspace_notification(db_name) + ret_data = f(inst, db_id, *args, **kwargs) + inst._unsubscribe_keyspace_notification(db_id) return ret_data except UnavailableDataError as e: if blocking: - if db_name in inst.keyspace_notification_channels: - result = inst._unavailable_data_handler(db_name, e.data) + if db_id in inst.keyspace_notification_channels: + result = inst._unavailable_data_handler(db_id, e.data) if result: continue # received updates, try to read data again else: - inst._unsubscribe_keyspace_notification(db_name) + inst._unsubscribe_keyspace_notification(db_id) raise # No updates was received. Raise exception else: # Subscribe to updates and try it again (avoiding race condition) - inst._subscribe_keyspace_notification(db_name) + inst._subscribe_keyspace_notification(db_id) else: return None except redis.exceptions.ResponseError: @@ -57,12 +57,12 @@ def wrapped(inst, db_name, *args, **kwargs): Retrying the request won't pass unless the schema itself changes. In this case, the error should be attributed to the application itself. Re-raise the error. """ - logger.exception("Bad DB request [{}:{}]{{ {} }}".format(db_name, f.__name__, str(args))) + logger.exception("Bad DB request [{}:{}]{{ {} }}".format(db_id, f.__name__, str(args))) raise except (redis.exceptions.RedisError, OSError): attempts += 1 - inst._connection_error_handler(db_name) - msg = "DB access failure by [{}:{}]{{ {} }}".format(db_name, f.__name__, str(args)) + inst._connection_error_handler(db_id) + msg = "DB access failure by [{}:{}]{{ {} }}".format(db_id, f.__name__, str(args)) if BLOCKING_ATTEMPT_ERROR_THRESHOLD < attempts < BLOCKING_ATTEMPT_SUPPRESSION: # Repeated access failures implies the database itself is unhealthy. logger.exception(msg=msg) @@ -75,7 +75,7 @@ def wrapped(inst, db_name, *args, **kwargs): class DBRegistry(dict): def __getitem__(self, item): if item not in self: - raise MissingClientError("No client connected for db_name '{}'".format(item)) + raise MissingClientError("No client connected for db_id '{}'".format(item)) return dict.__getitem__(self, item) @@ -142,8 +142,6 @@ class DBInterface(object): ACS Redis db mainly uses hash, therefore h is selected. """ - db_map = dict() - def __init__(self, **kwargs): super(DBInterface, self).__init__() @@ -161,147 +159,128 @@ def __init__(self, **kwargs): # notifications for each client self.keyspace_notification_channels = DBRegistry() - for db_name in self.db_map: - # set a database name as a constant value attribute. - setattr(self, db_name, db_name) - - @property - def db_list(self): - return self.db_map.keys() - - @classmethod - def get_dbid(cls, db_name): - """ - :returns the database index by name. None if the name doesn't exist. - """ - try: - return cls.db_map[db_name]['db'] - except (KeyError, ValueError): - return None - - def connect(self, db_name, retry_on=True): + def connect(self, db_id, retry_on=True): """ - :param db_name: named database to connect to + :param db_id: database id to connect to :param retry_on: if ``True`` -- will attempt to connect continuously. if ``False``, only one attempt will be made. """ if retry_on: - self._persistent_connect(db_name) + self._persistent_connect(db_id) else: - self._onetime_connect(db_name) + self._onetime_connect(db_id) - def _onetime_connect(self, db_name): + def _onetime_connect(self, db_id): """ - Connect to named database. + Connect to database id. """ - db_id = self.get_dbid(db_name) if db_id is None: - raise ValueError("No database ID configured for '{}'".format(db_name)) + raise ValueError("No database ID configured for '{}'".format(db_id)) - client = redis.StrictRedis(db=self.db_map[db_name]['db'], **self.redis_kwargs) + client = redis.StrictRedis(db=db_id, **self.redis_kwargs) # Enable the notification mechanism for keyspace events in Redis client.config_set('notify-keyspace-events', self.KEYSPACE_EVENTS) - self.redis_clients[db_name] = client + self.redis_clients[db_id] = client - def _persistent_connect(self, db_name): + def _persistent_connect(self, db_id): """ - Keep reconnecting to Database 'db_name' until success + Keep reconnecting to Database 'db_id' until success """ while True: try: - self._onetime_connect(db_name) + self._onetime_connect(db_id) return except RedisError: t_wait = self.CONNECT_RETRY_WAIT_TIME - logger.warning("Connecting to DB '{}' failed, will retry in {}s".format(db_name, t_wait)) - self.close(db_name) + logger.warning("Connecting to DB '{}' failed, will retry in {}s".format(db_id, t_wait)) + self.close(db_id) time.sleep(t_wait) - def close(self, db_name): + def close(self, db_id): """ Close all client(s) / keyspace channels. - :param db_name: DB to disconnect from. + :param db_id: DB to disconnect from. """ - if db_name in self.redis_clients: - self.redis_clients[db_name].connection_pool.disconnect() - if db_name in self.keyspace_notification_channels: - self.keyspace_notification_channels[db_name].close() + if db_id in self.redis_clients: + self.redis_clients[db_id].connection_pool.disconnect() + if db_id in self.keyspace_notification_channels: + self.keyspace_notification_channels[db_id].close() - def _subscribe_keyspace_notification(self, db_name): + def _subscribe_keyspace_notification(self, db_id): """ Subscribe the chosent client to keyspace event notifications """ logger.debug("Subscribe to keyspace notification") - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] pubsub = client.pubsub() pubsub.psubscribe(self.KEYSPACE_PATTERN) - self.keyspace_notification_channels[db_name] = pubsub + self.keyspace_notification_channels[db_id] = pubsub - def _unsubscribe_keyspace_notification(self, db_name): + def _unsubscribe_keyspace_notification(self, db_id): """ Unsubscribe the chosent client from keyspace event notifications """ - if db_name in self.keyspace_notification_channels: + if db_id in self.keyspace_notification_channels: logger.debug("Unsubscribe from keyspace notification") - self.keyspace_notification_channels[db_name].close() - del self.keyspace_notification_channels[db_name] + self.keyspace_notification_channels[db_id].close() + del self.keyspace_notification_channels[db_id] - def get_redis_client(self, db_name): + def get_redis_client(self, db_id): """ - :param db_name: Name of the DB to query + :param db_id: Name of the DB to query :return: The Redis client instance. """ - return self.redis_clients[db_name] + return self.redis_clients[db_id] - def publish(self, db_name, channel, message): + def publish(self, db_id, channel, message): """ Publish message via the channel """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] return client.publish(channel, message) - def expire(self, db_name, key, timeout_sec): + def expire(self, db_id, key, timeout_sec): """ Set a timeout on a key """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] return client.expire(key, timeout_sec) - def exists(self, db_name, key): + def exists(self, db_id, key): """ Check if a key exist in the db """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] return client.exists(key) @blockable - def keys(self, db_name, pattern='*'): + def keys(self, db_id, pattern='*'): """ - Retrieve all the keys of DB %db_name + Retrieve all the keys of DB %db_id """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] keys = client.keys(pattern=pattern) if not keys: - message = "DB '{}' is empty!".format(db_name) + message = "DB '{}' is empty!".format(db_id) logger.warning(message) raise UnavailableDataError(message, b'hset') else: return keys @blockable - def get(self, db_name, _hash, key): + def get(self, db_id, _hash, key): """ Retrieve the value of Key %key from Hashtable %hash - in Database %db_name + in Database %db_id Parameter %blocking indicates whether to wait when the query fails """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] val = client.hget(_hash, key) if not val: - message = "Key '{}' field '{}' unavailable in database '{}'".format(_hash, key, db_name) + message = "Key '{}' field '{}' unavailable in database '{}'".format(_hash, key, db_id) logger.warning(message) raise UnavailableDataError(message, _hash) else: @@ -309,17 +288,17 @@ def get(self, db_name, _hash, key): return None if val == b'None' else val @blockable - def get_all(self, db_name, _hash): + def get_all(self, db_id, _hash): """ - Get Hashtable %hash from DB %db_name + Get Hashtable %hash from DB %db_id Parameter %blocking indicates whether to wait if the hashtable has not been created yet """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] table = client.hgetall(_hash) if not table: - message = "Key '{}' unavailable in database '{}'".format(_hash, db_name) + message = "Key '{}' unavailable in database '{}'".format(_hash, db_id) logger.warning(message) raise UnavailableDataError(message, _hash) else: @@ -327,35 +306,35 @@ def get_all(self, db_name, _hash): return {k: None if v == b'None' else v for k, v in table.items()} @blockable - def set(self, db_name, _hash, key, val): + def set(self, db_id, _hash, key, val): """ - Add %(key, val) to Hashtable %hash in DB %db_name + Add %(key, val) to Hashtable %hash in DB %db_id Parameter %blocking indicates whether to retry in case of failure """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] return client.hset(_hash, key, val) @blockable - def delete(self, db_name, key): + def delete(self, db_id, key): """ - Delete %key from DB %db_name + Delete %key from DB %db_id Parameter %blocking indicates whether to retry in case of failure """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] return client.delete(key) @blockable - def delete_all_by_pattern(self, db_name, pattern): + def delete_all_by_pattern(self, db_id, pattern): """ - Delete all keys which match %pattern from DB %db_name + Delete all keys which match %pattern from DB %db_id Parameter %blocking indicates whether to retry in case of failure """ - client = self.redis_clients[db_name] + client = self.redis_clients[db_id] keys = client.keys(pattern) for key in keys: client.delete(key) - def _unavailable_data_handler(self, db_name, data): + def _unavailable_data_handler(self, db_id, data): """ When the queried config is not available in Redis--wait until it is available. Two timeouts are at work here: @@ -363,23 +342,23 @@ def _unavailable_data_handler(self, db_name, data): 2. Max data wait - swsssdk-specific. how long to wait for the data to populate (in absolute time) """ start = time.time() - logger.debug("Listening on pubsub channel '{}'".format(db_name)) + logger.debug("Listening on pubsub channel '{}'".format(db_id)) while time.time() - start < self.PUB_SUB_MAXIMUM_DATA_WAIT: - msg = self.keyspace_notification_channels[db_name].get_message(timeout=self.PUB_SUB_NOTIFICATION_TIMEOUT) + msg = self.keyspace_notification_channels[db_id].get_message(timeout=self.PUB_SUB_NOTIFICATION_TIMEOUT) if msg is not None and msg.get('data') == data: - logger.info("'{}' acquired via pub-sub. Unblocking...".format(data, db_name)) + logger.info("'{}' acquired via pub-sub. Unblocking...".format(data, db_id)) # Wait for a "settling" period before releasing the wait. time.sleep(self.DATA_RETRIEVAL_WAIT_TIME) return True - logger.warning("No notification for '{}' from '{}' received before timeout.".format(data, db_name)) + logger.warning("No notification for '{}' from '{}' received before timeout.".format(data, db_id)) return False - def _connection_error_handler(self, db_name): + def _connection_error_handler(self, db_id): """ In the event Redis is unavailable, close existing connections, and try again. """ logger.warning('Could not connect to Redis--waiting before trying again.') - self.close(db_name) + self.close(db_id) time.sleep(self.CONNECT_RETRY_WAIT_TIME) - self.connect(db_name, True) + self.connect(db_id, True) diff --git a/test/test_moduleLoad.py b/test/test_moduleLoad.py index 870680f2e3da..0e81b15c0123 100644 --- a/test/test_moduleLoad.py +++ b/test/test_moduleLoad.py @@ -7,13 +7,9 @@ from unittest import TestCase -class Test_load_connector_map(TestCase): - def test__load_connector_map(self): - # noinspection PyUnresolvedReferences - import swsssdk - +class Test_load_sonic_db_config(TestCase): def test__db_map_attributes(self): import swsssdk - db2 = swsssdk.SonicV2Connector() - self.assertTrue(all(hasattr(db2, db_name) for db_name in db2.db_map)) + db = swsssdk.SonicV2Connector() + self.assertTrue(all(hasattr(db, db_name) for db_name in db.get_db_list())) pass