18
18
19
19
"""
20
20
21
+ import sys
21
22
import time
22
23
from .dbconnector import SonicV2Connector
23
24
24
25
class ConfigDBConnector (SonicV2Connector ):
25
26
26
27
INIT_INDICATOR = 'CONFIG_DB_INITIALIZED'
28
+ TABLE_NAME_SEPARATOR = '|'
29
+ KEY_SEPARATOR = '|'
27
30
28
31
def __init__ (self ):
29
32
# Connect to Redis through TCP, which does not requires root.
@@ -84,7 +87,7 @@ def listen(self):
84
87
if item ['type' ] == 'pmessage' :
85
88
key = item ['channel' ].split (':' , 1 )[1 ]
86
89
try :
87
- (table , row ) = key .split (':' , 1 )
90
+ (table , row ) = key .split (self . TABLE_NAME_SEPARATOR , 1 )
88
91
if self .handlers .has_key (table ):
89
92
client = self .redis_clients [self .CONFIG_DB ]
90
93
data = self .__raw_to_typed (client .hgetall (key ))
@@ -94,12 +97,15 @@ def listen(self):
94
97
95
98
def __raw_to_typed (self , raw_data ):
96
99
if raw_data == None :
97
- return {}
100
+ return None
98
101
typed_data = {}
99
102
for key in raw_data :
103
+ # "NULL:NULL" is used as a placeholder for objects with no attributes
104
+ if key == "NULL" :
105
+ pass
100
106
# A column key with ending '@' is used to mark list-typed table items
101
107
# TODO: Replace this with a schema-based typing mechanism.
102
- if key .endswith ("@" ):
108
+ elif key .endswith ("@" ):
103
109
typed_data [key [:- 1 ]] = raw_data [key ].split (',' )
104
110
else :
105
111
typed_data [key ] = raw_data [key ]
@@ -108,6 +114,8 @@ def __raw_to_typed(self, raw_data):
108
114
def __typed_to_raw (self , typed_data ):
109
115
if typed_data == None :
110
116
return None
117
+ elif typed_data == {}:
118
+ return { "NULL" : "NULL" }
111
119
raw_data = {}
112
120
for key in typed_data :
113
121
value = typed_data [key ]
@@ -117,28 +125,50 @@ def __typed_to_raw(self, typed_data):
117
125
raw_data [key ] = value
118
126
return raw_data
119
127
128
+ @staticmethod
129
+ def serialize_key (key ):
130
+ if type (key ) is tuple :
131
+ return ConfigDBConnector .KEY_SEPARATOR .join (key )
132
+ else :
133
+ return key
134
+
135
+ @staticmethod
136
+ def deserialize_key (key ):
137
+ tokens = key .split (ConfigDBConnector .KEY_SEPARATOR )
138
+ if len (tokens ) > 1 :
139
+ return tuple (tokens )
140
+ else :
141
+ return key
142
+
120
143
def set_entry (self , table , key , data ):
121
144
"""Write a table entry to config db.
122
145
Args:
123
146
table: Table name.
124
- key: Key of table entry.
125
- data: Table row data in a form of dictionary {'column_key': 'value', ...}
147
+ key: Key of table entry, or a tuple of keys if it is a multi-key table.
148
+ data: Table row data in a form of dictionary {'column_key': 'value', ...}.
149
+ Pass {} as data will create an entry with no column if not already existed.
150
+ Pass None as data will delete the entry.
126
151
"""
152
+ key = self .serialize_key (key )
127
153
client = self .redis_clients [self .CONFIG_DB ]
128
- _hash = '{}:{}' .format (table .upper (), key )
129
- client .hmset (_hash , self .__typed_to_raw (data ))
154
+ _hash = '{}{}{}' .format (table .upper (), self .TABLE_NAME_SEPARATOR , key )
155
+ if data == None :
156
+ client .delete (_hash )
157
+ else :
158
+ client .hmset (_hash , self .__typed_to_raw (data ))
130
159
131
160
def get_entry (self , table , key ):
132
161
"""Read a table entry from config db.
133
162
Args:
134
163
table: Table name.
135
- key: Key of table entry.
164
+ key: Key of table entry, or a tuple of keys if it is a multi-key table .
136
165
Returns:
137
166
Table row data in a form of dictionary {'column_key': 'value', ...}
138
167
Empty dictionary if table does not exist or entry does not exist.
139
168
"""
169
+ key = self .serialize_key (key )
140
170
client = self .redis_clients [self .CONFIG_DB ]
141
- _hash = '{}:{} ' .format (table .upper (), key )
171
+ _hash = '{}{}{} ' .format (table .upper (), self . TABLE_NAME_SEPARATOR , key )
142
172
return self .__raw_to_typed (client .hgetall (_hash ))
143
173
144
174
def get_table (self , table ):
@@ -147,19 +177,20 @@ def get_table(self, table):
147
177
table: Table name.
148
178
Returns:
149
179
Table data in a dictionary form of
150
- { 'row_key': {'column_key': 'value', ...}, ...}
180
+ { 'row_key': {'column_key': value, ...}, ...}
181
+ or { ('l1_key', 'l2_key', ...): {'column_key': value, ...}, ...} for a multi-key table.
151
182
Empty dictionary if table does not exist.
152
183
"""
153
184
client = self .redis_clients [self .CONFIG_DB ]
154
- pattern = '{}: *' .format (table .upper ())
185
+ pattern = '{}{} *' .format (table .upper (), self . TABLE_NAME_SEPARATOR )
155
186
keys = client .keys (pattern )
156
187
data = {}
157
188
for key in keys :
158
189
try :
159
- (_ , row ) = key .split (':' , 1 )
190
+ (_ , row ) = key .split (self . TABLE_NAME_SEPARATOR , 1 )
160
191
entry = self .__raw_to_typed (client .hgetall (key ))
161
192
if entry :
162
- data [row ] = entry
193
+ data [self . deserialize_key ( row ) ] = entry
163
194
except ValueError :
164
195
pass #Ignore non table-formated redis entries
165
196
return data
@@ -170,6 +201,7 @@ def set_config(self, data):
170
201
data: config data in a dictionary form
171
202
{
172
203
'TABLE_NAME': { 'row_key': {'column_key': 'value', ...}, ...},
204
+ 'MULTI_KEY_TABLE_NAME': { ('l1_key', 'l2_key', ...) : {'column_key': 'value', ...}, ...},
173
205
...
174
206
}
175
207
"""
@@ -184,6 +216,7 @@ def get_config(self):
184
216
Config data in a dictionary form of
185
217
{
186
218
'TABLE_NAME': { 'row_key': {'column_key': 'value', ...}, ...},
219
+ 'MULTI_KEY_TABLE_NAME': { ('l1_key', 'l2_key', ...) : {'column_key': 'value', ...}, ...},
187
220
...
188
221
}
189
222
"""
@@ -192,10 +225,10 @@ def get_config(self):
192
225
data = {}
193
226
for key in keys :
194
227
try :
195
- (table_name , row ) = key .split (':' , 1 )
228
+ (table_name , row ) = key .split (self . TABLE_NAME_SEPARATOR , 1 )
196
229
entry = self .__raw_to_typed (client .hgetall (key ))
197
- if entry :
198
- data .setdefault (table_name , {})[row ] = entry
230
+ if entry != None :
231
+ data .setdefault (table_name , {})[self . deserialize_key ( row ) ] = entry
199
232
except ValueError :
200
233
pass #Ignore non table-formated redis entries
201
234
return data
0 commit comments