Skip to content

Commit

Permalink
Add port flap count and last flap timestamp to APPL_DB (sonic-net#3052)
Browse files Browse the repository at this point in the history
* Add port flap count and last flap timestamp
  • Loading branch information
prgeor authored Mar 12, 2024
1 parent e9931f3 commit 0c62091
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 3 deletions.
1 change: 1 addition & 0 deletions orchagent/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ class Port
uint32_t m_nat_zone_id = 0;
uint32_t m_vnid = VNID_NONE;
uint32_t m_fdb_count = 0;
uint64_t m_flap_count = 0;
uint32_t m_up_member_count = 0;
uint32_t m_maximum_headroom = 0;
std::set<uint32_t> m_adv_speeds;
Expand Down
48 changes: 46 additions & 2 deletions orchagent/portsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3107,6 +3107,30 @@ bool PortsOrch::removeVlanHostIntf(Port vl)
return true;
}

void PortsOrch::updateDbPortFlapCount(Port& port, sai_port_oper_status_t pstatus)
{
SWSS_LOG_ENTER();

++port.m_flap_count;
vector<FieldValueTuple> tuples;
FieldValueTuple tuple("flap_count", std::to_string(port.m_flap_count));
tuples.push_back(tuple);

auto now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
if (pstatus == SAI_PORT_OPER_STATUS_DOWN)
{
FieldValueTuple tuple("last_down_time", std::ctime(&now_c));
tuples.push_back(tuple);
}
else if (pstatus == SAI_PORT_OPER_STATUS_UP)
{
FieldValueTuple tuple("last_up_time", std::ctime(&now_c));
tuples.push_back(tuple);
}
m_portTable->set(port.m_alias, tuples);
}

void PortsOrch::updateDbPortOperStatus(const Port& port, sai_port_oper_status_t status) const
{
SWSS_LOG_ENTER();
Expand Down Expand Up @@ -5315,7 +5339,7 @@ bool PortsOrch::initializePort(Port &port)
/* Check warm start states */
vector<FieldValueTuple> tuples;
bool exist = m_portTable->get(port.m_alias, tuples);
string operStatus;
string operStatus, flapCount = "0";
if (exist)
{
for (auto i : tuples)
Expand All @@ -5324,9 +5348,14 @@ bool PortsOrch::initializePort(Port &port)
{
operStatus = fvValue(i);
}

if (fvField(i) == "flap_count")
{
flapCount = fvValue(i);
}
}
}
SWSS_LOG_DEBUG("initializePort %s with oper %s", port.m_alias.c_str(), operStatus.c_str());
SWSS_LOG_INFO("Port %s with oper %s flap_count=%s", port.m_alias.c_str(), operStatus.c_str(), flapCount.c_str());

/**
* Create database port oper status as DOWN if attr missing
Expand All @@ -5347,6 +5376,20 @@ bool PortsOrch::initializePort(Port &port)
port.m_oper_status = SAI_PORT_OPER_STATUS_DOWN;
}

// initalize port flap count
if (!flapCount.empty())
{
try
{
port.m_flap_count = stoull(flapCount);
m_portTable->hset(port.m_alias, "flap_count", flapCount);
}
catch (const std::exception &e)
{
SWSS_LOG_ERROR("Failed to get port (%s) flap_count: %s", port.m_alias.c_str(), e.what());
}
}

/* initialize port admin status */
if (!getPortAdminStatus(port.m_port_id, port.m_admin_state_up))
{
Expand Down Expand Up @@ -7580,6 +7623,7 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status)
if (port.m_type == Port::PHY)
{
updateDbPortOperStatus(port, status);
updateDbPortFlapCount(port, status);
updateGearboxPortOperStatus(port);

/* Refresh the port states and reschedule the poller tasks */
Expand Down
1 change: 1 addition & 0 deletions orchagent/portsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class PortsOrch : public Orch, public Subject

bool setHostIntfsOperStatus(const Port& port, bool up) const;
void updateDbPortOperStatus(const Port& port, sai_port_oper_status_t status) const;
void updateDbPortFlapCount(Port& port, sai_port_oper_status_t pstatus);

bool createVlanHostIntf(Port& vl, string hostif_name);
bool removeVlanHostIntf(Port vl);
Expand Down
68 changes: 68 additions & 0 deletions tests/mock_tests/portsorch_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,73 @@ namespace portsorch_test
}

};

/*
* Test port flap count
*/
TEST_F(PortsOrchTest, PortFlapCount)
{
Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME);

// Get SAI default ports to populate DB
auto ports = ut_helper::getInitialSaiPorts();

// Populate port table with SAI ports
for (const auto &it : ports)
{
portTable.set(it.first, it.second);
}

// Set PortConfigDone, PortInitDone
portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } });
portTable.set("PortInitDone", { { "lanes", "0" } });

// refill consumer
gPortsOrch->addExistingData(&portTable);
// Apply configuration : create ports
static_cast<Orch *>(gPortsOrch)->doTask();

// Get first port, expect the oper status is not UP
Port port;
gPortsOrch->getPort("Ethernet0", port);
ASSERT_TRUE(port.m_oper_status != SAI_PORT_OPER_STATUS_UP);
ASSERT_TRUE(port.m_flap_count == 0);

auto exec = static_cast<Notifier *>(gPortsOrch->getExecutor("PORT_STATUS_NOTIFICATIONS"));
auto consumer = exec->getNotificationConsumer();

// mock a redis reply for notification, it notifies that Ehernet0 is going to up
for (uint32_t count=0; count < 5; count++) {
sai_port_oper_status_t oper_status = (count % 2 == 0) ? SAI_PORT_OPER_STATUS_UP : SAI_PORT_OPER_STATUS_DOWN;
mockReply = (redisReply *)calloc(sizeof(redisReply), 1);
mockReply->type = REDIS_REPLY_ARRAY;
mockReply->elements = 3; // REDIS_PUBLISH_MESSAGE_ELEMNTS
mockReply->element = (redisReply **)calloc(sizeof(redisReply *), mockReply->elements);
mockReply->element[2] = (redisReply *)calloc(sizeof(redisReply), 1);
mockReply->element[2]->type = REDIS_REPLY_STRING;
sai_port_oper_status_notification_t port_oper_status;
port_oper_status.port_state = oper_status;
port_oper_status.port_id = port.m_port_id;
std::string data = sai_serialize_port_oper_status_ntf(1, &port_oper_status);
std::vector<FieldValueTuple> notifyValues;
FieldValueTuple opdata("port_state_change", data);
notifyValues.push_back(opdata);
std::string msg = swss::JSon::buildJson(notifyValues);
mockReply->element[2]->str = (char*)calloc(1, msg.length() + 1);
memcpy(mockReply->element[2]->str, msg.c_str(), msg.length());

// trigger the notification
consumer->readData();
gPortsOrch->doTask(*consumer);
mockReply = nullptr;

gPortsOrch->getPort("Ethernet0", port);
ASSERT_TRUE(port.m_oper_status == oper_status);
ASSERT_TRUE(port.m_flap_count == count+1);
}

cleanupPorts(gPortsOrch);
}

TEST_F(PortsOrchTest, PortBulkCreateRemove)
{
Expand Down Expand Up @@ -1956,6 +2023,7 @@ namespace portsorch_test

gPortsOrch->getPort("Ethernet0", port);
ASSERT_TRUE(port.m_oper_status == SAI_PORT_OPER_STATUS_UP);
ASSERT_TRUE(port.m_flap_count == 1);

std::vector<FieldValueTuple> values;
portTable.get("Ethernet0", values);
Expand Down
2 changes: 1 addition & 1 deletion tests/test_warm_reboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ def test_swss_port_state_syncup(self, dvs, testlog):
orchStateCount += 1;

# Only WARM_RESTART_TABLE|orchagent state=reconciled operation may exist after port oper status change.
assert orchStateCount == 1
assert orchStateCount == 2

#clean up arp
dvs.runcmd("arp -d 10.0.0.1")
Expand Down

0 comments on commit 0c62091

Please sign in to comment.