diff --git a/rules/sonic-db-cli.dep b/rules/sonic-db-cli.dep new file mode 100755 index 00000000000..68f2dca68b3 --- /dev/null +++ b/rules/sonic-db-cli.dep @@ -0,0 +1,8 @@ +SPATH := $($(SONIC_DB_CLI)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/sonic-db-cli.mk rules/sonic-db-cli.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(SPATH)) + +$(BASH)_CACHE_MODE := GIT_CONTENT_SHA +$(BASH)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(BASH)_DEP_FILES := $(DEP_FILES) \ No newline at end of file diff --git a/rules/sonic-db-cli.mk b/rules/sonic-db-cli.mk new file mode 100755 index 00000000000..e08524825d5 --- /dev/null +++ b/rules/sonic-db-cli.mk @@ -0,0 +1,15 @@ +# sonic-cli Debian package +SONIC_DB_CLI_VERSION = 1.0.0 +export SONIC_DB_CLI_VERSION + +SONIC_DB_CLI = sonic-db-cli_$(SONIC_DB_CLI_VERSION)_$(CONFIGURED_ARCH).deb +$(SONIC_DB_CLI)_DEPENDS += $(LIBSWSSCOMMON_DEV) +$(SONIC_DB_CLI)_RDEPENDS += $(LIBSWSSCOMMON) +$(SONIC_DB_CLI)_SRC_PATH = $(SRC_PATH)/sonic-db-cli + +SONIC_DPKG_DEBS += $(SONIC_DB_CLI) + +# The .c, .cpp, .h & .hpp files under src/{$DBG_SRC_ARCHIVE list} +# are archived into debug one image to facilitate debugging. +# +DBG_SRC_ARCHIVE += sonic-db-cli \ No newline at end of file diff --git a/src/sonic-db-cli/Makefile.am b/src/sonic-db-cli/Makefile.am new file mode 100755 index 00000000000..a3569af87e6 --- /dev/null +++ b/src/sonic-db-cli/Makefile.am @@ -0,0 +1,24 @@ +########################################################################### +## +## File: ./Makefile.am +## Versions: $Id: Makefile.am,v 1.0 2022/04/02 12:04:29 liuh@microsoft.com Exp $ +## Created: 2022/04/02 +## +########################################################################### +ACLOCAL_AMFLAGS = -I m4 --install +ACLOCAL_AMFLAGS = -I config +AUTOMAKE_OPTIONS = subdir-objects +SUBDIRS = tests + +bin_PROGRAMS = sonic-db-cli +sonic_db_cli_SOURCES = sonic-db-cli.h sonic-db-cli.cpp +sonic_db_cli_CPPFLAGS = $(AM_CPPFLAGS) -std=c++17 +sonic_db_cli_LDFLAGS = -lswsscommon $(BOOST_PROGRAM_OPTIONS_LIB) + +EXTRA_DIST = bootstrap sonic-db-cli.spec + +MAINTAINERCLEANFILES = Makefile.in config.h.in configure aclocal.m4 \ + config/config.guess config/config.sub config/depcomp \ + config/install-sh config/ltmain.sh config/missing + +pkgconfigdir = $(libdir)/pkgconfig diff --git a/src/sonic-db-cli/configure.ac b/src/sonic-db-cli/configure.ac new file mode 100755 index 00000000000..b407135e2da --- /dev/null +++ b/src/sonic-db-cli/configure.ac @@ -0,0 +1,70 @@ +dnl +dnl File: configure.in +dnl Revision: $Id: configure.ac,v 1.0 2022/04/02 12:04:29 liuh@microsoft.com Exp $ +dnl Created: 2022/04/02 +dnl Author: Liu Hua +dnl +dnl Process this file with autoconf to produce a configure script +dnl You need autoconf 2.59 or better! +dnl +dnl --------------------------------------------------------------------------- + +AC_PREREQ(2.59) +AC_COPYRIGHT([ +See the included file: COPYING for copyright information. +]) +AC_INIT(sonic-db-cli, 1.0.0, [liuh@microsoft.com]) + +AC_CONFIG_AUX_DIR(config) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_SRCDIR([sonic-db-cli.cpp]) +AC_CONFIG_HEADER([config.h]) +AC_CONFIG_MACRO_DIR([config]) +AC_CONFIG_MACRO_DIR([m4]) + +dnl -------------------------------------------------------------------- +dnl Checks for programs. +AC_LANG_C +AC_LANG([C++]) +AC_PROG_CC +AC_PROG_CXX +AM_PROG_CC_C_O +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_ENABLE_SHARED +AC_DISABLE_STATIC +AM_PROG_LIBTOOL + +dnl -------------------------------------------------------------------- +dnl Checks for libraries. +AC_CHECK_LIB(swsscommon, conn) +dnl Check boost libs: https://www.gnu.org/software/autoconf-archive/The-Macros.html#The-Macros +AX_BOOST_BASE(,, [AC_MSG_ERROR([boost lib missing])]) +AX_BOOST_PROGRAM_OPTIONS + +dnl -------------------------------------------------------------------- +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADER([swss/sonicv2connector.h], [], [AC_MSG_ERROR([swsscommon lib missing. ])] ) + +dnl -------------------------------------------------------------------- +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME + +dnl -------------------------------------------------------------------- +dnl Checks for library functions. +AC_FUNC_REALLOC +AC_FUNC_SELECT_ARGTYPES +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([bzero gethostbyname gettimeofday inet_ntoa select socket logwtmp getrandom]) + +dnl -------------------------------------------------------------------- +dnl Generate made files +AC_CONFIG_FILES([ + Makefile + tests/Makefile +]) +AC_OUTPUT diff --git a/src/sonic-db-cli/debian/changelog b/src/sonic-db-cli/debian/changelog new file mode 100644 index 00000000000..48c2cce85cb --- /dev/null +++ b/src/sonic-db-cli/debian/changelog @@ -0,0 +1,5 @@ +sonic-db-cli (1.0.0) unstable; urgency=medium + + * Initial release (Closes: #nnnn) + + -- Liu Hua Wed, 06 Apr 2022 09:21:18 +0000 diff --git a/src/sonic-db-cli/debian/control b/src/sonic-db-cli/debian/control new file mode 100644 index 00000000000..6a526bedbcf --- /dev/null +++ b/src/sonic-db-cli/debian/control @@ -0,0 +1,14 @@ +Source: sonic-db-cli +Section: unknown +Priority: optional +Maintainer: Liu Hua +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.5.1 +Homepage: +Rules-Requires-Root: no + +Package: sonic-db-cli +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: + diff --git a/src/sonic-db-cli/debian/copyright b/src/sonic-db-cli/debian/copyright new file mode 100644 index 00000000000..7cf565eff8d --- /dev/null +++ b/src/sonic-db-cli/debian/copyright @@ -0,0 +1 @@ +Copyright (C) 2022 Microsoft, Inc \ No newline at end of file diff --git a/src/sonic-db-cli/debian/rules b/src/sonic-db-cli/debian/rules new file mode 100755 index 00000000000..59ea751e676 --- /dev/null +++ b/src/sonic-db-cli/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +%: + dh $@ + + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) diff --git a/src/sonic-db-cli/sonic-db-cli.cpp b/src/sonic-db-cli/sonic-db-cli.cpp new file mode 100755 index 00000000000..3ba7e56eef4 --- /dev/null +++ b/src/sonic-db-cli/sonic-db-cli.cpp @@ -0,0 +1,240 @@ +#include +#include + +#include +#include + +#include "sonic-db-cli.h" + +static std::string name_space; +static std::string db_or_operation; +const char* empty_str = ""; + +void printUsage(const po::options_description &all_options) +{ + std::cout << "usage: sonic-db-cli [-h] [-s] [-n NAMESPACE] db_or_op [cmd ...]" << std::endl; + std::cout << all_options << std::endl; + + std::cout << "**sudo** needed for commands accesing a different namespace [-n], or using unixsocket connection [-s]" << std::endl; + std::cout << "" << std::endl; + std::cout << "Example 1: sonic-db-cli -n asic0 CONFIG_DB keys \\*" << std::endl; + std::cout << "Example 2: sonic-db-cli -n asic2 APPL_DB HGETALL VLAN_TABLE:Vlan10" << std::endl; + std::cout << "Example 3: sonic-db-cli APPL_DB HGET VLAN_TABLE:Vlan10 mtu" << std::endl; + std::cout << "Example 4: sonic-db-cli -n asic3 APPL_DB EVAL \"return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}\" 2 k1 k2 v1 v2" << std::endl; + std::cout << "Example 5: sonic-db-cli PING | sonic-db-cli -s PING" << std::endl; + std::cout << "Example 6: sonic-db-cli SAVE | sonic-db-cli -s SAVE" << std::endl; + std::cout << "Example 7: sonic-db-cli FLUSHALL | sonic-db-cli -s FLUSHALL" << std::endl; +} + +void printRedisReply(redisReply* reply) +{ + switch(reply->type) + { + case REDIS_REPLY_INTEGER: + std::cout << reply->integer; + break; + case REDIS_REPLY_STRING: + case REDIS_REPLY_ERROR: + case REDIS_REPLY_STATUS: + case REDIS_REPLY_NIL: + std::cout << std::string(reply->str, reply->len); + break; + case REDIS_REPLY_ARRAY: + for (size_t i = 0; i < reply->elements; i++) + { + printRedisReply(reply->element[i]); + } + break; + default: + std::cerr << reply->type << std::endl; + throw std::runtime_error("Unexpected reply type"); + } + + std::cout << std::endl; +} + +int executeCommands( + const std::string& db_name, + std::vector& commands, + const std::string& name_space, + bool use_unix_socket) +{ + std::unique_ptr dbconn; + if (name_space.compare("None") == 0) { + dbconn = std::make_unique(use_unix_socket, empty_str); + } + else { + dbconn = std::make_unique(true, name_space.c_str()); + } + + try { + dbconn->connect(db_name); + } + catch (const std::exception& e) + { + std::cerr << "Invalid database name input: " << db_name << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + + auto& client = dbconn->get_redis_client(db_name); + + size_t argc = commands.size(); + const char** argv = new const char*[argc]; + size_t* argvc = new size_t[argc]; + for (size_t i = 0; i < argc; i++) + { + argv[i] = strdup(commands[i].c_str()); + argvc[i] = commands[i].size(); + } + + swss::RedisCommand command; + command.formatArgv(argc, argv, argvc); + swss::RedisReply reply(&client, command); + + auto redisReply = reply.getContext(); + printRedisReply(redisReply); + + return 0; +} + +void handleSingleOperation( + const std::string& name_space, + const std::string& db_name, + const std::string& operation, + bool use_unix_socket) +{ + swss::SonicV2Connector_Native conn(use_unix_socket, name_space.c_str()); + conn.connect(db_name); + auto& client = conn.get_redis_client(db_name); + + swss::RedisReply reply(&client, operation); + auto redisReply = reply.getContext(); + printRedisReply(redisReply); +} + +void handleAllInstances( + const std::string& name_space, + const std::string& operation, + bool use_unix_socket) +{ + // Use the tcp connectivity if namespace is local and unixsocket cmd_option is present. + if (name_space.compare("None") == 0) { + use_unix_socket = true; + } + + auto dbNames = swss::SonicDBConfig::getDbList(name_space); + // Operate All Redis Instances in Parallel + // TODO: if one of the operations failed, it could fail quickly and not necessary to wait all other operations + std::for_each( + std::execution::par, + dbNames.begin(), + dbNames.end(), + [=](auto&& db_name) + { + handleSingleOperation(name_space, db_name, operation, use_unix_socket); + }); +} + +int handleOperation( + const po::options_description &all_options, + const po::variables_map &variables_map) +{ + if (variables_map.count("db_or_op")) { + auto db_or_operation = variables_map["db_or_op"].as(); + auto name_space = variables_map["--namespace"].as(); + bool useUnixSocket = variables_map.count("--unixsocket"); + if (name_space.compare("None") != 0) { + swss::SonicDBConfig::initializeGlobalConfig(name_space); + } + + if (variables_map.count("cmd")) { + auto commands = variables_map["cmd"].as< std::vector >(); + return executeCommands(db_or_operation, commands, name_space, useUnixSocket); + } + else if (name_space.compare("PING") == 0 || name_space.compare("SAVE") == 0 || name_space.compare("FLUSHALL") == 0) { + // redis-cli doesn't depend on database_config.json which could raise some exceptions + // sonic-db-cli catch all possible exceptions and handle it as a failure case which not return 'OK' or 'PONG' + handleAllInstances(name_space, db_or_operation, useUnixSocket); + } + else { + printUsage(all_options); + } + } + else { + printUsage(all_options); + } + + return 0; +} + +void parseCliArguments( + int argc, + char** argv, + po::options_description &all_options, + po::variables_map &variables_map) +{ + all_options.add_options() + ("--help,-h", "Help message") + ("--unixsocket,-s", "Override use of tcp_port and use unixsocket") + ("--namespace,-n", po::value(&name_space)->default_value("None"), "Namespace string to use asic0/asic1.../asicn") + ("db_or_op", po::value(&db_or_operation)->default_value(empty_str), "Database name Or Unary operation(only PING/SAVE/FLUSHALL supported)") + ("cmd", po::value< std::vector >(), "Command to execute in database") + ; + + po::positional_options_description positional_opts; + positional_opts.add("db_or_op", 1); + positional_opts.add("cmd", -1); + + po::store(po::command_line_parser(argc, argv) + .options(all_options) + .positional(positional_opts) + .run(), + variables_map); + po::notify(variables_map); +} + +#ifdef TESTING +int sonic_db_cli(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + po::options_description all_options("SONiC DB CLI"); + po::variables_map variables_map; + + try { + parseCliArguments(argc, argv, all_options, variables_map); + } + catch (po::error_with_option_name& e) { + std::cerr << "Command Line Syntax Error: " << e.what() << std::endl; + printUsage(all_options); + return -1; + } + catch (po::error& e) { + std::cerr << "Command Line Error: " << e.what() << std::endl; + printUsage(all_options); + return -1; + } + + if (variables_map.count("--help")) { + printUsage(all_options); + return 0; + } + + try + { + return handleOperation(all_options, variables_map); + } + catch (const std::exception& e) + { + std::cerr << "An exception of type " << e.what() << " occurred. Arguments:" << std::endl; + for (int idx = 0; idx < argc; idx++) { + std::cerr << argv[idx] << " "; + } + std::cerr << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/src/sonic-db-cli/sonic-db-cli.h b/src/sonic-db-cli/sonic-db-cli.h new file mode 100755 index 00000000000..effda9507ae --- /dev/null +++ b/src/sonic-db-cli/sonic-db-cli.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +namespace po = boost::program_options; + +void printUsage(const po::options_description &all_options); + +void printRedisReply(redisReply* reply); + +int executeCommands( + const std::string& db_name, + std::vector& commands, + const std::string& name_space, + bool use_unix_socket); + +void handleSingleOperation( + const std::string& name_space, + const std::string& db_name, + const std::string& operation, + bool use_unix_socket); + +void handleAllInstances( + const std::string& name_space, + const std::string& operation, + bool use_unix_socket); + +int handleOperation( + const po::options_description &all_options, + const po::variables_map &variables_map); + +void parseCliArguments( + int argc, + char** argv, + po::options_description &all_options, + po::variables_map &variables_map); + +#ifdef TESTING +int sonic_db_cli(int argc, char** argv); +#endif \ No newline at end of file diff --git a/src/sonic-db-cli/tests/Makefile.am b/src/sonic-db-cli/tests/Makefile.am new file mode 100644 index 00000000000..428aeeb1a11 --- /dev/null +++ b/src/sonic-db-cli/tests/Makefile.am @@ -0,0 +1,15 @@ +#INCLUDES = -I $(top_srcdir) + +bin_PROGRAMS = tests +DBGFLAGS = -ggdb -DDEBUG + +CFLAGS_GTEST = -DTESTING -Wno-write-strings +LDADD_GTEST = -L/usr/src/gtest -lgtest -lgtest_main -lgmock -lgmock_main + +tests_SOURCES = main.cpp \ + ../sonic-db-cli.cpp \ + ../sonic-db-cli.h + +tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) +tests_CPPFLAGS = $(tests_CFLAGS) -std=c++17 +tests_LDADD = $(LDADD_GTEST) -lpthread -lswsscommon $(CODE_COVERAGE_LIBS) $(BOOST_PROGRAM_OPTIONS_LIB) \ No newline at end of file diff --git a/src/sonic-db-cli/tests/database_config.json b/src/sonic-db-cli/tests/database_config.json new file mode 100644 index 00000000000..feea811bce6 --- /dev/null +++ b/src/sonic-db-cli/tests/database_config.json @@ -0,0 +1,107 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port": 6379, + "unix_socket_path": "/var/run/redis/redis.sock" + }, + "redis1":{ + "hostname" : "127.0.0.1", + "port": 6380, + "unix_socket_path": "/var/run/redis/redis1.sock" + }, + "redis2":{ + "hostname" : "127.0.0.1", + "port": 6381, + "unix_socket_path": "/var/run/redis/redis2.sock" + }, + "redis3":{ + "hostname" : "127.0.0.1", + "port": 6382, + "unix_socket_path": "/var/run/redis/redis3.sock" + }, + "redis4":{ + "hostname" : "127.0.0.1", + "port": 6383, + "unix_socket_path": "/var/run/redis/redis4.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + }, + "ASIC_DB2" : { + "id" : 10, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB2" : { + "id" : 11, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB2" : { + "id" : 12, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB2" : { + "id" : 13, + "separator": "|", + "instance" : "redis" + }, + "APPL_STATE_DB" : { + "id" : 14, + "separator": ":", + "instance" : "redis" + }, + "TEST_DB" : { + "id" : 15, + "separator": ":", + "instance" : "redis" + } + }, + "VERSION" : "1.0" +} \ No newline at end of file diff --git a/src/sonic-db-cli/tests/database_config0.json b/src/sonic-db-cli/tests/database_config0.json new file mode 100644 index 00000000000..e51f6f59e1a --- /dev/null +++ b/src/sonic-db-cli/tests/database_config0.json @@ -0,0 +1,87 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port": 6379, + "unix_socket_path": "/var/run/redis0/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + }, + "ASIC_DB2" : { + "id" : 10, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB2" : { + "id" : 11, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB2" : { + "id" : 12, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB2" : { + "id" : 13, + "separator": "|", + "instance" : "redis" + }, + "APPL_STATE_DB" : { + "id" : 14, + "separator": ":", + "instance" : "redis" + }, + "TEST_DB" : { + "id" : 15, + "separator": ":", + "instance" : "redis" + } + }, + "VERSION" : "1.0" +} \ No newline at end of file diff --git a/src/sonic-db-cli/tests/database_config1.json b/src/sonic-db-cli/tests/database_config1.json new file mode 100644 index 00000000000..568df3cc6d3 --- /dev/null +++ b/src/sonic-db-cli/tests/database_config1.json @@ -0,0 +1,87 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port": 6379, + "unix_socket_path": "/var/run/redis1/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + }, + "ASIC_DB2" : { + "id" : 10, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB2" : { + "id" : 11, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB2" : { + "id" : 12, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB2" : { + "id" : 13, + "separator": "|", + "instance" : "redis" + }, + "APPL_STATE_DB" : { + "id" : 14, + "separator": ":", + "instance" : "redis" + }, + "TEST_DB" : { + "id" : 15, + "separator": ":", + "instance" : "redis" + } + }, + "VERSION" : "1.0" +} \ No newline at end of file diff --git a/src/sonic-db-cli/tests/database_global.json b/src/sonic-db-cli/tests/database_global.json new file mode 100644 index 00000000000..fe33bb6de98 --- /dev/null +++ b/src/sonic-db-cli/tests/database_global.json @@ -0,0 +1,16 @@ +{ + "INCLUDES" : [ + { + "include" : "database_config.json" + }, + { + "namespace" : "asic0", + "include" : "database_config0.json" + }, + { + "namespace" : "asic1", + "include" : "database_config1.json" + } + ], + "VERSION" : "1.0" +} \ No newline at end of file diff --git a/src/sonic-db-cli/tests/help_output.txt b/src/sonic-db-cli/tests/help_output.txt new file mode 100644 index 00000000000..ff2ea28266c --- /dev/null +++ b/src/sonic-db-cli/tests/help_output.txt @@ -0,0 +1,18 @@ +usage: sonic-db-cli [-h] [-s] [-n NAMESPACE] db_or_op [cmd ...] +SONiC DB CLI: + ----help Help message + ----unixsocket Override use of tcp_port and use unixsocket + ----namespace arg (=None) Namespace string to use asic0/asic1.../asicn + --db_or_op arg Database name Or Unary operation(only + PING/SAVE/FLUSHALL supported) + --cmd arg Command to execute in database + +**sudo** needed for commands accesing a different namespace [-n], or using unixsocket connection [-s] + +Example 1: sonic-db-cli -n asic0 CONFIG_DB keys \* +Example 2: sonic-db-cli -n asic2 APPL_DB HGETALL VLAN_TABLE:Vlan10 +Example 3: sonic-db-cli APPL_DB HGET VLAN_TABLE:Vlan10 mtu +Example 4: sonic-db-cli -n asic3 APPL_DB EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 k1 k2 v1 v2 +Example 5: sonic-db-cli PING | sonic-db-cli -s PING +Example 6: sonic-db-cli SAVE | sonic-db-cli -s SAVE +Example 7: sonic-db-cli FLUSHALL | sonic-db-cli -s FLUSHALL diff --git a/src/sonic-db-cli/tests/main.cpp b/src/sonic-db-cli/tests/main.cpp new file mode 100644 index 00000000000..a70f482cb04 --- /dev/null +++ b/src/sonic-db-cli/tests/main.cpp @@ -0,0 +1,71 @@ +#include "gtest/gtest.h" +#include +#include +#include +#include +#include "sonic-db-cli.h" + +std::string db_file = "./database_config.json"; +std::string global_db_file = "./database_global.json"; + +#define TEST_DB "APPL_DB" +#define TEST_NAMESPACE "asic0" +#define INVALID_NAMESPACE "invalid" + +class SwsscommonEnvironment : public ::testing::Environment { +public: + void SetUp() override { + // load local config file + //SonicDBConfig::initialize(db_file); + //EXPECT_TRUE(SonicDBConfig::isInit()); + + // load local global file + swss::SonicDBConfig::initializeGlobalConfig(global_db_file); + EXPECT_TRUE(swss::SonicDBConfig::isGlobalInit()); + } +}; + +std::string readFileContent(std::string file_name) { + std::ifstream help_output_file(file_name); + std::stringstream buffer; + buffer << help_output_file.rdbuf(); + return buffer.str(); +} + +TEST(sonic_db_cli, test_cli_help) { + char *args[2]; + args[0] = "sonic-db-cli"; + args[1] = "-h"; + + testing::internal::CaptureStdout(); + EXPECT_EQ(0, sonic_db_cli(1, args)); + auto output = testing::internal::GetCapturedStdout(); + auto expected_output = readFileContent("help_output.txt"); + EXPECT_EQ(expected_output, output); +} + +TEST(sonic_db_cli, test_cli_default_ns_run_cmd) { + char *args[5]; + args[0] = "sonic-db-cli"; + args[1] = "APPL_DB"; + args[2] = "HGET"; + args[3] = "VLAN_TABLE:Vlan10"; + args[4] = "mtu"; + + testing::internal::CaptureStdout(); + EXPECT_EQ(0, sonic_db_cli(4, args)); + auto output = testing::internal::GetCapturedStdout(); + std::cout << output <