Skip to content

Commit

Permalink
Fix bug: ConfigDBConnector.get_table does not work in python3 (sonic-…
Browse files Browse the repository at this point in the history
…net#92)

`ConfigDBConnector` is not working well under python3, for example:
```
Oct 29 21:56:58.496505 sonic INFO hostcfgd[20662]: Traceback (most recent call last):
Oct 29 21:56:58.496724 sonic INFO hostcfgd[20662]:   File "/usr/bin/hostcfgd", line 387, in <module>
Oct 29 21:56:58.496873 sonic INFO hostcfgd[20662]:     main()
Oct 29 21:56:58.497014 sonic INFO hostcfgd[20662]:   File "/usr/bin/hostcfgd", line 382, in main
Oct 29 21:56:58.497147 sonic INFO hostcfgd[20662]:     daemon = HostConfigDaemon()
Oct 29 21:56:58.497939 sonic INFO hostcfgd[20662]:   File "/usr/bin/hostcfgd", line 237, in __init__
Oct 29 21:56:58.498103 sonic INFO hostcfgd[20662]:     tacacs_server = self.config_db.get_table('TACPLUS_SERVER')
Oct 29 21:56:58.498236 sonic INFO hostcfgd[20662]:   File "/usr/local/lib/python3.7/dist-packages/swsssdk/configdb.py", line 269, in get_table
Oct 29 21:56:58.498388 sonic INFO hostcfgd[20662]:     entry = self.raw_to_typed(client.hgetall(key))
Oct 29 21:56:58.498523 sonic INFO hostcfgd[20662]:   File "/usr/local/lib/python3.7/dist-packages/swsssdk/configdb.py", line 123, in raw_to_typed
Oct 29 21:56:58.498649 sonic INFO hostcfgd[20662]:     key = raw_key.decode()
Oct 29 21:56:58.498779 sonic INFO hostcfgd[20662]: AttributeError: 'str' object has no attribute 'decode'
```

So I will force parent class `SonicV2Connector` to handle all the `encode` / `decode` by specify `decode_responses=True` in its constructor parameters. But the class `ConfigDBConnector` will refuse contructed by `decode_responses=False`, which is okay to existing code base.

Tested in DUT:
```
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from swsssdk import ConfigDBConnector
>>> db = ConfigDBConnector()
>>> db.dbintf.redis_kwargs
{'host': '127.0.0.1', 'decode_responses': True}
>>> db.connect()
>>> db.get_table('TACPLUS_SERVER')
{'10.10.10.10': {'priority': '1', 'tcp_port': '49'}}
```
  • Loading branch information
qiluo-msft committed Oct 30, 2020
1 parent 748c404 commit 1ea30d2
Showing 1 changed file with 16 additions and 29 deletions.
45 changes: 16 additions & 29 deletions src/swsssdk/configdb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
SONiC ConfigDB connection module
SONiC ConfigDB connection module
Example:
# Write to config DB
Expand Down Expand Up @@ -29,11 +29,16 @@ class ConfigDBConnector(SonicV2Connector):
TABLE_NAME_SEPARATOR = '|'
KEY_SEPARATOR = '|'

def __init__(self, **kwargs):
def __init__(self, decode_responses=True, **kwargs):
# By default, connect to Redis through TCP, which does not requires root.
if len(kwargs) == 0:
kwargs['host'] = '127.0.0.1'

if PY3K:
if not decode_responses:
raise NotImplementedError('ConfigDBConnector with decode_responses=False is not supported in python3')
kwargs['decode_responses'] = True

"""The ConfigDBConnector class will accept the parameter 'namespace' which is used to
load the database_config and connect to the redis DB instances in that namespace.
By default namespace is set to None, which means it connects to local redis DB instances.
Expand Down Expand Up @@ -88,11 +93,11 @@ def unsubscribe(self, table):
Args:
table: Table name.
"""
if self.handlers.has_key(table):
if table in self.handlers:
self.handlers.pop(table)

def __fire(self, table, key, data):
if self.handlers.has_key(table):
if table in self.handlers:
handler = self.handlers[table]
handler(table, key, data)

Expand All @@ -106,7 +111,7 @@ def listen(self):
key = item['channel'].split(':', 1)[1]
try:
(table, row) = key.split(self.TABLE_NAME_SEPARATOR, 1)
if self.handlers.has_key(table):
if table in self.handlers:
client = self.get_redis_client(self.db_name)
data = self.raw_to_typed(client.hgetall(key))
self.__fire(table, row, data)
Expand All @@ -119,26 +124,17 @@ def raw_to_typed(self, raw_data):
typed_data = {}
for raw_key in raw_data:
key = raw_key
if PY3K:
key = raw_key.decode()

# "NULL:NULL" is used as a placeholder for objects with no attributes
if key == "NULL":
pass
# A column key with ending '@' is used to mark list-typed table items
# TODO: Replace this with a schema-based typing mechanism.
elif key.endswith("@"):
value = ""
if PY3K:
value = raw_data[raw_key].decode("utf-8").split(',')
else:
value = raw_data[raw_key].split(',')
value = raw_data[raw_key].split(',')
typed_data[key[:-1]] = value
else:
if PY3K:
typed_data[key] = raw_data[raw_key].decode()
else:
typed_data[key] = raw_data[raw_key]
typed_data[key] = raw_data[raw_key]
return typed_data

def typed_to_raw(self, typed_data):
Expand Down Expand Up @@ -188,7 +184,7 @@ def set_entry(self, table, key, data):
else:
original = self.get_entry(table, key)
client.hmset(_hash, self.typed_to_raw(data))
for k in [ k for k in original.keys() if k not in data.keys() ]:
for k in [ k for k in original if k not in data ]:
if type(original[k]) == list:
k = k + '@'
client.hdel(_hash, self.serialize_key(k))
Expand Down Expand Up @@ -239,8 +235,6 @@ def get_keys(self, table, split=True):
data = []
for key in keys:
try:
if PY3K:
key = key.decode()
if split:
(_, row) = key.split(self.TABLE_NAME_SEPARATOR, 1)
data.append(self.deserialize_key(row))
Expand All @@ -267,14 +261,9 @@ def get_table(self, table):
for key in keys:
try:
entry = self.raw_to_typed(client.hgetall(key))
if entry != None:
if PY3K:
key = key.decode()
(_, row) = key.split(self.TABLE_NAME_SEPARATOR, 1)
data[self.deserialize_key(row)] = entry
else:
(_, row) = key.split(self.TABLE_NAME_SEPARATOR, 1)
data[self.deserialize_key(row)] = entry
if entry is not None:
(_, row) = key.split(self.TABLE_NAME_SEPARATOR, 1)
data[self.deserialize_key(row)] = entry
except ValueError:
pass #Ignore non table-formated redis entries
return data
Expand Down Expand Up @@ -324,8 +313,6 @@ def get_config(self):
keys = client.keys('*')
data = {}
for key in keys:
if PY3K:
key = key.decode()
try:
(table_name, row) = key.split(self.TABLE_NAME_SEPARATOR, 1)
entry = self.raw_to_typed(client.hgetall(key))
Expand Down

0 comments on commit 1ea30d2

Please sign in to comment.