Skip to content

Commit

Permalink
Add support for configuring loopback mode
Browse files Browse the repository at this point in the history
Added SWSS support for configuring loopback modes through CONFIG DB.
  • Loading branch information
royyi8 committed Apr 11, 2024
1 parent 1941023 commit 93a0467
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 0 deletions.
1 change: 1 addition & 0 deletions orchagent/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ class Port
uint32_t m_maximum_headroom = 0;
std::set<uint32_t> m_adv_speeds;
sai_port_interface_type_t m_interface_type = SAI_PORT_INTERFACE_TYPE_NONE;
sai_port_loopback_mode_t m_loopback_mode = SAI_PORT_LOOPBACK_MODE_NONE;
std::set<sai_port_interface_type_t> m_adv_interface_types;
bool m_mpls = false;
/*
Expand Down
5 changes: 5 additions & 0 deletions orchagent/port/portcnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ class PortConfig final
bool is_set = false;
} description; // Port description

struct {
sai_port_loopback_mode_t value;
bool is_set = false;
} loopback_mode;

std::string key;
std::string op;

Expand Down
43 changes: 43 additions & 0 deletions orchagent/port/porthlpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ static const std::unordered_map<std::string, Port::Role> portRoleMap =
{ PORT_ROLE_DPC, Port::Role::Dpc }
};

static const std::unordered_map<std::string, sai_port_loopback_mode_t> portLoopbackModeMap = {
{"none", SAI_PORT_LOOPBACK_MODE_NONE},
{"phy_local", SAI_PORT_LOOPBACK_MODE_PHY},
{"mac_local", SAI_PORT_LOOPBACK_MODE_MAC},
{"phy_remote", SAI_PORT_LOOPBACK_MODE_PHY_REMOTE},
{"mac_remote", SAI_PORT_LOOPBACK_MODE_MAC_REMOTE},
};

// functions ----------------------------------------------------------------------------------------------------------

template<typename T>
Expand Down Expand Up @@ -233,6 +241,11 @@ std::string PortHelper::getAdminStatusStr(const PortConfig &port) const
return this->getFieldValueStr(port, PORT_ADMIN_STATUS);
}

std::string PortHelper::getLoopbackModeStr(const PortConfig &port) const
{
return this->getFieldValueStr(port, PORT_LOOPBACK_MODE);
}

bool PortHelper::parsePortAlias(PortConfig &port, const std::string &field, const std::string &value) const
{
SWSS_LOG_ENTER();
Expand Down Expand Up @@ -749,6 +762,29 @@ bool PortHelper::parsePortDescription(PortConfig &port, const std::string &field
return true;
}

bool PortHelper::parsePortLoopbackMode(PortConfig &port, const std::string &field, const std::string &value) const
{
SWSS_LOG_ENTER();

if (value.empty())
{
SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str());
return false;
}

const auto &cit = portLoopbackModeMap.find(value);
if (cit == portLoopbackModeMap.cend())
{
SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str());
return false;
}

port.loopback_mode.value = cit->second;
port.loopback_mode.is_set = true;

return true;
}

bool PortHelper::parsePortConfig(PortConfig &port) const
{
SWSS_LOG_ENTER();
Expand Down Expand Up @@ -996,6 +1032,13 @@ bool PortHelper::parsePortConfig(PortConfig &port) const
return false;
}
}
else if (field == PORT_LOOPBACK_MODE)
{
if (!this->parsePortLoopbackMode(port, field, value))
{
return false;
}
}
else
{
SWSS_LOG_WARN("Unknown field(%s): skipping ...", field.c_str());
Expand Down
2 changes: 2 additions & 0 deletions orchagent/port/porthlpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class PortHelper final
std::string getLearnModeStr(const PortConfig &port) const;
std::string getLinkTrainingStr(const PortConfig &port) const;
std::string getAdminStatusStr(const PortConfig &port) const;
std::string getLoopbackModeStr(const PortConfig &port) const;

bool parsePortConfig(PortConfig &port) const;
bool validatePortConfig(PortConfig &port) const;
Expand Down Expand Up @@ -53,4 +54,5 @@ class PortHelper final
bool parsePortRole(PortConfig &port, const std::string &field, const std::string &value) const;
bool parsePortAdminStatus(PortConfig &port, const std::string &field, const std::string &value) const;
bool parsePortDescription(PortConfig &port, const std::string &field, const std::string &value) const;
bool parsePortLoopbackMode(PortConfig &port, const std::string &field, const std::string &value) const;
};
1 change: 1 addition & 0 deletions orchagent/port/portschema.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,4 @@
#define PORT_ROLE "role"
#define PORT_ADMIN_STATUS "admin_status"
#define PORT_DESCRIPTION "description"
#define PORT_LOOPBACK_MODE "loopback_mode"
45 changes: 45 additions & 0 deletions orchagent/portsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4301,6 +4301,30 @@ void PortsOrch::doPortTask(Consumer &consumer)
}
}

if (pCfg.loopback_mode.is_set)
{
if (p.m_loopback_mode != pCfg.loopback_mode.value)
{
auto status = setPortLoopbackMode(p.m_port_id, pCfg.loopback_mode.value);
if (status.ok())
{
SWSS_LOG_NOTICE("Set port %s loopback mode to %s",
p.m_alias.c_str(),
m_portHlpr.getLoopbackModeStr(pCfg).c_str());
p.m_loopback_mode = pCfg.loopback_mode.value;
m_portList[p.m_alias] = p;
}
else
{
SWSS_LOG_ERROR(
"Failed to set port %s loopback mode to %s",
p.m_alias.c_str(), m_portHlpr.getLoopbackModeStr(pCfg).c_str());
it = taskMap.erase(it);
continue;
}
}
}

/* create host_tx_ready field in state-db */
initHostTxReadyState(p);

Expand Down Expand Up @@ -8104,6 +8128,27 @@ void PortsOrch::getPortSerdesVal(const std::string& val_str,
}
}

ReturnCode PortsOrch::setPortLoopbackMode(sai_object_id_t id, sai_port_loopback_mode_t loopback_mode)
{
SWSS_LOG_ENTER();

sai_attribute_t attr;
attr.id = SAI_PORT_ATTR_LOOPBACK_MODE;
attr.value.u32 = loopback_mode;

sai_status_t status = sai_port_api->set_port_attribute(id, &attr);
if (status != SAI_STATUS_SUCCESS)
{
LOG_ERROR_AND_RETURN(ReturnCode(status)
<< "Failed to set loopback mode " << attr.value.u32
<< " to port pid 0x" << std::hex << id
<< ", rv: " << sai_serialize_status(status));
}

SWSS_LOG_INFO("Set loopback mode %u on port pid: %" PRIx64, attr.value.u32, id);
return ReturnCode();
}

/* Bring up/down Vlan interface associated with L3 VNI*/
bool PortsOrch::updateL3VniStatus(uint16_t vlan_id, bool isUp)
{
Expand Down
1 change: 1 addition & 0 deletions orchagent/portsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ class PortsOrch : public Orch, public Subject

ReturnCode addSendToIngressHostIf(const std::string &send_to_ingress_name);
ReturnCode removeSendToIngressHostIf();
ReturnCode setPortLoopbackMode(sai_object_id_t id, sai_port_loopback_mode_t loopback_mode);
void initGearbox();
bool initGearboxPort(Port &port);
bool getPortOperFec(const Port& port, sai_port_fec_mode_t &fec_mode) const;
Expand Down
99 changes: 99 additions & 0 deletions tests/test_port.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,105 @@ def test_PortHostTxSignalSet(self, dvs, testlog):
expected_fields = {"SAI_PORT_ATTR_HOST_TX_SIGNAL_ENABLE":"false"}
adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields)

def test_InvalidPortLoopbackMode(self, dvs, testlog):
pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0)

table_name = "PORT_TABLE"
port_name = "Ethernet0"
attribute_name = "loopback_mode"

for attribute_value in ["", "invalid"]:
# Set invalid loopback mode.
tbl = swsscommon.ProducerStateTable(pdb, table_name)
fvs = swsscommon.FieldValuePairs([(attribute_name, attribute_value)])
tbl.set(port_name, fvs)
time.sleep(1)

# Check application database.
tbl = swsscommon.Table(pdb, table_name)
(status, fvs) = tbl.get(port_name)
assert status == True
attribute_found = False
for fv in fvs:
if fv[0] == attribute_name:
assert fv[1] == attribute_value
assert attribute_found == True

# Check attribute not present in asic database.
tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT")
(status, fvs) = tbl.get(dvs.asicdb.portnamemap[port_name])
assert status == True
attribute_found = False
for fv in fvs:
if fv[0] == "SAI_PORT_ATTR_LOOPBACK_MODE":
attribute_found = True
assert attribute_found == False

def test_PortLoopbackMode(self, dvs, testlog):
pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0)
adb = swsscommon.DBConnector(1, dvs.redis_sock, 0)

table_name = "PORT_TABLE"
port_name = "Ethernet0"
attribute_name = "loopback_mode"
attribute_value = "mac_local"

# Enable loopback mode.
tbl = swsscommon.ProducerStateTable(pdb, table_name)
fvs = swsscommon.FieldValuePairs([(attribute_name, attribute_value)])
tbl.set(port_name, fvs)
time.sleep(1)

# Check application database.
tbl = swsscommon.Table(pdb, table_name)
(status, fvs) = tbl.get(port_name)
assert status == True
attribute_found = False
for fv in fvs:
if fv[0] == attribute_name:
attribute_found = True
assert fv[1] == attribute_value
assert attribute_found == True

# Check asic database.
tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT")
(status, fvs) = tbl.get(dvs.asicdb.portnamemap[port_name])
assert status == True
attribute_found = False
for fv in fvs:
if fv[0] == "SAI_PORT_ATTR_LOOPBACK_MODE":
attribute_found = True
assert fv[1] == "SAI_PORT_LOOPBACK_MODE_MAC"
assert attribute_found == True

# Disable loopback mode.
attribute_value = "none"
tbl = swsscommon.ProducerStateTable(pdb, table_name)
fvs = swsscommon.FieldValuePairs([(attribute_name, attribute_value)])
tbl.set(port_name, fvs)
time.sleep(1)

# Check application database.
tbl = swsscommon.Table(pdb, table_name)
(status, fvs) = tbl.get(port_name)
assert status == True
attribute_found = False
for fv in fvs:
if fv[0] == attribute_name:
attribute_found = True
assert fv[1] == attribute_value
assert attribute_found == True

# Check asic database.
tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT")
(status, fvs) = tbl.get(dvs.asicdb.portnamemap[port_name])
assert status == True
attribute_found = False
for fv in fvs:
if fv[0] == "SAI_PORT_ATTR_LOOPBACK_MODE":
attribute_found = True
assert fv[1] == "SAI_PORT_LOOPBACK_MODE_NONE"
assert attribute_found == True

# Add Dummy always-pass test at end as workaroud
# for issue when Flaky fail on final test it invokes module tear-down before retrying
Expand Down

0 comments on commit 93a0467

Please sign in to comment.