Skip to content

Commit 01f17ba

Browse files
JunhongMaoStormLiangMS
authored andcommitted
[VOQ][saidump] Enhance saidump with new option -r to parser the JSON file and displays/format the right output (#1288)
Why I did it Fix issue: sonic-net/sonic-buildimage#13561 The existing saidump use https://github.com/sonic-net/sonic-swss-common/blob/master/common/table_dump.lua script which loops the ASIC_DB more than 5 seconds and blocks other processes access. This solution uses the redis-db SAVE option to save the snapshot of DB each time and recover later, instead of looping through each entry in the table. Related PRs: sonic-net/sonic-utilities#2972 sonic-net/sonic-buildimage#16466
1 parent 715337e commit 01f17ba

File tree

1 file changed

+209
-5
lines changed

1 file changed

+209
-5
lines changed

saidump/saidump.cpp

Lines changed: 209 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
#include <string>
33
#include <set>
44
#include <sstream>
5+
#include <iostream>
6+
#include <fstream>
7+
#include <regex>
8+
#include <climits>
59

610
extern "C" {
711
#include <sai.h>
@@ -10,32 +14,44 @@ extern "C" {
1014
#include "swss/table.h"
1115
#include "meta/sai_serialize.h"
1216
#include "sairediscommon.h"
17+
#include <nlohmann/json.hpp>
1318

1419
#include <getopt.h>
1520

1621
// TODO split to multiple cpp
1722

1823
using namespace swss;
24+
using json = nlohmann::json;
25+
26+
// Default value: 100 MB
27+
constexpr int64_t RDB_JSON_MAX_SIZE = 1024 * 1024 * 100;
1928

2029
struct CmdOptions
2130
{
2231
bool skipAttributes;
2332
bool dumpTempView;
2433
bool dumpGraph;
34+
std::string rdbJsonFile;
35+
uint64_t rdbJSonSizeLimit;
2536
};
2637

27-
CmdOptions g_cmdOptions;
28-
std::map<sai_object_id_t, const TableMap*> g_oid_map;
38+
39+
static CmdOptions g_cmdOptions;
40+
static std::map<sai_object_id_t, const TableMap*> g_oid_map;
2941

3042
void printUsage()
3143
{
3244
SWSS_LOG_ENTER();
3345

34-
std::cout << "Usage: saidump [-t] [-g] [-h]" << std::endl;
46+
std::cout << "Usage: saidump [-t] [-g] [-r] [-m] [-h]" << std::endl;
3547
std::cout << " -t --tempView:" << std::endl;
3648
std::cout << " Dump temp view" << std::endl;
3749
std::cout << " -g --dumpGraph:" << std::endl;
3850
std::cout << " Dump current graph" << std::endl;
51+
std::cout << " -r --rdb:" << std::endl;
52+
std::cout << " Dump by parsing the RDB JSON file, which is created by rdbtools based on Redis dump.rdb that is generated by Redis SAVE command" << std::endl;
53+
std::cout << " -m --max:" << std::endl;
54+
std::cout << " Config the the RDB JSON file's max size in MB, which is optional with default value 100MB" << std::endl;
3955
std::cout << " -h --help:" << std::endl;
4056
std::cout << " Print out this message" << std::endl;
4157
}
@@ -48,15 +64,19 @@ CmdOptions handleCmdLine(int argc, char **argv)
4864

4965
options.dumpTempView = false;
5066
options.dumpGraph = false;
67+
options.rdbJSonSizeLimit = RDB_JSON_MAX_SIZE;
5168

52-
const char* const optstring = "gth";
69+
const char* const optstring = "gtr:m:h";
70+
uint64_t result = 0;
5371

5472
while (true)
5573
{
5674
static struct option long_options[] =
5775
{
5876
{ "dumpGraph", no_argument, 0, 'g' },
5977
{ "tempView", no_argument, 0, 't' },
78+
{ "rdb", required_argument, 0, 'r' },
79+
{ "max", required_argument, 0, 'm' },
6080
{ "help", no_argument, 0, 'h' },
6181
{ 0, 0, 0, 0 }
6282
};
@@ -82,6 +102,33 @@ CmdOptions handleCmdLine(int argc, char **argv)
82102
options.dumpTempView = true;
83103
break;
84104

105+
case 'r':
106+
SWSS_LOG_NOTICE("Dumping from %s", optarg);
107+
options.rdbJsonFile = std::string(optarg);
108+
break;
109+
110+
case 'm':
111+
if(!regex_match(optarg, std::regex(R"([+]?\d+)"))) //only positive numeric chars are valid, such as 3984, +3232, etc.
112+
{
113+
SWSS_LOG_WARN("invalid option -m %s", optarg);
114+
printUsage();
115+
exit(EXIT_SUCCESS);
116+
}
117+
118+
result = strtoull(optarg, NULL, 0);
119+
120+
if((errno == ERANGE && result == ULLONG_MAX) || result == 0 || result >= INT_MAX)
121+
{
122+
SWSS_LOG_WARN("invalid option -m %s", optarg);
123+
printUsage();
124+
exit(EXIT_SUCCESS);
125+
}
126+
127+
options.rdbJSonSizeLimit = result * 1024 * 1024;
128+
SWSS_LOG_NOTICE("Configure the RDB JSON MAX size to %llu MB", options.rdbJSonSizeLimit / 1024 / 1024);
129+
130+
break;
131+
85132
case 'h':
86133
printUsage();
87134
exit(EXIT_SUCCESS);
@@ -399,7 +446,153 @@ void dumpGraph(const TableDump& td)
399446
std::cout << "}" << std::endl;
400447
}
401448

402-
int main(int argc, char ** argv)
449+
#define SWSS_LOG_ERROR_AND_STDERR(format, ...) { fprintf(stderr, format"\n", ##__VA_ARGS__); SWSS_LOG_ERROR(format, ##__VA_ARGS__); }
450+
451+
/**
452+
* @brief Process the input JSON file to make sure it's a valid JSON file for the JSON library.
453+
*/
454+
static sai_status_t preProcessFile(const std::string file_name)
455+
{
456+
SWSS_LOG_ENTER();
457+
458+
std::ifstream input_file(file_name);
459+
460+
if (!input_file.is_open())
461+
{
462+
SWSS_LOG_ERROR_AND_STDERR("Failed to open the input file %s.", file_name.c_str());
463+
return SAI_STATUS_FAILURE;
464+
}
465+
466+
input_file.seekg(0, std::ios::end); // Move to the end of the file
467+
uint64_t file_size = input_file.tellg(); // Get the current position
468+
SWSS_LOG_NOTICE("Get %s's size %" PRIu64 " Bytes, limit: %" PRIu64 " MB.", file_name.c_str(), file_size, g_cmdOptions.rdbJSonSizeLimit / 1024 / 1024);
469+
470+
if (file_size >= g_cmdOptions.rdbJSonSizeLimit)
471+
{
472+
SWSS_LOG_ERROR_AND_STDERR("Get %s's size failure or its size %" PRIu64 " >= %" PRIu64 " MB.", file_name.c_str(), file_size, g_cmdOptions.rdbJSonSizeLimit / 1024 / 1024);
473+
return SAI_STATUS_FAILURE;
474+
}
475+
476+
input_file.seekg(0); // Move to the begin of the file
477+
478+
// Read the content of the input file into a string
479+
std::string content((std::istreambuf_iterator<char>(input_file)),
480+
std::istreambuf_iterator<char>());
481+
482+
content = regex_replace(content, std::regex("\\},\\{\\r"), ",");
483+
484+
std::ofstream outputFile(file_name);
485+
486+
if (!outputFile.is_open())
487+
{
488+
SWSS_LOG_ERROR_AND_STDERR("Failed to open the output file %s.", file_name.c_str());
489+
return SAI_STATUS_FAILURE;
490+
}
491+
492+
//Remove the 1st and last char to make sure its format is same as previous output
493+
if (content.size() >= 2 && content[0] == '[' && content[content.length()-1] == ']')
494+
{
495+
outputFile << content.substr(1, content.size()-2);
496+
}
497+
else
498+
{
499+
outputFile << content;
500+
}
501+
502+
return SAI_STATUS_SUCCESS;
503+
}
504+
505+
static sai_status_t dumpFromRedisRdbJson(const std::string file_name)
506+
{
507+
SWSS_LOG_ENTER();
508+
509+
std::ifstream input_file(file_name);
510+
511+
if (!input_file.is_open())
512+
{
513+
SWSS_LOG_ERROR_AND_STDERR("The file %s does not exist for dumping from Redis RDB JSON file.", file_name.c_str());
514+
return SAI_STATUS_FAILURE;
515+
}
516+
517+
try
518+
{
519+
// Parse the JSON data from the file (validation)
520+
nlohmann::json jsonData;
521+
input_file >> jsonData;
522+
523+
SWSS_LOG_DEBUG("JSON file is valid.");
524+
525+
for (json::iterator it = jsonData.begin(); it != jsonData.end(); ++it)
526+
{
527+
json jj_key = it.key();
528+
529+
std::string keystr = jj_key;
530+
std::string item_name = keystr;
531+
size_t pos = keystr.find_first_of(":");
532+
533+
if (pos != std::string::npos)
534+
{
535+
if(ASIC_STATE_TABLE != keystr.substr(0, pos)) // filter out non ASIC_STATE
536+
{
537+
continue;
538+
}
539+
540+
item_name = keystr.substr(pos + 1);
541+
542+
if (item_name.find(":") != std::string::npos)
543+
{
544+
item_name.replace(item_name.find_first_of(":"), 1, " ");
545+
}
546+
}
547+
else
548+
{
549+
continue;
550+
}
551+
552+
std::cout << item_name << " " << std::endl;
553+
554+
json jj = it.value();
555+
556+
if (!it->is_object())
557+
{
558+
continue;
559+
}
560+
561+
TableMap map;
562+
563+
for (json::iterator itt = jj.begin(); itt != jj.end(); ++itt)
564+
{
565+
if (itt.key() != "NULL")
566+
{
567+
map[itt.key()] = itt.value();
568+
}
569+
}
570+
571+
constexpr size_t LINE_IDENT = 4;
572+
size_t max_len = get_max_attr_len(map);
573+
std::string str_indent = pad_string("", LINE_IDENT);
574+
575+
for (const auto&field: map)
576+
{
577+
std::cout << str_indent << pad_string(field.first, max_len) << " : ";
578+
std::cout << field.second << std::endl;
579+
}
580+
581+
std::cout << std::endl;
582+
}
583+
584+
return SAI_STATUS_SUCCESS;
585+
}
586+
catch (std::exception &ex)
587+
{
588+
SWSS_LOG_ERROR_AND_STDERR("JSON file %s is invalid.", file_name.c_str());
589+
SWSS_LOG_ERROR_AND_STDERR("JSON parsing error: %s.", ex.what());
590+
}
591+
592+
return SAI_STATUS_FAILURE;
593+
}
594+
595+
int main(int argc, char **argv)
403596
{
404597
swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG);
405598

@@ -411,6 +604,17 @@ int main(int argc, char ** argv)
411604

412605
g_cmdOptions = handleCmdLine(argc, argv);
413606

607+
608+
if (g_cmdOptions.rdbJsonFile.size() > 0)
609+
{
610+
if (SAI_STATUS_FAILURE == preProcessFile(g_cmdOptions.rdbJsonFile))
611+
{
612+
return EXIT_FAILURE;
613+
}
614+
615+
return dumpFromRedisRdbJson(g_cmdOptions.rdbJsonFile);
616+
}
617+
414618
swss::DBConnector db("ASIC_DB", 0);
415619

416620
std::string table = ASIC_STATE_TABLE;

0 commit comments

Comments
 (0)