Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ConfigDBPipeConnector]: Added set_entry API #586

Merged
merged 2 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions common/configdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,63 @@ void ConfigDBPipeConnector_Native::_delete_table(DBConnector& client, RedisTrans
}
}

// Write a table entry to config db
// Remove extra fields in the db which are not in the data
// Args:
// table: Table name
// key: Key of table entry, or a tuple of keys if it is a multi-key table
// data: Table row data in a form of dictionary {'column_key': 'value', ...}
// Pass {} as data will delete the entry
void ConfigDBPipeConnector_Native::set_entry(string table, string key, const map<string, string>& data)
nazariig marked this conversation as resolved.
Show resolved Hide resolved
{
auto& client = get_redis_client(m_db_name);
DBConnector clientPipe(client);
RedisTransactioner pipe(&clientPipe);

pipe.multi();
_set_entry(pipe, table, key, data);
pipe.exec();
nazariig marked this conversation as resolved.
Show resolved Hide resolved
}

// Write a table entry to config db
// Remove extra fields in the db which are not in the data
// Args:
// pipe: Redis DB pipe
// table: Table name
// key: Key of table entry, or a tuple of keys if it is a multi-key table
// data: Table row data in a form of dictionary {'column_key': 'value', ...}
// Pass {} as data will delete the entry
void ConfigDBPipeConnector_Native::_set_entry(RedisTransactioner& pipe, std::string table, std::string key, const std::map<std::string, std::string>& data)
{
string _hash = to_upper(table) + m_table_name_separator + key;
if (data.empty())
{
RedisCommand sdel;
sdel.format("DEL %s", _hash.c_str());
pipe.enqueue(sdel, REDIS_REPLY_INTEGER);
}
else
{
auto original = get_entry(table, key);

RedisCommand shmset;
shmset.formatHMSET(_hash, data.begin(), data.end());
pipe.enqueue(shmset, REDIS_REPLY_STATUS);

for (auto& it: original)
{
auto& k = it.first;
bool found = data.find(k) != data.end();
if (!found)
{
RedisCommand shdel;
shdel.formatHDEL(_hash, k);
pipe.enqueue(shdel, REDIS_REPLY_INTEGER);
}
}
}
}

// Modify a table entry to config db.
// Args:
// table: Table name.
Expand Down
6 changes: 4 additions & 2 deletions common/configdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class ConfigDBConnector_Native : public SonicV2Connector_Native
void db_connect(std::string db_name, bool wait_for_init = false, bool retry_on = false);
void connect(bool wait_for_init = true, bool retry_on = false);

void set_entry(std::string table, std::string key, const std::map<std::string, std::string>& data);
void mod_entry(std::string table, std::string key, const std::map<std::string, std::string>& data);
virtual void set_entry(std::string table, std::string key, const std::map<std::string, std::string>& data);
virtual void mod_entry(std::string table, std::string key, const std::map<std::string, std::string>& data);
nazariig marked this conversation as resolved.
Show resolved Hide resolved
std::map<std::string, std::string> get_entry(std::string table, std::string key);
std::vector<std::string> get_keys(std::string table, bool split = true);
std::map<std::string, std::map<std::string, std::string>> get_table(std::string table);
Expand Down Expand Up @@ -218,6 +218,7 @@ class ConfigDBPipeConnector_Native: public ConfigDBConnector_Native
public:
ConfigDBPipeConnector_Native(bool use_unix_socket_path = false, const char *netns = "");

void set_entry(std::string table, std::string key, const std::map<std::string, std::string>& data) override;
void mod_config(const std::map<std::string, std::map<std::string, std::map<std::string, std::string>>>& data) override;
std::map<std::string, std::map<std::string, std::map<std::string, std::string>>> get_config() override;

Expand All @@ -226,6 +227,7 @@ class ConfigDBPipeConnector_Native: public ConfigDBConnector_Native

int _delete_entries(DBConnector& client, RedisTransactioner& pipe, const char *pattern, int cursor);
void _delete_table(DBConnector& client, RedisTransactioner& pipe, std::string table);
void _set_entry(RedisTransactioner& pipe, std::string table, std::string key, const std::map<std::string, std::string>& data);
void _mod_entry(RedisTransactioner& pipe, std::string table, std::string key, const std::map<std::string, std::string>& data);
int _get_config(DBConnector& client, RedisTransactioner& pipe, std::map<std::string, std::map<std::string, std::map<std::string, std::string>>>& data, int cursor);
};
Expand Down
1 change: 1 addition & 0 deletions common/schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ namespace swss {

#define STATE_SWITCH_CAPABILITY_TABLE_NAME "SWITCH_CAPABILITY_TABLE"
#define STATE_ACL_STAGE_CAPABILITY_TABLE_NAME "ACL_STAGE_CAPABILITY_TABLE"
#define STATE_PBH_CAPABILITIES_TABLE_NAME "PBH_CAPABILITIES"
#define STATE_PORT_TABLE_NAME "PORT_TABLE"
#define STATE_LAG_TABLE_NAME "LAG_TABLE"
#define STATE_VLAN_TABLE_NAME "VLAN_TABLE"
Expand Down
89 changes: 79 additions & 10 deletions tests/test_redis_ut.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,24 +398,93 @@ def test_ConfigDBPipeConnector():
config_db = ConfigDBPipeConnector()
config_db.connect(wait_for_init=False)
config_db.get_redis_client(config_db.CONFIG_DB).flushdb()
config_db.set_entry("TEST_PORT", "Ethernet112", {"alias": "etp1x"})

#
# set_entry
#

# Verify entry set
config_db.set_entry("PORT_TABLE", "Ethernet1", {"alias": "etp1x"})
allconfig = config_db.get_config()
assert allconfig["TEST_PORT"]["Ethernet112"]["alias"] == "etp1x"
assert allconfig["PORT_TABLE"]["Ethernet1"]["alias"] == "etp1x"

config_db.set_entry("TEST_PORT", "Ethernet112", {"mtu": "12345"})
allconfig = config_db.get_config()
assert "alias" not in allconfig["TEST_PORT"]["Ethernet112"]
assert allconfig["TEST_PORT"]["Ethernet112"]["mtu"] == "12345"
config_db.set_entry("ACL_TABLE", "EVERFLOW", {"ports": ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]})
allconfig = config_db.get_config()
assert allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] == ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]

# Verify entry update
config_db.set_entry("PORT_TABLE", "Ethernet1", {"mtu": "12345"})
allconfig = config_db.get_config()
assert "alias" not in allconfig["PORT_TABLE"]["Ethernet1"]
assert allconfig["PORT_TABLE"]["Ethernet1"]["mtu"] == "12345"

# Verify entry clear
config_db.set_entry("PORT_TABLE", "Ethernet1", {})
allconfig = config_db.get_config()
assert len(allconfig["PORT_TABLE"]["Ethernet1"]) == 0

# Verify entry delete
config_db.set_entry("PORT_TABLE", "Ethernet1", None)
config_db.set_entry("ACL_TABLE", "EVERFLOW", None)
allconfig = config_db.get_config()
assert len(allconfig) == 0

#
# mod_config
#

# Verify entry set
allconfig.setdefault("PORT_TABLE", {}).setdefault("Ethernet1", {})
allconfig["PORT_TABLE"]["Ethernet1"]["alias"] = "etp1x"
config_db.mod_config(allconfig)
allconfig = config_db.get_config()
assert allconfig["PORT_TABLE"]["Ethernet1"]["alias"] == "etp1x"

allconfig.setdefault("VLAN_TABLE", {})
allconfig["VLAN_TABLE"]["Vlan1"] = {}
config_db.mod_config(allconfig)
allconfig["TEST_PORT"]["Ethernet113"] = None
allconfig["TEST_VLAN"] = None
allconfig = config_db.get_config()
assert len(allconfig["VLAN_TABLE"]["Vlan1"]) == 0

allconfig.setdefault("ACL_TABLE", {}).setdefault("EVERFLOW", {})
allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] = ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]
config_db.mod_config(allconfig)
allconfig.setdefault("ACL_TABLE", {}).setdefault("EVERFLOW", {})["ports"] = ["Ethernet0", "Ethernet4", "Ethernet8"]
allconfig = config_db.get_config()
assert allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] == ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]

# Verify entry delete
allconfig["PORT_TABLE"]["Ethernet1"] = None
allconfig["VLAN_TABLE"]["Vlan1"] = None
allconfig["ACL_TABLE"]["EVERFLOW"] = None
config_db.mod_config(allconfig)
allconfig = config_db.get_config()
assert len(allconfig) == 0

config_db.delete_table("TEST_PORT")
# Verify table delete
for i in range(1, 1001, 1):
# Make sure we have enough entries to trigger REDIS_SCAN_BATCH_SIZE
allconfig.setdefault("PORT_TABLE", {}).setdefault("Ethernet{}".format(i), {})
allconfig["PORT_TABLE"]["Ethernet{}".format(i)]["alias"] = "etp{}x".format(i)

config_db.mod_config(allconfig)
allconfig = config_db.get_config()
assert len(allconfig["PORT_TABLE"]) == 1000

allconfig["PORT_TABLE"] = None
config_db.mod_config(allconfig)
allconfig = config_db.get_config()
assert len(allconfig) == 0

#
# delete_table
#

# Verify direct table delete
allconfig.setdefault("PORT_TABLE", {}).setdefault("Ethernet1", {})
allconfig["PORT_TABLE"]["Ethernet1"]["alias"] = "etp1x"
allconfig.setdefault("ACL_TABLE", {}).setdefault("EVERFLOW", {})
allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] = ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]
config_db.delete_table("PORT_TABLE")
config_db.delete_table("ACL_TABLE")
allconfig = config_db.get_config()
assert len(allconfig) == 0
Expand Down