Skip to content
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
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 @@ -3097,6 +3097,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 @@ -5354,7 +5378,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 @@ -5363,9 +5387,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 @@ -5386,6 +5415,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 @@ -7625,6 +7668,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 @@ -504,6 +504,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 @@ -2038,6 +2105,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