Skip to content

Commit 310e0aa

Browse files
authored
[portsorch,intfsorch] add port, rif rates FC groups (#1201)
* [portsorch,intfsorch] add port, rif rates FC groups * fix comments * remove speed calc from lua scripts * trigger lgtm
1 parent 5ddea37 commit 310e0aa

8 files changed

+203
-6
lines changed

orchagent/Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ CFLAGS_SAI = -I /usr/include/sai
55
swssdir = $(datadir)/swss
66

77
dist_swss_DATA = \
8+
rif_rates.lua \
89
pfc_detect_innovium.lua \
910
pfc_detect_mellanox.lua \
1011
pfc_detect_broadcom.lua \
1112
pfc_detect_barefoot.lua \
1213
pfc_detect_nephos.lua \
1314
pfc_restore.lua \
15+
port_rates.lua \
1416
watermark_queue.lua \
1517
watermark_pg.lua \
1618
watermark_bufferpool.lua

orchagent/flexcounterorch.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ extern BufferOrch *gBufferOrch;
2121
unordered_map<string, string> flexCounterGroupMap =
2222
{
2323
{"PORT", PORT_STAT_COUNTER_FLEX_COUNTER_GROUP},
24+
{"PORT_RATES", PORT_RATE_COUNTER_FLEX_COUNTER_GROUP},
2425
{"QUEUE", QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP},
2526
{"PFCWD", PFC_WD_FLEX_COUNTER_GROUP},
2627
{"QUEUE_WATERMARK", QUEUE_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP},
2728
{"PG_WATERMARK", PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP},
2829
{BUFFER_POOL_WATERMARK_KEY, BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP},
2930
{"RIF", RIF_STAT_COUNTER_FLEX_COUNTER_GROUP},
31+
{"RIF_RATES", RIF_RATE_COUNTER_FLEX_COUNTER_GROUP},
3032
{"DEBUG_COUNTER", DEBUG_COUNTER_FLEX_COUNTER_GROUP},
3133
};
3234

orchagent/intfsorch.cpp

+18-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,24 @@ IntfsOrch::IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch) :
7878
fieldValues.emplace_back(POLL_INTERVAL_FIELD, RIF_FLEX_STAT_COUNTER_POLL_MSECS);
7979
fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ);
8080
m_flexCounterGroupTable->set(RIF_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues);
81+
82+
string rifRatePluginName = "rif_rates.lua";
83+
84+
try
85+
{
86+
string rifRateLuaScript = swss::loadLuaScript(rifRatePluginName);
87+
string rifRateSha = swss::loadRedisScript(m_counter_db.get(), rifRateLuaScript);
88+
89+
vector<FieldValueTuple> fieldValues;
90+
fieldValues.emplace_back(RIF_PLUGIN_FIELD, rifRateSha);
91+
fieldValues.emplace_back(POLL_INTERVAL_FIELD, RIF_FLEX_STAT_COUNTER_POLL_MSECS);
92+
fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ);
93+
m_flexCounterGroupTable->set(RIF_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues);
94+
}
95+
catch (const runtime_error &e)
96+
{
97+
SWSS_LOG_WARN("RIF flex counter group plugins was not set successfully: %s", e.what());
98+
}
8199
}
82100

83101
sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias)
@@ -1140,7 +1158,6 @@ void IntfsOrch::addRifToFlexCounter(const string &id, const string &name, const
11401158
/* update RIF in FLEX_COUNTER_DB */
11411159
string key = getRifFlexCounterTableKey(id);
11421160

1143-
11441161
std::ostringstream counters_stream;
11451162
for (const auto& it: rifStatIds)
11461163
{
@@ -1150,7 +1167,6 @@ void IntfsOrch::addRifToFlexCounter(const string &id, const string &name, const
11501167
/* check the state of intf, if registering the intf to FC will result in runtime error */
11511168
vector<FieldValueTuple> fieldValues;
11521169
fieldValues.emplace_back(RIF_COUNTER_ID_LIST, counters_stream.str());
1153-
11541170
m_flexCounterTable->set(key, fieldValues);
11551171
SWSS_LOG_DEBUG("Registered interface %s to Flex counter", name.c_str());
11561172
}

orchagent/intfsorch.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern sai_object_id_t gVirtualRouterId;
1717
extern MacAddress gMacAddress;
1818

1919
#define RIF_STAT_COUNTER_FLEX_COUNTER_GROUP "RIF_STAT_COUNTER"
20+
#define RIF_RATE_COUNTER_FLEX_COUNTER_GROUP "RIF_RATE_COUNTER"
2021

2122
struct IntfsEntry
2223
{
@@ -36,7 +37,7 @@ class IntfsOrch : public Orch
3637
sai_object_id_t getRouterIntfsId(const string&);
3738
bool isPrefixSubnet(const IpPrefix&, const string&);
3839
string getRouterIntfsAlias(const IpAddress &ip, const string &vrf_name = "");
39-
40+
string getRifRateFlexCounterTableKey(string key);
4041
void increaseRouterIntfsRefCount(const string&);
4142
void decreaseRouterIntfsRefCount(const string&);
4243

orchagent/port_rates.lua

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
-- KEYS - port IDs
2+
-- ARGV[1] - counters db index
3+
-- ARGV[2] - counters table name
4+
-- ARGV[3] - poll time interval
5+
-- return log
6+
7+
local logtable = {}
8+
9+
local function logit(msg)
10+
logtable[#logtable+1] = tostring(msg)
11+
end
12+
13+
local counters_db = ARGV[1]
14+
local counters_table_name = ARGV[2]
15+
local rates_table_name = "RATES"
16+
17+
-- Get configuration
18+
redis.call('SELECT', counters_db)
19+
local smooth_interval = redis.call('HGET', rates_table_name .. ':' .. 'PORT', 'PORT_SMOOTH_INTERVAL')
20+
local alpha = redis.call('HGET', rates_table_name .. ':' .. 'PORT', 'PORT_ALPHA')
21+
local one_minus_alpha = 1.0 - alpha
22+
local delta = tonumber(ARGV[3])
23+
24+
logit(alpha)
25+
logit(one_minus_alpha)
26+
logit(delta)
27+
28+
local initialized = redis.call('HGET', rates_table_name, 'INIT_DONE')
29+
30+
logit(initialized)
31+
32+
for i = 1, n do
33+
-- Get new COUNTERS values
34+
local in_ucast_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_UCAST_PKTS')
35+
local in_non_ucast_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS')
36+
local out_ucast_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_UCAST_PKTS')
37+
local out_non_ucast_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS')
38+
local in_octets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_OCTETS')
39+
local out_octets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_OCTETS')
40+
41+
if initialized == 'DONE' or initialized == 'COUNTERS_LAST' then
42+
-- Get old COUNTERS values
43+
local in_ucast_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_UCAST_PKTS_last')
44+
local in_non_ucast_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS_last')
45+
local out_ucast_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_UCAST_PKTS_last')
46+
local out_non_ucast_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS_last')
47+
local in_octets_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_OCTETS_last')
48+
local out_octets_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_OCTETS_last')
49+
50+
-- Calculate new rates values
51+
local rx_bps_new = (in_octets - in_octets_last)/delta
52+
local tx_bps_new = (out_octets - out_octets_last)/delta
53+
local rx_pps_new = ((in_ucast_pkts + in_non_ucast_pkts) - (in_ucast_pkts_last + in_non_ucast_pkts_last))/delta
54+
local tx_pps_new = ((out_ucast_pkts + out_non_ucast_pkts) - (out_ucast_pkts_last + out_non_ucast_pkts_last))/delta
55+
56+
if initialized == "DONE" then
57+
-- Get old rates values
58+
local rx_bps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS')
59+
local rx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS')
60+
local tx_bps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS')
61+
local tx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS')
62+
63+
-- Smooth the rates values and store them in DB
64+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS', alpha*rx_bps_new + one_minus_alpha*rx_bps_old)
65+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', alpha*rx_pps_new + one_minus_alpha*rx_pps_old)
66+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS', alpha*tx_bps_new + one_minus_alpha*tx_bps_old)
67+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS', alpha*tx_pps_new + one_minus_alpha*tx_pps_old)
68+
else
69+
-- Store unsmoothed initial rates values in DB
70+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS', rx_bps_new)
71+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', rx_pps_new)
72+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS', tx_bps_new)
73+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS', tx_pps_new)
74+
redis.call('HSET', rates_table_name, 'INIT_DONE', 'DONE')
75+
end
76+
else
77+
-- Set old COUNTERS values
78+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_UCAST_PKTS_last', in_ucast_pkts)
79+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS_last', in_non_ucast_pkts)
80+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_UCAST_PKTS_last', out_ucast_pkts)
81+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS_last', out_non_ucast_pkts)
82+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_IN_OCTETS_last', in_octets)
83+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_PORT_STAT_IF_OUT_OCTETS_last', out_octets)
84+
redis.call('HSET', rates_table_name, 'INIT_DONE', 'COUNTERS_LAST')
85+
end
86+
end
87+
88+
return logtable

orchagent/portsorch.cpp

+12-3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ extern BufferOrch *gBufferOrch;
5050
#define QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 10000
5151
#define QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000"
5252
#define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000"
53+
#define PORT_RATE_FLEX_COUNTER_POLLING_INTERVAL_MS "1000"
5354

5455

5556
static map<string, sai_port_fec_mode_t> fec_mode_map =
@@ -226,6 +227,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector<table_name_with_pri_t> &tableNames)
226227
string queueWmSha, pgWmSha;
227228
string queueWmPluginName = "watermark_queue.lua";
228229
string pgWmPluginName = "watermark_pg.lua";
230+
string portRatePluginName = "port_rates.lua";
229231

230232
try
231233
{
@@ -235,6 +237,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector<table_name_with_pri_t> &tableNames)
235237
string pgLuaScript = swss::loadLuaScript(pgWmPluginName);
236238
pgWmSha = swss::loadRedisScript(m_counter_db.get(), pgLuaScript);
237239

240+
string portRateLuaScript = swss::loadLuaScript(portRatePluginName);
241+
string portRateSha = swss::loadRedisScript(m_counter_db.get(), portRateLuaScript);
242+
238243
vector<FieldValueTuple> fieldValues;
239244
fieldValues.emplace_back(QUEUE_PLUGIN_FIELD, queueWmSha);
240245
fieldValues.emplace_back(POLL_INTERVAL_FIELD, QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS);
@@ -246,10 +251,16 @@ PortsOrch::PortsOrch(DBConnector *db, vector<table_name_with_pri_t> &tableNames)
246251
fieldValues.emplace_back(POLL_INTERVAL_FIELD, PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS);
247252
fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ_AND_CLEAR);
248253
m_flexCounterGroupTable->set(PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues);
254+
255+
fieldValues.clear();
256+
fieldValues.emplace_back(PORT_PLUGIN_FIELD, portRateSha);
257+
fieldValues.emplace_back(POLL_INTERVAL_FIELD, PORT_RATE_FLEX_COUNTER_POLLING_INTERVAL_MS);
258+
fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ);
259+
m_flexCounterGroupTable->set(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues);
249260
}
250261
catch (const runtime_error &e)
251262
{
252-
SWSS_LOG_ERROR("Watermark flex counter groups were not set successfully: %s", e.what());
263+
SWSS_LOG_ERROR("Port flex counter groups were not set successfully: %s", e.what());
253264
}
254265

255266
uint32_t i, j;
@@ -1827,15 +1838,13 @@ bool PortsOrch::initPort(const string &alias, const int index, const set<int> &l
18271838
vector<FieldValueTuple> fields;
18281839
fields.push_back(tuple);
18291840
m_counterTable->set("", fields);
1830-
18311841
// Install a flex counter for this port to track stats
18321842
std::unordered_set<std::string> counter_stats;
18331843
for (const auto& it: port_stat_ids)
18341844
{
18351845
counter_stats.emplace(sai_serialize_port_stat(it));
18361846
}
18371847
port_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, counter_stats);
1838-
18391848
PortUpdate update = { p, true };
18401849
notify(SUBJECT_TYPE_PORT_CHANGE, static_cast<void *>(&update));
18411850

orchagent/portsorch.h

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define FCS_LEN 4
1818
#define VLAN_TAG_LEN 4
1919
#define PORT_STAT_COUNTER_FLEX_COUNTER_GROUP "PORT_STAT_COUNTER"
20+
#define PORT_RATE_COUNTER_FLEX_COUNTER_GROUP "PORT_RATE_COUNTER"
2021
#define QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP "QUEUE_STAT_COUNTER"
2122
#define QUEUE_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP "QUEUE_WATERMARK_STAT_COUNTER"
2223
#define PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP "PG_WATERMARK_STAT_COUNTER"
@@ -142,6 +143,7 @@ class PortsOrch : public Orch, public Subject
142143

143144
std::string getQueueWatermarkFlexCounterTableKey(std::string s);
144145
std::string getPriorityGroupWatermarkFlexCounterTableKey(std::string s);
146+
std::string getPortRateFlexCounterTableKey(std::string s);
145147

146148
shared_ptr<DBConnector> m_counter_db;
147149
shared_ptr<DBConnector> m_flex_db;

orchagent/rif_rates.lua

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
-- KEYS - rif IDs
2+
-- ARGV[1] - counters db index
3+
-- ARGV[2] - counters table name
4+
-- ARGV[3] - poll time interval
5+
-- return log
6+
7+
local logtable = {}
8+
9+
local function logit(msg)
10+
logtable[#logtable+1] = tostring(msg)
11+
end
12+
13+
local counters_db = ARGV[1]
14+
local counters_table_name = ARGV[2]
15+
local rates_table_name = "RATES"
16+
17+
-- Get configuration
18+
redis.call('SELECT', counters_db)
19+
local smooth_interval = redis.call('HGET', rates_table_name .. ':' .. 'RIF', 'RIF_SMOOTH_INTERVAL')
20+
local alpha = redis.call('HGET', rates_table_name .. ':' .. 'RIF', 'RIF_ALPHA')
21+
local one_minus_alpha = 1.0 - alpha
22+
local delta = tonumber(ARGV[3])
23+
24+
local initialized = redis.call('HGET', rates_table_name, 'INIT_DONE')
25+
logit(initialized)
26+
27+
for i = 1, n do
28+
-- Get new COUNTERS values
29+
local in_octets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_IN_OCTETS')
30+
local in_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_IN_PACKETS')
31+
local out_octets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS')
32+
local out_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS')
33+
34+
if initialized == "DONE" or initialized == "COUNTERS_LAST" then
35+
-- Get old COUNTERS values
36+
local in_octets_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_IN_OCTETS_last')
37+
local in_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_IN_PACKETS_last')
38+
local out_octets_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS_last')
39+
local out_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS_last')
40+
41+
-- Calculate new rates values
42+
local rx_bps_new = (in_octets - in_octets_last)/delta
43+
local tx_bps_new = (out_octets - out_octets_last)/delta
44+
local rx_pps_new = (in_pkts - in_pkts_last)/delta
45+
local tx_pps_new = (out_pkts - out_pkts_last)/delta
46+
47+
if initialized == "DONE" then
48+
-- Get old rates values
49+
local rx_bps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS')
50+
local rx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS')
51+
local tx_bps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS')
52+
local tx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS')
53+
54+
-- Smooth the rates values and store them in DB
55+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS', alpha*rx_bps_new + one_minus_alpha*rx_bps_old)
56+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', alpha*rx_pps_new + one_minus_alpha*rx_pps_old)
57+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS', alpha*tx_bps_new + one_minus_alpha*tx_bps_old)
58+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS', alpha*tx_pps_new + one_minus_alpha*tx_pps_old)
59+
else
60+
-- Store unsmoothed initial rates values in DB
61+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS', rx_bps_new)
62+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', rx_pps_new)
63+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS', tx_bps_new)
64+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS', tx_pps_new)
65+
redis.call('HSET', rates_table_name, 'INIT_DONE', 'DONE')
66+
end
67+
else
68+
-- Set old COUNTERS values
69+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_IN_OCTETS_last', in_octets)
70+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_IN_PACKETS_last', in_pkts)
71+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS_last', out_octets)
72+
redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS_last', out_pkts)
73+
redis.call('HSET', rates_table_name, 'INIT_DONE', 'COUNTERS_LAST')
74+
end
75+
end
76+
77+
return logtable

0 commit comments

Comments
 (0)