Skip to content

Commit 03748ce

Browse files
[MultiDB] add sonic-db-cli SAVE/FLUSHALL option (#69)
* [MultiDB] add sonic-db-cli SAVE/FLUSHALL option * address reviews and add exception handling * exception output to stderr * Update src/swsssdk/scripts/sonic-db-cli
1 parent f2a7598 commit 03748ce

File tree

1 file changed

+47
-40
lines changed

1 file changed

+47
-40
lines changed

src/swsssdk/scripts/sonic-db-cli

+47-40
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,54 @@ import swsssdk
55
import redis
66
import argparse
77
from multiprocessing import Pool
8+
from functools import partial
89

9-
def ping_unix_path_single_instance(inst_info):
10+
def handle_single_instance(op, use_unix_socket, inst_info):
1011
inst_hostname = inst_info['hostname']
11-
inst_unix_socket_path = inst_info['unix_socket_path']
12-
r = redis.Redis(host=inst_hostname, unix_socket_path=inst_unix_socket_path)
13-
rsp = False
14-
msg = 'Could not connect to Redis at {}:{}: Connection refused'.format(inst_hostname, inst_unix_socket_path)
15-
try:
16-
rsp = r.ping()
17-
except redis.exceptions.ConnectionError as e:
18-
pass
19-
return 'PONG' if rsp else msg
20-
21-
def ping_tcp_single_instance(inst_info):
22-
inst_hostname = inst_info['hostname']
23-
inst_port = inst_info['port']
24-
r = redis.Redis(host=inst_hostname, port=inst_port)
12+
if use_unix_socket:
13+
inst_unix_socket_path = inst_info['unix_socket_path']
14+
r = redis.Redis(host=inst_hostname, unix_socket_path=inst_unix_socket_path)
15+
else:
16+
inst_port = inst_info['port']
17+
r = redis.Redis(host=inst_hostname, port=inst_port)
2518
rsp = False
26-
msg = 'Could not connect to Redis at {}:{}: Connection refused'.format(inst_hostname, inst_port)
19+
msg = 'Could not connect to Redis at {}:{}: Connection refused'.format(inst_hostname,
20+
inst_unix_socket_path if use_unix_socket else inst_port)
2721
try:
28-
rsp = r.ping()
22+
if op == 'PING':
23+
rsp = r.ping()
24+
elif op == 'SAVE':
25+
rsp = r.save()
26+
elif op == 'FLUSHALL':
27+
rsp = r.flushall()
28+
else:
29+
raise ValueError("Operation {} is not supported".format(op))
2930
except redis.exceptions.ConnectionError as e:
3031
pass
31-
return 'PONG' if rsp else msg
32+
return None if rsp else msg
3233

33-
def ping_all_instances(namespace, use_unix_socket=False):
34-
ping_single_instance = ping_unix_path_single_instance
34+
def handle_all_instances(namespace, op, use_unix_socket=False):
3535
# Use the tcp connectivity if namespace is local and unixsocket cmd_option is present.
36-
if namespace is None:
37-
if not use_unix_socket:
38-
ping_single_instance = ping_tcp_single_instance
36+
if namespace is not None:
37+
use_unix_socket = True
3938

4039
db_insts = swsssdk.SonicDBConfig.get_instancelist(namespace)
41-
# ping all redis instances together
42-
# TODO: if one of the ping failed, it could fail quickly and not necessary to wait all other pings
40+
# Operate All Redis Instances in Parallel
41+
# TODO: if one of the operations failed, it could fail quickly and not necessary to wait all other operations
4342
p = Pool(len(db_insts))
44-
rsps = p.map(ping_single_instance, [v for k, v in db_insts.items()])
45-
msg = []
46-
for rsp in rsps:
47-
if rsp != 'PONG':
48-
msg.append(rsp)
43+
func = partial(handle_single_instance, op, use_unix_socket)
44+
rsps = p.map(func, [v for k, v in db_insts.items()])
45+
msg = [rsp for rsp in rsps if rsp]
46+
4947
if msg:
5048
print('\n'.join(msg))
5149
sys.exit(1)
52-
else:
50+
51+
if op == 'PING':
5352
print('PONG')
54-
sys.exit(0)
53+
else:
54+
print('OK')
55+
sys.exit(0)
5556

5657
def execute_cmd(dbname, cmd, namespace, use_unix_socket=False):
5758
if namespace is None:
@@ -94,12 +95,13 @@ Example 1: sonic-db-cli -n asic0 CONFIG_DB keys \*
9495
Example 2: sonic-db-cli -n asic2 APPL_DB HGETALL VLAN_TABLE:Vlan10
9596
Example 3: sonic-db-cli APPL_DB HGET VLAN_TABLE:Vlan10 mtu
9697
Example 4: sonic-db-cli -n asic3 APPL_DB EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 k1 k2 v1 v2
97-
Example 5: sonic-db-cli PING
98-
Example 6: sonic-db-cli -s PING
98+
Example 5: sonic-db-cli PING | sonic-db-cli -s PING
99+
Example 6: sonic-db-cli SAVE | sonic-db-cli -s SAVE
100+
Example 7: sonic-db-cli FLUSHALL | sonic-db-cli -s FLUSHALL
99101
""")
100102
parser.add_argument('-s', '--unixsocket', help="Override use of tcp_port and use unixsocket", action='store_true')
101103
parser.add_argument('-n', '--namespace', type=str, help="Namespace string to use asic0/asic1.../asicn", default=None)
102-
parser.add_argument('db_or_op', type=str, help='Database name Or Unary operation(only PING supported)')
104+
parser.add_argument('db_or_op', type=str, help='Database name Or Unary operation(only PING/SAVE/FLUSHALL supported)')
103105
parser.add_argument('cmd', nargs='*', type=str, help='Command to execute in database')
104106

105107
args = parser.parse_args()
@@ -110,11 +112,16 @@ Example 6: sonic-db-cli -s PING
110112
swsssdk.SonicDBConfig.load_sonic_global_db_config(namespace=args.namespace)
111113
if args.cmd:
112114
execute_cmd(args.db_or_op, args.cmd, args.namespace, args.unixsocket)
113-
elif args.db_or_op == 'PING':
114-
ping_all_instances(args.namespace, args.unixsocket)
115-
# TODO next PR will support 'SAVE' and 'FLUSHALL'
116-
# elif args.db_or_op == 'SAVE':
117-
# elif args.db_or_op == 'FLUSHALL':
115+
elif args.db_or_op in ('PING', 'SAVE', 'FLUSHALL'):
116+
# redis-cli doesn't depend on database_config.json which could raise some exceptions
117+
# sonic-db-cli catch all possible exceptions and handle it as a failure case which not return 'OK' or 'PONG'
118+
try:
119+
handle_all_instances(args.namespace, args.db_or_op, args.unixsocket)
120+
except Exception as ex:
121+
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
122+
message = template.format(type(ex).__name__, ex.args)
123+
print(message, file=sys.stderr)
124+
sys.exit(1)
118125
else:
119126
parser.print_help()
120127
else:

0 commit comments

Comments
 (0)