Skip to content

Commit 4d0dc7a

Browse files
liuh-80yxieca
authored andcommitted
[sonic-cli] Fix sonic-db-cli output format not backward compatible with python version issue. (#631)
#### Why I did it Fix sonic-db-cli output format not backward compatible with python version issue. Which break some E2E test. #### How I did it Re-write redis reply format method, keep same format with redis-py. #### How to verify it Add c++ unit test to cover all code. Pass all E2E test scenario. #### Which release branch to backport (provide reason below if selected) <!-- - Note we only backport fixes to a release branch, *not* features! - Please also provide a reason for the backporting below. - e.g. - [x] 202006 --> - [ ] 201811 - [ ] 201911 - [ ] 202006 - [ ] 202012 - [ ] 202106 - [ ] 202111 #### Description for the changelog Re-write sonic-cli with c++ for sonic startup performance issue #### Link to config_db schema for YANG module changes <!-- Provide a link to config_db schema for the table for which YANG model is defined Link should point to correct section on https://github.com/Azure/SONiC/wiki/Configuration. --> #### A picture of a cute animal (not mandatory but encouraged)
1 parent c5573fe commit 4d0dc7a

File tree

6 files changed

+446
-63
lines changed

6 files changed

+446
-63
lines changed

common/redisreply.cpp

+210-16
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,65 @@
1+
#include <set>
12
#include <string.h>
23
#include <stdint.h>
34
#include <vector>
45
#include <iostream>
56
#include <sstream>
67
#include <system_error>
78
#include <functional>
9+
#include <boost/algorithm/string.hpp>
810

911
#include "common/logger.h"
1012
#include "common/redisreply.h"
1113
#include "common/dbconnector.h"
1214
#include "common/rediscommand.h"
15+
#include "common/stringutility.h"
1316

1417
using namespace std;
18+
using namespace boost;
1519

1620
namespace swss {
1721

22+
static set<string> g_intToBoolCommands = {
23+
"COPY",
24+
"EXPIRE",
25+
"EXPIREAT",
26+
"PEXPIRE",
27+
"PEXPIREAT",
28+
"HEXISTS",
29+
"MOVE",
30+
"MSETNX",
31+
"PERSIST",
32+
"RENAMENX",
33+
"SISMEMBER",
34+
"SMOVE",
35+
"SETNX"
36+
};
37+
38+
static set<string> g_strToBoolCommands = {
39+
"AUTH",
40+
"HMSET",
41+
"PSETEX",
42+
"SETEX",
43+
"FLUSHALL",
44+
"FLUSHDB",
45+
"LSET",
46+
"LTRIM",
47+
"MSET",
48+
"PFMERGE",
49+
"ASKING",
50+
"READONLY",
51+
"READWRITE",
52+
"RENAME",
53+
"SAVE",
54+
"SELECT",
55+
"SHUTDOWN",
56+
"SLAVEOF",
57+
"SWAPDB",
58+
"WATCH",
59+
"UNWATCH",
60+
"SET"
61+
};
62+
1863
template <typename FUNC>
1964
inline void guard(FUNC func, const char* command)
2065
{
@@ -226,38 +271,35 @@ template<> RedisMessage RedisReply::getReply<RedisMessage>()
226271
return ret;
227272
}
228273

229-
230274
string RedisReply::to_string()
231275
{
232-
return to_string(getContext());
276+
return RedisReply::to_string(this->getContext());
233277
}
234278

235-
string RedisReply::to_string(redisReply *reply)
279+
string RedisReply::to_string(redisReply *reply, string command)
236280
{
281+
/*
282+
Response format need keep as same as redis-py, redis-py using a command to result type mapping to convert result:
283+
https://github.com/redis/redis-py/blob/bedf3c82a55b4b67eed93f686cb17e82f7ab19cd/redis/client.py#L682
284+
Redis command result type can be found here:
285+
https://redis.io/commands/
286+
Only commands used by scripts in sonic repos are supported by these method.
287+
For example: 'Info' command not used by any sonic scripts, so the output format will different with redis-py.
288+
*/
237289
switch(reply->type)
238290
{
239291
case REDIS_REPLY_INTEGER:
240-
return std::to_string(reply->integer);
292+
return formatReply(command, reply->integer);
241293

242294
case REDIS_REPLY_STRING:
243295
case REDIS_REPLY_ERROR:
244296
case REDIS_REPLY_STATUS:
245297
case REDIS_REPLY_NIL:
246-
return string(reply->str, reply->len);
298+
return formatReply(command, reply->str, reply->len);
247299

248300
case REDIS_REPLY_ARRAY:
249301
{
250-
stringstream result;
251-
for (size_t i = 0; i < reply->elements; i++)
252-
{
253-
result << to_string(reply->element[i]);
254-
255-
if (i < reply->elements - 1)
256-
{
257-
result << endl;
258-
}
259-
}
260-
return result.str();
302+
return formatReply(command, reply->element, reply->elements);
261303
}
262304

263305
default:
@@ -266,4 +308,156 @@ string RedisReply::to_string(redisReply *reply)
266308
}
267309
}
268310

311+
string RedisReply::formatReply(string command, long long integer)
312+
{
313+
if (g_intToBoolCommands.find(command) != g_intToBoolCommands.end())
314+
{
315+
if (integer == 1)
316+
{
317+
return string("True");
318+
}
319+
else if (integer == 0)
320+
{
321+
return string("False");
322+
}
323+
}
324+
else if (command == "AUTH")
325+
{
326+
if (integer != 0)
327+
{
328+
return string("OK");
329+
}
330+
}
331+
332+
return std::to_string(integer);
333+
}
334+
335+
string RedisReply::formatReply(string command, const char* str, size_t len)
336+
{
337+
string result = string(str, len);
338+
if (g_strToBoolCommands.find(command) != g_strToBoolCommands.end()
339+
&& result == "OK")
340+
{
341+
return string("True");
342+
}
343+
344+
return result;
345+
}
346+
347+
string RedisReply::formatReply(string command, struct redisReply **element, size_t elements)
348+
{
349+
if (command == "HGETALL")
350+
{
351+
return formatDictReply(element, elements);
352+
}
353+
else if(command == "SCAN"
354+
|| command == "SSCAN")
355+
{
356+
return formatSscanReply(element, elements);
357+
}
358+
else if(command == "HSCAN")
359+
{
360+
return formatHscanReply(element, elements);
361+
}
362+
else if(command == "BLPOP"
363+
|| command == "BRPOP")
364+
{
365+
return formatTupleReply(element, elements);
366+
}
367+
else
368+
{
369+
return formatListReply(element, elements);
370+
}
371+
}
372+
373+
string RedisReply::formatSscanReply(struct redisReply **element, size_t elements)
374+
{
375+
if (elements != 2)
376+
{
377+
throw system_error(make_error_code(errc::io_error),
378+
"Invalid result");
379+
}
380+
381+
// format HSCAN result, here is a example:
382+
// (0, {'test3': 'test3', 'test2': 'test2'})
383+
ostringstream result;
384+
result << "(" << element[0]->integer << ", ";
385+
// format the field mapping part
386+
result << formatArrayReply(element[1]->element, element[1]->elements);
387+
result << ")";
388+
389+
return result.str();
390+
}
391+
392+
string RedisReply::formatHscanReply(struct redisReply **element, size_t elements)
393+
{
394+
if (elements != 2)
395+
{
396+
throw system_error(make_error_code(errc::io_error),
397+
"Invalid result");
398+
}
399+
400+
// format HSCAN result, here is a example:
401+
// (0, {'test3': 'test3', 'test2': 'test2'})
402+
ostringstream result;
403+
result << "(" << element[0]->integer << ", ";
404+
// format the field mapping part
405+
result << formatDictReply(element[1]->element, element[1]->elements);
406+
result << ")";
407+
408+
return result.str();
409+
}
410+
411+
string RedisReply::formatDictReply(struct redisReply **element, size_t elements)
412+
{
413+
if (elements%2 != 0)
414+
{
415+
throw system_error(make_error_code(errc::io_error),
416+
"Invalid result");
417+
}
418+
419+
// format dictionary, not using json.h because the output format are different, here is a example:
420+
// {'test3': 'test3', 'test2': 'test2'}
421+
vector<string> elementvector;
422+
for (unsigned int i = 0; i < elements; i += 2)
423+
{
424+
elementvector.push_back("'" + to_string(element[i]) + "': '" + to_string(element[i+1]) + "'");
425+
}
426+
427+
return swss::join(", ", '{', '}', elementvector.begin(), elementvector.end());
428+
}
429+
430+
string RedisReply::formatArrayReply(struct redisReply **element, size_t elements)
431+
{
432+
vector<string> elementvector;
433+
for (unsigned int i = 0; i < elements; i++)
434+
{
435+
elementvector.push_back("'" + to_string(element[i]) + "'");
436+
}
437+
438+
return swss::join(", ", '[', ']', elementvector.begin(), elementvector.end());
439+
}
440+
441+
string RedisReply::formatListReply(struct redisReply **element, size_t elements)
442+
{
443+
vector<string> elementvector;
444+
for (unsigned int i = 0; i < elements; i++)
445+
{
446+
elementvector.push_back(to_string(element[i]));
447+
}
448+
449+
return swss::join("\n", elementvector.begin(), elementvector.end());
450+
}
451+
452+
string RedisReply::formatTupleReply(struct redisReply **element, size_t elements)
453+
{
454+
vector<string> elementvector;
455+
for (unsigned int i = 0; i < elements; i++)
456+
{
457+
elementvector.push_back("'" + to_string(element[i]) + "'");
458+
}
459+
460+
return swss::join(", ", '(', ')', elementvector.begin(), elementvector.end());
461+
}
462+
269463
}

common/redisreply.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,22 @@ class RedisReply
9494

9595
std::string to_string();
9696

97-
static std::string to_string(redisReply *reply);
97+
static std::string to_string(redisReply *reply, std::string command = std::string());
9898

9999
private:
100100
void checkStatus(const char *status);
101101
void checkReply();
102102

103+
static std::string formatReply(std::string command, struct redisReply **element, size_t elements);
104+
static std::string formatReply(std::string command, long long integer);
105+
static std::string formatReply(std::string command, const char* str, size_t len);
106+
static std::string formatSscanReply(struct redisReply **element, size_t elements);
107+
static std::string formatHscanReply(struct redisReply **element, size_t elements);
108+
static std::string formatDictReply(struct redisReply **element, size_t elements);
109+
static std::string formatArrayReply(struct redisReply **element, size_t elements);
110+
static std::string formatListReply(struct redisReply **element, size_t elements);
111+
static std::string formatTupleReply(struct redisReply **element, size_t elements);
112+
103113
redisReply *m_reply;
104114
};
105115

common/stringutility.h

+21-10
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,21 @@ void lexical_convert(const std::vector<std::string> &strs, T &t, Args &... args)
7070
namespace join_detail
7171
{
7272

73-
template <typename T>
74-
void join(std::ostringstream &ostream, char, const T &t)
73+
template <typename D, typename T>
74+
void join(std::ostringstream &ostream, D, const T &t)
7575
{
7676
ostream << t;
7777
}
7878

79-
template <typename T, typename... Args>
80-
void join(std::ostringstream &ostream, char delimiter, const T &t, const Args &... args)
79+
template <typename D, typename T, typename... Args>
80+
void join(std::ostringstream &ostream, const D &delimiter, const T &t, const Args &... args)
8181
{
8282
ostream << t << delimiter;
8383
join(ostream, delimiter, args...);
8484
}
8585

86-
template <typename Iterator>
87-
void join(std::ostringstream &ostream, char delimiter, Iterator begin, Iterator end)
86+
template <typename D, typename Iterator>
87+
void join(std::ostringstream &ostream, const D &delimiter, Iterator begin, Iterator end)
8888
{
8989
if (begin == end)
9090
{
@@ -99,22 +99,33 @@ namespace join_detail
9999
}
100100
}
101101

102-
template <typename T, typename... Args>
103-
static inline std::string join(char delimiter, const T &t, const Args &... args)
102+
template <typename D, typename T, typename... Args>
103+
static inline std::string join(const D &delimiter, const T &t, const Args &... args)
104104
{
105105
std::ostringstream ostream;
106106
join_detail::join(ostream, delimiter, t, args...);
107107
return ostream.str();
108108
}
109109

110-
template <typename Iterator>
111-
static inline std::string join(char delimiter, Iterator begin, Iterator end)
110+
template <typename D, typename Iterator>
111+
static inline std::string join(const D &delimiter, Iterator begin, Iterator end)
112112
{
113113
std::ostringstream ostream;
114114
join_detail::join(ostream, delimiter, begin, end);
115115
return ostream.str();
116116
}
117117

118+
template <typename D, typename Iterator>
119+
static inline std::string join(const D &delimiter, char beginsym, char endsym, Iterator begin, Iterator end)
120+
{
121+
std::ostringstream ostream;
122+
ostream << beginsym;
123+
join_detail::join(ostream, delimiter, begin, end);
124+
ostream << endsym;
125+
return ostream.str();
126+
}
127+
128+
118129
static inline bool hex_to_binary(const std::string &hex_str, std::uint8_t *buffer, size_t buffer_length)
119130
{
120131
if (hex_str.length() != (buffer_length * 2))

0 commit comments

Comments
 (0)