Skip to content

Commit 3423a49

Browse files
authored
Improve orchagent to enable Dash ZMQ by feature flag. (#3619)
Improve orchagent to enable Dash ZMQ by feature flag. Why I did it On current image, orchagent only enable ZMQ on Dash tables, so use -q option to control the ZMQ feature is enough. However, we plan to improve route performance by enable ZMQ between fpmsyncd and orchagent. sonic-net/SONiC#1659 Then there will be 2 features on orchagent share same ZMQ address, so using -q to control the Dash ZMQ feature is not enough. For Dash, we plan to migrate to enable/disable with the 'orch_dash_zmq_enabled' feature flag. There will be another PR in orchagent to make Dash enable/disable ZMQ with this flag. And for improve route performance by enable ZMQ between fpmsyncd and orchagent, it will be controlled by the 'orch_route_zmq_enabled' flag. ZMQ feature flag add by following PR: sonic-net/sonic-buildimage#22451
1 parent f38f6e4 commit 3423a49

9 files changed

Lines changed: 259 additions & 18 deletions

File tree

lib/orch_zmq_config.cpp

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
#include <iostream>
22
#include <fstream>
3+
#include <regex>
34

5+
#include "dbconnector.h"
6+
#include "logger.h"
47
#include "orch_zmq_config.h"
8+
#include <stdio.h>
59

610
#define ZMQ_TABLE_CONFIGFILE "/etc/swss/orch_zmq_tables.conf"
711

12+
// ZMQ none IPV6 address with port, for example: tcp://127.0.0.1:5555 tcp://localhost:5555
13+
const std::regex ZMQ_NONE_IPV6_ADDRESS_WITH_PORT("\\w+:\\/\\/[^:]+:\\d+");
14+
15+
// ZMQ IPV6 address with port, for example: tcp://[fe80::fb7:c6df:9d3a:3d7b]:5555
16+
const std::regex ZMQ_IPV6_ADDRESS_WITH_PORT("\\w+:\\/\\/\\[.*\\]+:\\d+");
17+
818
std::set<std::string> swss::load_zmq_tables()
919
{
1020
std::set<std::string> tables;
@@ -22,16 +32,62 @@ std::set<std::string> swss::load_zmq_tables()
2232
return tables;
2333
}
2434

25-
26-
std::shared_ptr<swss::ZmqClient> swss::create_zmq_client(std::string zmq_address)
35+
int swss::get_zmq_port()
2736
{
28-
// swssconfig running inside swss contianer, so need get ZMQ port according to namespace ID.
2937
auto zmq_port = ORCH_ZMQ_PORT;
3038
if (const char* nsid = std::getenv("NAMESPACE_ID"))
3139
{
3240
// namespace start from 0, using original ZMQ port for global namespace
3341
zmq_port += atoi(nsid) + 1;
3442
}
3543

36-
return std::make_shared<ZmqClient>(zmq_address + ":" + std::to_string(zmq_port));
44+
return zmq_port;
45+
}
46+
47+
std::shared_ptr<swss::ZmqClient> swss::create_zmq_client(std::string zmq_address, std::string vrf)
48+
{
49+
// swssconfig running inside swss contianer, so need get ZMQ port according to namespace ID.
50+
auto zmq_port = get_zmq_port();
51+
zmq_address = zmq_address + ":" + std::to_string(zmq_port);
52+
SWSS_LOG_NOTICE("Create ZMQ server with address: %s, vrf: %s", zmq_address.c_str(), vrf.c_str());
53+
return std::make_shared<ZmqClient>(zmq_address, vrf);
54+
}
55+
56+
std::shared_ptr<swss::ZmqServer> swss::create_zmq_server(std::string zmq_address, std::string vrf)
57+
{
58+
// TODO: remove this check after orchagent.sh migrate to pass ZMQ address without port
59+
if (!std::regex_search(zmq_address, ZMQ_NONE_IPV6_ADDRESS_WITH_PORT)
60+
&& !std::regex_search(zmq_address, ZMQ_IPV6_ADDRESS_WITH_PORT))
61+
{
62+
auto zmq_port = get_zmq_port();
63+
zmq_address = zmq_address + ":" + std::to_string(zmq_port);
64+
}
65+
66+
SWSS_LOG_NOTICE("Create ZMQ server with address: %s, vrf: %s", zmq_address.c_str(), vrf.c_str());
67+
return std::make_shared<ZmqServer>(zmq_address, vrf);
68+
}
69+
70+
bool swss::get_feature_status(std::string feature, bool default_value)
71+
{
72+
std::shared_ptr<std::string> enabled = nullptr;
73+
74+
try
75+
{
76+
swss::DBConnector config_db("CONFIG_DB", 0);
77+
enabled = config_db.hget("DEVICE_METADATA|localhost", feature);
78+
}
79+
catch (const std::runtime_error &e)
80+
{
81+
SWSS_LOG_ERROR("Not found feature %s failed with exception: %s", feature.c_str(), e.what());
82+
return default_value;
83+
}
84+
85+
if (!enabled)
86+
{
87+
SWSS_LOG_NOTICE("Not found feature %s status, return default value.", feature.c_str());
88+
return default_value;
89+
}
90+
91+
SWSS_LOG_NOTICE("Get feature %s status: %s", feature.c_str(), enabled->c_str());
92+
return *enabled == "true";
3793
}

lib/orch_zmq_config.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
#ifndef SWSS_ORCH_ZMQ_CONFIG_H
22
#define SWSS_ORCH_ZMQ_CONFIG_H
33

4+
#include <memory>
45
#include <string.h>
56
#include <set>
67

78
#include "dbconnector.h"
89
#include "zmqclient.h"
10+
#include "zmqserver.h"
11+
#include "zmqproducerstatetable.h"
912

1013
/*
1114
* swssconfig will only connect to local orchagent ZMQ endpoint.
1215
*/
1316
#define ZMQ_LOCAL_ADDRESS "tcp://localhost"
1417

18+
/*
19+
* Feature flag to enable the gNMI service to send DASH events to orchagent via the ZMQ channel.
20+
*/
21+
#define ORCH_NORTHBOND_DASH_ZMQ_ENABLED "orch_northbond_dash_zmq_enabled"
22+
1523
namespace swss {
1624

1725
std::set<std::string> load_zmq_tables();
1826

19-
std::shared_ptr<ZmqClient> create_zmq_client(std::string zmq_address);
27+
int get_zmq_port();
28+
29+
std::shared_ptr<ZmqClient> create_zmq_client(std::string zmq_address, std::string vrf="");
30+
31+
std::shared_ptr<ZmqServer> create_zmq_server(std::string zmq_address, std::string vrf="");
32+
33+
bool get_feature_status(std::string feature, bool default_value);
2034

2135
}
2236

orchagent/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ orchagent_SOURCES = \
4848
$(top_srcdir)/lib/gearboxutils.cpp \
4949
$(top_srcdir)/lib/subintf.cpp \
5050
$(top_srcdir)/lib/recorder.cpp \
51+
$(top_srcdir)/lib/orch_zmq_config.cpp \
5152
orchdaemon.cpp \
5253
orch.cpp \
5354
notifications.cpp \

orchagent/main.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extern "C" {
2323
#include <logger.h>
2424

2525
#include "orchdaemon.h"
26+
#include "orch_zmq_config.h"
2627
#include "sai_serialize.h"
2728
#include "saihelper.h"
2829
#include "notifications.h"
@@ -362,9 +363,8 @@ int main(int argc, char **argv)
362363
string record_location = Recorder::DEFAULT_DIR;
363364
string swss_rec_filename = Recorder::SWSS_FNAME;
364365
string sairedis_rec_filename = Recorder::SAIREDIS_FNAME;
365-
string zmq_server_address = "tcp://127.0.0.1:" + to_string(ORCH_ZMQ_PORT);
366+
string zmq_server_address = "";
366367
string vrf;
367-
bool enable_zmq = false;
368368
string responsepublisher_rec_filename = Recorder::RESPPUB_FNAME;
369369
int record_type = 3; // Only swss and sairedis recordings enabled by default.
370370
long heartBeatInterval = HEART_BEAT_INTERVAL_MSECS_DEFAULT;
@@ -457,7 +457,6 @@ int main(int argc, char **argv)
457457
if (optarg)
458458
{
459459
zmq_server_address = optarg;
460-
enable_zmq = true;
461460
}
462461
break;
463462
case 't':
@@ -530,14 +529,14 @@ int main(int argc, char **argv)
530529

531530
// Instantiate ZMQ server
532531
shared_ptr<ZmqServer> zmq_server = nullptr;
533-
if (enable_zmq)
532+
if (zmq_server_address.empty())
534533
{
535-
SWSS_LOG_NOTICE("Instantiate ZMQ server : %s, %s", zmq_server_address.c_str(), vrf.c_str());
536-
zmq_server = make_shared<ZmqServer>(zmq_server_address.c_str(), vrf.c_str());
534+
SWSS_LOG_NOTICE("The ZMQ channel on the northbound side of orchagent has been disabled.");
537535
}
538536
else
539537
{
540-
SWSS_LOG_NOTICE("ZMQ disabled");
538+
SWSS_LOG_NOTICE("The ZMQ channel on the northbound side of orchagent has been initialized: %s, %s", zmq_server_address.c_str(), vrf.c_str());
539+
zmq_server = create_zmq_server(zmq_server_address, vrf);
541540
}
542541

543542
// Get switch_type

orchagent/orchdaemon.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <sairedis.h>
88
#include "warm_restart.h"
99
#include <iostream>
10+
#include "orch_zmq_config.h"
1011

1112
#define SAI_SWITCH_ATTR_CUSTOM_RANGE_BASE SAI_SWITCH_ATTR_CUSTOM_RANGE_START
1213
#include "sairedis.h"
@@ -1222,11 +1223,20 @@ bool DpuOrchDaemon::init()
12221223
{
12231224
SWSS_LOG_NOTICE("DpuOrchDaemon init...");
12241225
OrchDaemon::init();
1226+
1227+
// Enable the gNMI service to send DASH events to orchagent via the ZMQ channel.
1228+
ZmqServer *dash_zmq_server = nullptr;
1229+
if (get_feature_status(ORCH_NORTHBOND_DASH_ZMQ_ENABLED, true))
1230+
{
1231+
SWSS_LOG_NOTICE("Enable the gNMI service to send DASH events to orchagent via the ZMQ channel.");
1232+
dash_zmq_server = m_zmqServer;
1233+
}
1234+
12251235
vector<string> dash_vnet_tables = {
12261236
APP_DASH_VNET_TABLE_NAME,
12271237
APP_DASH_VNET_MAPPING_TABLE_NAME
12281238
};
1229-
DashVnetOrch *dash_vnet_orch = new DashVnetOrch(m_applDb, dash_vnet_tables, m_dpu_appstateDb, m_zmqServer);
1239+
DashVnetOrch *dash_vnet_orch = new DashVnetOrch(m_applDb, dash_vnet_tables, m_dpu_appstateDb, dash_zmq_server);
12301240
gDirectory.set(dash_vnet_orch);
12311241

12321242
vector<string> dash_tables = {
@@ -1237,7 +1247,7 @@ bool DpuOrchDaemon::init()
12371247
APP_DASH_QOS_TABLE_NAME
12381248
};
12391249

1240-
DashOrch *dash_orch = new DashOrch(m_applDb, dash_tables, m_dpu_appstateDb, m_zmqServer);
1250+
DashOrch *dash_orch = new DashOrch(m_applDb, dash_tables, m_dpu_appstateDb, dash_zmq_server);
12411251
gDirectory.set(dash_orch);
12421252

12431253
vector<string> dash_ha_tables = {
@@ -1254,7 +1264,7 @@ bool DpuOrchDaemon::init()
12541264
APP_DASH_ROUTE_GROUP_TABLE_NAME
12551265
};
12561266

1257-
DashRouteOrch *dash_route_orch = new DashRouteOrch(m_applDb, dash_route_tables, dash_orch, m_dpu_appstateDb, m_zmqServer);
1267+
DashRouteOrch *dash_route_orch = new DashRouteOrch(m_applDb, dash_route_tables, dash_orch, m_dpu_appstateDb, dash_zmq_server);
12581268
gDirectory.set(dash_route_orch);
12591269

12601270
vector<string> dash_acl_tables = {
@@ -1264,20 +1274,20 @@ bool DpuOrchDaemon::init()
12641274
APP_DASH_ACL_GROUP_TABLE_NAME,
12651275
APP_DASH_ACL_RULE_TABLE_NAME
12661276
};
1267-
DashAclOrch *dash_acl_orch = new DashAclOrch(m_applDb, dash_acl_tables, dash_orch, m_dpu_appstateDb, m_zmqServer);
1277+
DashAclOrch *dash_acl_orch = new DashAclOrch(m_applDb, dash_acl_tables, dash_orch, m_dpu_appstateDb, dash_zmq_server);
12681278
gDirectory.set(dash_acl_orch);
12691279

12701280
vector<string> dash_tunnel_tables = {
12711281
APP_DASH_TUNNEL_TABLE_NAME
12721282
};
1273-
DashTunnelOrch *dash_tunnel_orch = new DashTunnelOrch(m_applDb, dash_tunnel_tables, m_dpu_appstateDb, m_zmqServer);
1283+
DashTunnelOrch *dash_tunnel_orch = new DashTunnelOrch(m_applDb, dash_tunnel_tables, m_dpu_appstateDb, dash_zmq_server);
12741284
gDirectory.set(dash_tunnel_orch);
12751285

12761286
vector<string> dash_meter_tables = {
12771287
APP_DASH_METER_POLICY_TABLE_NAME,
12781288
APP_DASH_METER_RULE_TABLE_NAME
12791289
};
1280-
DashMeterOrch *dash_meter_orch = new DashMeterOrch(m_applDb, dash_meter_tables, dash_orch, m_dpu_appstateDb, m_zmqServer);
1290+
DashMeterOrch *dash_meter_orch = new DashMeterOrch(m_applDb, dash_meter_tables, dash_orch, m_dpu_appstateDb, dash_zmq_server);
12811291
gDirectory.set(dash_meter_orch);
12821292

12831293
addOrchList(dash_acl_orch);

tests/mock_tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ tests_SOURCES = aclorch_ut.cpp \
7878
$(top_srcdir)/lib/gearboxutils.cpp \
7979
$(top_srcdir)/lib/subintf.cpp \
8080
$(top_srcdir)/lib/recorder.cpp \
81+
$(top_srcdir)/lib/orch_zmq_config.cpp \
8182
$(top_srcdir)/orchagent/orchdaemon.cpp \
8283
$(top_srcdir)/orchagent/orch.cpp \
8384
$(top_srcdir)/orchagent/notifications.cpp \

tests/mock_tests/mock_table.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "table.h"
22
#include "producerstatetable.h"
33
#include "producertable.h"
4+
#include "mock_table.h"
45
#include <set>
56
#include <memory>
67

@@ -145,6 +146,12 @@ namespace swss
145146
std::shared_ptr<std::string> DBConnector::hget(const std::string &key, const std::string &field)
146147
{
147148
std::string value;
149+
150+
if (field == HGET_THROW_EXCEPTION_FIELD_NAME)
151+
{
152+
throw std::runtime_error("HGET failed, unexpected reply type, memory exception");
153+
}
154+
148155
if (_hget(getDbId(), key, "", field, value))
149156
{
150157
std::shared_ptr<std::string> ptr(new std::string(value));
@@ -156,6 +163,51 @@ namespace swss
156163
}
157164
}
158165

166+
int64_t DBConnector::hdel(const std::string &key, const std::string &field)
167+
{
168+
auto &table = gDB[getDbId()][key];
169+
auto key_iter = table.find("");
170+
if (key_iter == table.end())
171+
{
172+
return 0;
173+
}
174+
175+
int removed = 0;
176+
auto attrs = key_iter->second;
177+
std::vector<FieldValueTuple> new_attrs;
178+
for (auto attr_iter : attrs)
179+
{
180+
if (attr_iter.first == field)
181+
{
182+
removed += 1;
183+
continue;
184+
}
185+
186+
new_attrs.push_back(attr_iter);
187+
}
188+
189+
table[""] = new_attrs;
190+
191+
return removed;
192+
}
193+
194+
void DBConnector::hset(const std::string &key, const std::string &field, const std::string &value)
195+
{
196+
FieldValueTuple fvp(field, value);
197+
std::vector<FieldValueTuple> attrs = { fvp };
198+
199+
auto &table = gDB[getDbId()][key];
200+
auto iter = table.find("");
201+
if (iter == table.end())
202+
{
203+
table[""] = attrs;
204+
}
205+
else
206+
{
207+
merge_values(iter->second, attrs);
208+
}
209+
}
210+
159211
void ProducerTable::set(const std::string &key,
160212
const std::vector<FieldValueTuple> &values,
161213
const std::string &op,

tests/mock_tests/mock_table.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
#include "table.h"
44

5+
// Use this field in the mock test to simulate an exception during hget.
6+
#define HGET_THROW_EXCEPTION_FIELD_NAME "hget_throw_exception"
7+
58
namespace testing_db
69
{
710
void reset();

0 commit comments

Comments
 (0)