From f448a22ac38850889b07d69bfe601a5eaf575c92 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Tue, 10 Apr 2018 02:52:44 -0700 Subject: [PATCH 01/27] FdbOrch getPort failed because fdb_entry have no switch_id --- orchagent/fdborch.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 64dea1b23d..d55d1a724e 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -168,6 +168,7 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) } sai_fdb_entry_t entry; + entry.switch_id = gSwitchId; memcpy(entry.mac_address, mac.getMac(), sizeof(sai_mac_t)); entry.bv_id = port.m_vlan_info.vlan_oid; @@ -423,6 +424,7 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) sai_status_t status; sai_fdb_entry_t fdb_entry; + fdb_entry.switch_id = gSwitchId; memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); fdb_entry.bv_id = entry.bv_id; From 7f81135c00ddd1e83461325f2f0dd4d909627b58 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Mon, 16 Apr 2018 00:08:07 -0700 Subject: [PATCH 02/27] [crmorch]: neighbor used counter increased twice --- orchagent/neighorch.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 4ddedaa080..961fc1f414 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -395,16 +395,16 @@ bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) } m_intfsOrch->decreaseRouterIntfsRefCount(alias); - return false; - } + if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEIGHBOR); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEIGHBOR); + } - if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) - { - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEIGHBOR); - } - else - { - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEIGHBOR); + return false; } } else From 5c2af28a944d36303ff7b174fe813303b27c44c8 Mon Sep 17 00:00:00 2001 From: "leo.li" Date: Tue, 22 May 2018 02:01:53 -0700 Subject: [PATCH 03/27] [Portsorch]: Add lag-name and oid map to counter table COUNTERS_PORT_NAME_MAP --- orchagent/portsorch.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index a89760fc93..b4fa69c785 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2232,6 +2232,12 @@ bool PortsOrch::addLag(string lag_alias) lag.m_members = set(); m_portList[lag_alias] = lag; + /* Add lag name map to counter table */ + FieldValueTuple tuple(lag_alias, sai_serialize_object_id(lag_id)); + vector fields; + fields.push_back(tuple); + m_counterTable->set("", fields); + return true; } From 53f2663e96c94f61fd3c6a4a0ac226b58c5714b4 Mon Sep 17 00:00:00 2001 From: "leo.li" Date: Wed, 23 May 2018 20:27:22 -0700 Subject: [PATCH 04/27] [Portsorch]:Add operation to remove lag-name and oid map from counter table COUNTERS_PORT_NAME_MAP --- orchagent/portsorch.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index b4fa69c785..a32333bc30 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2266,6 +2266,8 @@ bool PortsOrch::removeLag(Port lag) SWSS_LOG_NOTICE("Remove LAG %s lid:%lx", lag.m_alias.c_str(), lag.m_lag_id); + m_counterTable->del(lag.m_alias); + m_portList.erase(lag.m_alias); return true; From 03c047203c8c72b11c8598ad9c4a656bea68bd4e Mon Sep 17 00:00:00 2001 From: "leo.li" Date: Mon, 20 Aug 2018 23:33:32 -0700 Subject: [PATCH 05/27] [Pfc-wd]: Add pfc-wd support for nephos --- orchagent/Makefile.am | 1 + orchagent/orch.h | 1 + orchagent/orchdaemon.cpp | 3 +- orchagent/pfc_detect_nephos.lua | 99 +++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 orchagent/pfc_detect_nephos.lua diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 24249c5ff2..78e4abfd86 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -8,6 +8,7 @@ dist_swss_DATA = \ pfc_detect_mellanox.lua \ pfc_detect_broadcom.lua \ pfc_detect_barefoot.lua \ + pfc_detect_nephos.lua \ pfc_restore.lua bin_PROGRAMS = orchagent routeresync diff --git a/orchagent/orch.h b/orchagent/orch.h index dfbadb62bf..17218c05cb 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -35,6 +35,7 @@ const char config_db_key_delimiter = '|'; #define BRCM_PLATFORM_SUBSTRING "broadcom" #define BFN_PLATFORM_SUBSTRING "barefoot" #define VS_PLATFORM_SUBSTRING "vs" +#define NPS_PLATFORM_SUBSTRING "nephos" #define CONFIGDB_KEY_SEPARATOR "|" #define DEFAULT_KEY_SEPARATOR ":" diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 63de2326f2..19dfa15272 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -180,7 +180,8 @@ bool OrchDaemon::init() CFG_PFC_WD_TABLE_NAME }; - if (platform == MLNX_PLATFORM_SUBSTRING) + if (platform == MLNX_PLATFORM_SUBSTRING + || platform == NPS_PLATFORM_SUBSTRING) { static const vector portStatIds = diff --git a/orchagent/pfc_detect_nephos.lua b/orchagent/pfc_detect_nephos.lua new file mode 100644 index 0000000000..b1b38e6e5e --- /dev/null +++ b/orchagent/pfc_detect_nephos.lua @@ -0,0 +1,99 @@ +-- KEYS - queue IDs +-- ARGV[1] - counters db index +-- ARGV[2] - counters table name +-- ARGV[3] - poll time interval +-- return queue Ids that satisfy criteria + +local counters_db = ARGV[1] +local counters_table_name = ARGV[2] +local poll_time = tonumber(ARGV[3]) + +local rets = {} + +redis.call('SELECT', counters_db) + +-- Iterate through each queue +local n = table.getn(KEYS) +for i = n, 1, -1 do + local counter_keys = redis.call('HKEYS', counters_table_name .. ':' .. KEYS[i]) + local counter_num = 0 + local old_counter_num = 0 + local is_deadlock = false + local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') + local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') + local big_red_switch_mode = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'BIG_RED_SWITCH_MODE') + if not big_red_switch_mode and (pfc_wd_status == 'operational' or pfc_wd_action == 'alert') then + local detection_time = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME') + if detection_time then + detection_time = tonumber(detection_time) + local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') + if not time_left then + time_left = detection_time + else + time_left = tonumber(time_left) + end + + local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) + local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) + local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' + local pfc_duration_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PAUSE_DURATION' + + -- Get all counters + local occupancy_bytes = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES') + local packets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS') + local pfc_rx_packets = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key) + local pfc_duration = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key) + + if occupancy_bytes and packets and pfc_rx_packets and pfc_duration then + occupancy_bytes = tonumber(occupancy_bytes) + packets = tonumber(packets) + pfc_rx_packets = tonumber(pfc_rx_packets) + pfc_duration = tonumber(pfc_duration) + + local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') + local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') + local pfc_duration_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') + -- DEBUG CODE START. Uncomment to enable + local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') + -- DEBUG CODE END. + + -- If this is not a first run, then we have last values available + if packets_last and pfc_rx_packets_last and pfc_duration_last then + packets_last = tonumber(packets_last) + pfc_rx_packets_last = tonumber(pfc_rx_packets_last) + pfc_duration_last = tonumber(pfc_duration_last) + + -- Check actual condition of queue being in PFC storm + if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or + -- DEBUG CODE START. Uncomment to enable + (debug_storm == "enabled") or + -- DEBUG CODE END. + (occupancy_bytes == 0 and packets - packets_last == 0 and (pfc_duration - pfc_duration_last) > poll_time * 0.8) then + if time_left <= poll_time then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') + is_deadlock = true + time_left = detection_time + else + time_left = time_left - poll_time + end + else + if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') + end + time_left = detection_time + end + end + + -- Save values for next run + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) + redis.call('HDEL', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last', pfc_duration) + end + end + end +end + +return rets + From 88e3f4f16158499bfac0bc2e2c8ac9d6933c5d88 Mon Sep 17 00:00:00 2001 From: leoli-nps <38268885+leoli-nps@users.noreply.github.com> Date: Thu, 10 Jan 2019 13:59:55 +0800 Subject: [PATCH 06/27] Update Makefile.am Delete the extra line left in the conflict resolution --- orchagent/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 1c070fee73..f8839fb18c 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -9,7 +9,6 @@ dist_swss_DATA = \ pfc_detect_broadcom.lua \ pfc_detect_barefoot.lua \ pfc_detect_nephos.lua \ - pfc_restore.lua pfc_restore.lua \ watermark_queue.lua \ watermark_pg.lua From 91fdc69969143f6beb348250df707f2e9f94f1e6 Mon Sep 17 00:00:00 2001 From: "leo.li" Date: Mon, 18 Mar 2019 00:50:05 -0700 Subject: [PATCH 07/27] [Pfc-wd]: Remove pfc-wd code for nephos from this branch --- orchagent/Makefile.am | 1 - orchagent/orch.h | 1 - orchagent/orchdaemon.cpp | 3 +- orchagent/pfc_detect_nephos.lua | 99 --------------------------------- 4 files changed, 1 insertion(+), 103 deletions(-) delete mode 100644 orchagent/pfc_detect_nephos.lua diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index f8839fb18c..0b7f9265fa 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -8,7 +8,6 @@ dist_swss_DATA = \ pfc_detect_mellanox.lua \ pfc_detect_broadcom.lua \ pfc_detect_barefoot.lua \ - pfc_detect_nephos.lua \ pfc_restore.lua \ watermark_queue.lua \ watermark_pg.lua diff --git a/orchagent/orch.h b/orchagent/orch.h index 83e816a148..f14332940f 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -36,7 +36,6 @@ const char state_db_key_delimiter = '|'; #define BRCM_PLATFORM_SUBSTRING "broadcom" #define BFN_PLATFORM_SUBSTRING "barefoot" #define VS_PLATFORM_SUBSTRING "vs" -#define NPS_PLATFORM_SUBSTRING "nephos" #define CONFIGDB_KEY_SEPARATOR "|" #define DEFAULT_KEY_SEPARATOR ":" diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 65bb625473..50c51104a9 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -209,8 +209,7 @@ bool OrchDaemon::init() CFG_PFC_WD_TABLE_NAME }; - if (platform == MLNX_PLATFORM_SUBSTRING - || platform == NPS_PLATFORM_SUBSTRING) + if (platform == MLNX_PLATFORM_SUBSTRING) { static const vector portStatIds = diff --git a/orchagent/pfc_detect_nephos.lua b/orchagent/pfc_detect_nephos.lua deleted file mode 100644 index b1b38e6e5e..0000000000 --- a/orchagent/pfc_detect_nephos.lua +++ /dev/null @@ -1,99 +0,0 @@ --- KEYS - queue IDs --- ARGV[1] - counters db index --- ARGV[2] - counters table name --- ARGV[3] - poll time interval --- return queue Ids that satisfy criteria - -local counters_db = ARGV[1] -local counters_table_name = ARGV[2] -local poll_time = tonumber(ARGV[3]) - -local rets = {} - -redis.call('SELECT', counters_db) - --- Iterate through each queue -local n = table.getn(KEYS) -for i = n, 1, -1 do - local counter_keys = redis.call('HKEYS', counters_table_name .. ':' .. KEYS[i]) - local counter_num = 0 - local old_counter_num = 0 - local is_deadlock = false - local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') - local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') - local big_red_switch_mode = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'BIG_RED_SWITCH_MODE') - if not big_red_switch_mode and (pfc_wd_status == 'operational' or pfc_wd_action == 'alert') then - local detection_time = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME') - if detection_time then - detection_time = tonumber(detection_time) - local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') - if not time_left then - time_left = detection_time - else - time_left = tonumber(time_left) - end - - local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) - local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) - local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' - local pfc_duration_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PAUSE_DURATION' - - -- Get all counters - local occupancy_bytes = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES') - local packets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS') - local pfc_rx_packets = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key) - local pfc_duration = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key) - - if occupancy_bytes and packets and pfc_rx_packets and pfc_duration then - occupancy_bytes = tonumber(occupancy_bytes) - packets = tonumber(packets) - pfc_rx_packets = tonumber(pfc_rx_packets) - pfc_duration = tonumber(pfc_duration) - - local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') - local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') - local pfc_duration_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') - -- DEBUG CODE START. Uncomment to enable - local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') - -- DEBUG CODE END. - - -- If this is not a first run, then we have last values available - if packets_last and pfc_rx_packets_last and pfc_duration_last then - packets_last = tonumber(packets_last) - pfc_rx_packets_last = tonumber(pfc_rx_packets_last) - pfc_duration_last = tonumber(pfc_duration_last) - - -- Check actual condition of queue being in PFC storm - if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or - -- DEBUG CODE START. Uncomment to enable - (debug_storm == "enabled") or - -- DEBUG CODE END. - (occupancy_bytes == 0 and packets - packets_last == 0 and (pfc_duration - pfc_duration_last) > poll_time * 0.8) then - if time_left <= poll_time then - redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') - is_deadlock = true - time_left = detection_time - else - time_left = time_left - poll_time - end - else - if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then - redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') - end - time_left = detection_time - end - end - - -- Save values for next run - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) - redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) - redis.call('HDEL', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') - redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last', pfc_duration) - end - end - end -end - -return rets - From e772a12c51d4587ad17c92e8df7a88c21661686e Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Mon, 3 Jun 2019 01:45:42 -0700 Subject: [PATCH 08/27] Support VRF --- cfgmgr/intfmgr.cpp | 50 ++++- cfgmgr/intfmgr.h | 2 + cfgmgr/nbrmgr.cpp | 23 +-- cfgmgr/vrfmgr.cpp | 17 ++ cfgmgr/vrfmgr.h | 1 + fpmsyncd/routesync.cpp | 51 ++++- orchagent/aclorch.cpp | 34 ++-- orchagent/intfsorch.cpp | 127 +++++-------- orchagent/intfsorch.h | 8 +- orchagent/mirrororch.cpp | 14 +- orchagent/mirrororch.h | 2 +- orchagent/neighorch.cpp | 97 ++++++---- orchagent/neighorch.h | 65 +++++-- orchagent/orchdaemon.cpp | 2 +- orchagent/routeorch.cpp | 394 +++++++++++++++++++++++++-------------- orchagent/routeorch.h | 180 +++++++++++++++--- orchagent/vnetorch.cpp | 16 +- orchagent/vrforch.cpp | 12 +- orchagent/vrforch.h | 51 ++++- 19 files changed, 759 insertions(+), 387 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index cd509b3bbd..52f79a66f6 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -15,6 +15,7 @@ using namespace swss; #define LAG_PREFIX "PortChannel" #define LOOPBACK_PREFIX "Loopback" #define VNET_PREFIX "Vnet" +#define VRF_PREFIX "Vrf" IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), @@ -66,6 +67,30 @@ void IntfMgr::setIntfVrf(const string &alias, const string vrfName) EXEC_WITH_ERROR_THROW(cmd.str(), res); } +int IntfMgr::getIntfIpCount(const string &alias) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " address show " << alias << " | grep inet | grep -v 'inet6 fe80:' | wc -l"; + EXEC_WITH_ERROR_THROW(cmd.str(), res); + + return std::stoi(res); +} + +bool IntfMgr::isIntfGeneralDone(const string &alias) +{ + vector temp; + + if (m_stateIntfTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Intf %s is ready", alias.c_str()); + return true; + } + + return false; +} + bool IntfMgr::isIntfStateOk(const string &alias) { vector temp; @@ -94,6 +119,14 @@ bool IntfMgr::isIntfStateOk(const string &alias) return true; } } + else if (!alias.compare(0, strlen(VRF_PREFIX), VRF_PREFIX)) + { + if (m_stateVrfTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Vrf %s is ready", alias.c_str()); + return true; + } + } else if (m_statePortTable.get(alias, temp)) { SWSS_LOG_DEBUG("Port %s is ready", alias.c_str()); @@ -144,6 +177,11 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, // Set Interface VRF except for lo if (!is_lo) { + /* if intfGeneral has been done then skip */ + if (isIntfGeneralDone(alias)) + { + return true; + } if (!vrf_name.empty()) { setIntfVrf(alias, vrf_name); @@ -154,12 +192,19 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { m_appIntfTableProducer.set("lo", data); } + m_stateIntfTable.hset(alias, "state", "ok"); } else if (op == DEL_COMMAND) { // Set Interface VRF except for lo if (!is_lo) { + /* make sure all ip addresses associated with interface are removed, otherwise these ip address would + be set with global vrf and it may cause ip address confliction. */ + if (getIntfIpCount(alias)) + { + return false; + } setIntfVrf(alias, ""); m_appIntfTableProducer.del(alias); } @@ -167,6 +212,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { m_appIntfTableProducer.del("lo"); } + m_stateIntfTable.del(alias); } else { @@ -190,10 +236,10 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, if (op == SET_COMMAND) { /* - * Don't proceed if port/LAG/VLAN is not ready yet. + * Don't proceed if port/LAG/VLAN and intfGeneral are not ready yet. * The pending task will be checked periodically and retried. */ - if (!isIntfStateOk(alias)) + if (!isIntfStateOk(alias) || !isIntfGeneralDone(alias)) { SWSS_LOG_DEBUG("Interface is not ready, skipping %s", alias.c_str()); return false; diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index d10b5d8b4c..82c000e983 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -27,6 +27,8 @@ class IntfMgr : public Orch bool doIntfAddrTask(const vector& keys, const vector& data, const string& op); void doTask(Consumer &consumer); bool isIntfStateOk(const string &alias); + bool isIntfGeneralDone(const string &alias); + int getIntfIpCount(const string &alias); }; } diff --git a/cfgmgr/nbrmgr.cpp b/cfgmgr/nbrmgr.cpp index d2ca7ebc96..ca914ade9e 100644 --- a/cfgmgr/nbrmgr.cpp +++ b/cfgmgr/nbrmgr.cpp @@ -13,9 +13,6 @@ using namespace swss; -#define VLAN_PREFIX "Vlan" -#define LAG_PREFIX "PortChannel" - static bool send_message(struct nl_sock *sk, struct nl_msg *msg) { bool rc = false; @@ -66,25 +63,9 @@ bool NbrMgr::isIntfStateOk(const string &alias) { vector temp; - if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) - { - if (m_stateVlanTable.get(alias, temp)) - { - SWSS_LOG_DEBUG("Vlan %s is ready", alias.c_str()); - return true; - } - } - else if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) - { - if (m_stateLagTable.get(alias, temp)) - { - SWSS_LOG_DEBUG("Lag %s is ready", alias.c_str()); - return true; - } - } - else if (m_statePortTable.get(alias, temp)) + if (m_stateIntfTable.get(alias, temp)) { - SWSS_LOG_DEBUG("Port %s is ready", alias.c_str()); + SWSS_LOG_DEBUG("Intf %s is ready", alias.c_str()); return true; } diff --git a/cfgmgr/vrfmgr.cpp b/cfgmgr/vrfmgr.cpp index c8edf21512..c0dc2569f6 100644 --- a/cfgmgr/vrfmgr.cpp +++ b/cfgmgr/vrfmgr.cpp @@ -139,6 +139,17 @@ bool VrfMgr::setLink(const string& vrfName) return true; } +int VrfMgr::getVrfIntfCount(const string& vrfName) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " link show " << " | grep 'master " << vrfName << "' | wc -l"; + EXEC_WITH_ERROR_THROW(cmd.str(), res); + + return std::stoi(res); +} + void VrfMgr::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -173,6 +184,12 @@ void VrfMgr::doTask(Consumer &consumer) } else if (op == DEL_COMMAND) { + if (getVrfIntfCount(vrfName)) + { + it++; + continue; + } + if (!delLink(vrfName)) { SWSS_LOG_ERROR("Failed to remove vrf netdev %s", vrfName.c_str()); diff --git a/cfgmgr/vrfmgr.h b/cfgmgr/vrfmgr.h index ab20b7ffd3..d2e089110d 100644 --- a/cfgmgr/vrfmgr.h +++ b/cfgmgr/vrfmgr.h @@ -21,6 +21,7 @@ class VrfMgr : public Orch private: bool delLink(const string& vrfName); bool setLink(const string& vrfName); + int getVrfIntfCount(const string& vrfName); void recycleTable(uint32_t table); uint32_t getFreeTable(void); void handleVnetConfigSet(KeyOpFieldsValuesTuple &t); diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index 8f05338f2d..1b3cd8f8db 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -16,6 +16,7 @@ using namespace swss; #define VXLAN_IF_NAME_PREFIX "Brvxlan" #define VNET_PREFIX "Vnet" +#define VRF_PREFIX "Vrf" RouteSync::RouteSync(RedisPipeline *pipeline) : m_routeTable(pipeline, APP_ROUTE_TABLE_NAME, true), @@ -70,10 +71,40 @@ void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj) { struct rtnl_route *route_obj = (struct rtnl_route *)obj; struct nl_addr *dip; - char destipprefix[MAX_ADDR_SIZE + 1] = {0}; + char destipprefix[IFNAMSIZ + MAX_ADDR_SIZE + 2] = {0}; + + /* + * Here vrf_index is not real table id but vrf_id in zebra. + * It is vrf interface index in kernel for vrf route and VRF_DEFAULT(0) for global route. + * Now zebra fpm only fill in a uchar, ifindex limited to 255. + */ + unsigned int vrf_index = rtnl_route_get_table(route_obj); + if (vrf_index) + { + if (!getIfName(vrf_index, destipprefix, IFNAMSIZ)) + { + SWSS_LOG_ERROR("Fail to get the VRF name (ifindex %u)", vrf_index); + return; + } + /* + * Now vrf device name is required to start with VRF_PREFIX, + * it is difficult to split vrf_name:ipv6_addr. + */ + if (memcmp(destipprefix, VRF_PREFIX, strlen(VRF_PREFIX))) + { + SWSS_LOG_ERROR("Invalid VRF name %s (ifindex %u)", destipprefix, vrf_index); + return; + } + destipprefix[strlen(destipprefix)] = ':'; + } dip = rtnl_route_get_dst(route_obj); - nl_addr2str(dip, destipprefix, MAX_ADDR_SIZE); + nl_addr2str(dip, destipprefix + strlen(destipprefix), MAX_ADDR_SIZE); + /* Full mask route append prefix length, or else resync cannot match. */ + if (nl_addr_get_prefixlen(dip) == (8 * nl_addr_get_len(dip))) + { + snprintf(destipprefix + strlen(destipprefix), sizeof(destipprefix) - strlen(destipprefix), "/%u", nl_addr_get_prefixlen(dip)); + } SWSS_LOG_DEBUG("Receive new route message dest ip prefix: %s", destipprefix); /* @@ -186,6 +217,11 @@ void RouteSync::onVnetRouteMsg(int nlmsg_type, struct nl_object *obj, string vne struct nl_addr *dip = rtnl_route_get_dst(route_obj); char destipprefix[MAX_ADDR_SIZE + 1] = {0}; nl_addr2str(dip, destipprefix, MAX_ADDR_SIZE); + /* Full mask route append prefix length, or else resync cannot match. */ + if (nl_addr_get_prefixlen(dip) == (8 * nl_addr_get_len(dip))) + { + snprintf(destipprefix + strlen(destipprefix), sizeof(destipprefix) - strlen(destipprefix), "/%u", nl_addr_get_prefixlen(dip)); + } string vnet_dip = vnet + string(":") + destipprefix; SWSS_LOG_DEBUG("Receive new vnet route message %s", vnet_dip.c_str()); @@ -325,6 +361,17 @@ string RouteSync::getNextHopGw(struct rtnl_route *route_obj) nl_addr2str(addr, gw_ip, MAX_ADDR_SIZE); result += gw_ip; } + else + { + if (rtnl_route_get_family(route_obj) == AF_INET) + { + result += "0.0.0.0"; + } + else + { + result += "::"; + } + } if (i + 1 < rtnl_route_get_nnexthops(route_obj)) { diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 44f827854b..e02231338b 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -499,12 +499,12 @@ void AclRule::decreaseNextHopRefCount() { if (!m_redirect_target_next_hop.empty()) { - m_pAclOrch->m_neighOrch->decreaseNextHopRefCount(IpAddress(m_redirect_target_next_hop)); + m_pAclOrch->m_neighOrch->decreaseNextHopRefCount(NextHopKey(m_redirect_target_next_hop)); m_redirect_target_next_hop.clear(); } if (!m_redirect_target_next_hop_group.empty()) { - IpAddresses target = IpAddresses(m_redirect_target_next_hop_group); + NextHopGroupKey target = NextHopGroupKey(m_redirect_target_next_hop_group); m_pAclOrch->m_routeOrch->decreaseNextHopRefCount(target); // remove next hop group in case it's not used by anything else if (m_pAclOrch->m_routeOrch->isRefCounterZero(target)) @@ -807,44 +807,44 @@ sai_object_id_t AclRuleL3::getRedirectObjectId(const string& redirect_value) } } - // Try to parse nexthop ip address + // Try to parse nexthop ip address and interface name try { - IpAddress ip(target); - if (!m_pAclOrch->m_neighOrch->hasNextHop(ip)) + NextHopKey nh(target); + if (!m_pAclOrch->m_neighOrch->hasNextHop(nh)) { - SWSS_LOG_ERROR("ACL Redirect action target next hop ip: '%s' doesn't exist on the switch", ip.to_string().c_str()); + SWSS_LOG_ERROR("ACL Redirect action target next hop ip: '%s' doesn't exist on the switch", nh.to_string().c_str()); return SAI_NULL_OBJECT_ID; } m_redirect_target_next_hop = target; - m_pAclOrch->m_neighOrch->increaseNextHopRefCount(ip); - return m_pAclOrch->m_neighOrch->getNextHopId(ip); + m_pAclOrch->m_neighOrch->increaseNextHopRefCount(nh); + return m_pAclOrch->m_neighOrch->getNextHopId(nh); } catch (...) { // no error, just try next variant } - // try to parse nh group ip addresses + // try to parse nh group the set of try { - IpAddresses ips(target); - if (!m_pAclOrch->m_routeOrch->hasNextHopGroup(ips)) + NextHopGroupKey nhg(target); + if (!m_pAclOrch->m_routeOrch->hasNextHopGroup(nhg)) { - SWSS_LOG_INFO("ACL Redirect action target next hop group: '%s' doesn't exist on the switch. Creating it.", ips.to_string().c_str()); + SWSS_LOG_INFO("ACL Redirect action target next hop group: '%s' doesn't exist on the switch. Creating it.", nhg.to_string().c_str()); - if (!m_pAclOrch->m_routeOrch->addNextHopGroup(ips)) + if (!m_pAclOrch->m_routeOrch->addNextHopGroup(nhg)) { - SWSS_LOG_ERROR("Can't create required target next hop group '%s'", ips.to_string().c_str()); + SWSS_LOG_ERROR("Can't create required target next hop group '%s'", nhg.to_string().c_str()); return SAI_NULL_OBJECT_ID; } - SWSS_LOG_DEBUG("Created acl redirect target next hop group '%s'", ips.to_string().c_str()); + SWSS_LOG_DEBUG("Created acl redirect target next hop group '%s'", nhg.to_string().c_str()); } m_redirect_target_next_hop_group = target; - m_pAclOrch->m_routeOrch->increaseNextHopRefCount(ips); - return m_pAclOrch->m_routeOrch->getNextHopGroupId(ips); + m_pAclOrch->m_routeOrch->increaseNextHopRefCount(nhg); + return m_pAclOrch->m_routeOrch->getNextHopGroupId(nhg); } catch (...) { diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 467f3aa8ff..324c109ced 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -78,10 +78,44 @@ sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias) { Port port; gPortsOrch->getPort(alias, port); - assert(port.m_rif_id); return port.m_rif_id; } +sai_object_id_t IntfsOrch::getVRFid(const string &alias) +{ + Port port; + gPortsOrch->getPort(alias, port); + return port.m_vr_id; +} + +bool IntfsOrch::isPrefixSubnet(const IpPrefix &ip_prefix, const string& alias) +{ + if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) + return false; + for (auto &prefixIt: m_syncdIntfses[alias].ip_addresses) + { + if (prefixIt.getSubnet() == ip_prefix) + return true; + } + return false; +} + +string IntfsOrch::getRouterIntfsAlias(const IpAddress &ip, sai_object_id_t vrf_id) +{ + for (const auto &it_intfs: m_syncdIntfses) + { + if (getVRFid(it_intfs.first) != vrf_id) + continue; + + for (const auto &prefixIt: it_intfs.second.ip_addresses) + { + if (prefixIt.isAddressInSubnet(ip)) + return it_intfs.first; + } + } + return string(); +} + void IntfsOrch::increaseRouterIntfsRefCount(const string &alias) { SWSS_LOG_ENTER(); @@ -148,11 +182,12 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre auto it_intfs = m_syncdIntfses.find(alias); if (it_intfs == m_syncdIntfses.end()) { - if (addRouterIntfs(vrf_id, port)) + if (!ip_prefix && addRouterIntfs(vrf_id, port)) { IntfsEntry intfs_entry; intfs_entry.ref_count = 0; m_syncdIntfses[alias] = intfs_entry; + m_vrfOrch->increaseVrfRefCount(vrf_id); } else { @@ -193,9 +228,7 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre return false; } - vrf_id = port.m_vr_id; - addSubnetRoute(port, *ip_prefix); - addIp2MeRoute(vrf_id, *ip_prefix); + addIp2MeRoute(port.m_vr_id, *ip_prefix); if (port.m_type == Port::VLAN) { @@ -218,8 +251,7 @@ bool IntfsOrch::removeIntf(const string& alias, sai_object_id_t vrf_id, const Ip if (ip_prefix && m_syncdIntfses[alias].ip_addresses.count(*ip_prefix)) { - removeSubnetRoute(port, *ip_prefix); - removeIp2MeRoute(vrf_id, *ip_prefix); + removeIp2MeRoute(port.m_vr_id, *ip_prefix); if(port.m_type == Port::VLAN) { @@ -229,12 +261,12 @@ bool IntfsOrch::removeIntf(const string& alias, sai_object_id_t vrf_id, const Ip m_syncdIntfses[alias].ip_addresses.erase(*ip_prefix); } - /* Remove router interface that no IP addresses are associated with */ - if (m_syncdIntfses[alias].ip_addresses.size() == 0) + if (!ip_prefix) { - if (removeRouterIntfs(port)) + if (m_syncdIntfses[alias].ip_addresses.size() == 0 && removeRouterIntfs(port)) { m_syncdIntfses.erase(alias); + m_vrfOrch->decreaseVrfRefCount(vrf_id); return true; } else @@ -578,81 +610,6 @@ bool IntfsOrch::removeRouterIntfs(Port &port) return true; } -void IntfsOrch::addSubnetRoute(const Port &port, const IpPrefix &ip_prefix) -{ - sai_route_entry_t unicast_route_entry; - unicast_route_entry.switch_id = gSwitchId; - unicast_route_entry.vr_id = port.m_vr_id; - copy(unicast_route_entry.destination, ip_prefix); - subnet(unicast_route_entry.destination, unicast_route_entry.destination); - - sai_attribute_t attr; - vector attrs; - - attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; - attr.value.s32 = SAI_PACKET_ACTION_FORWARD; - attrs.push_back(attr); - - attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; - attr.value.oid = port.m_rif_id; - attrs.push_back(attr); - - sai_status_t status = sai_route_api->create_route_entry(&unicast_route_entry, (uint32_t)attrs.size(), attrs.data()); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to create subnet route to %s from %s, rv:%d", - ip_prefix.to_string().c_str(), port.m_alias.c_str(), status); - throw runtime_error("Failed to create subnet route."); - } - - SWSS_LOG_NOTICE("Create subnet route to %s from %s", - ip_prefix.to_string().c_str(), port.m_alias.c_str()); - increaseRouterIntfsRefCount(port.m_alias); - - if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) - { - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); - } - else - { - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); - } - - gRouteOrch->notifyNextHopChangeObservers(ip_prefix, IpAddresses(), true); -} - -void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) -{ - sai_route_entry_t unicast_route_entry; - unicast_route_entry.switch_id = gSwitchId; - unicast_route_entry.vr_id = port.m_vr_id; - copy(unicast_route_entry.destination, ip_prefix); - subnet(unicast_route_entry.destination, unicast_route_entry.destination); - - sai_status_t status = sai_route_api->remove_route_entry(&unicast_route_entry); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to remove subnet route to %s from %s, rv:%d", - ip_prefix.to_string().c_str(), port.m_alias.c_str(), status); - throw runtime_error("Failed to remove subnet route."); - } - - SWSS_LOG_NOTICE("Remove subnet route to %s from %s", - ip_prefix.to_string().c_str(), port.m_alias.c_str()); - decreaseRouterIntfsRefCount(port.m_alias); - - if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) - { - gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); - } - else - { - gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); - } - - gRouteOrch->notifyNextHopChangeObservers(ip_prefix, IpAddresses(), false); -} - void IntfsOrch::addIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix) { sai_route_entry_t unicast_route_entry; diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 9030186387..84f58976da 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -32,6 +32,9 @@ class IntfsOrch : public Orch IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch); sai_object_id_t getRouterIntfsId(const string&); + sai_object_id_t getVRFid(const string&); + bool isPrefixSubnet(const IpPrefix&, const string&); + string getRouterIntfsAlias(const IpAddress &ip, sai_object_id_t vrf_id = gVirtualRouterId); void increaseRouterIntfsRefCount(const string&); void decreaseRouterIntfsRefCount(const string&); @@ -76,14 +79,9 @@ class IntfsOrch : public Orch std::string getRifFlexCounterTableKey(std::string s); - int getRouterIntfsRefCount(const string&); - bool addRouterIntfs(sai_object_id_t vrf_id, Port &port); bool removeRouterIntfs(Port &port); - void addSubnetRoute(const Port &port, const IpPrefix &ip_prefix); - void removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix); - void addDirectedBroadcast(const Port &port, const IpPrefix &ip_prefix); void removeDirectedBroadcast(const Port &port, const IpPrefix &ip_prefix); }; diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 135e3464e2..58c29e49cd 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -368,7 +368,7 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) // 3) Otherwise, return false. if (!m_neighOrch->getNeighborEntry(session.dstIp, session.neighborInfo.neighbor, session.neighborInfo.mac) && - (session.nexthopInfo.nexthop.isZero() || + (session.nexthopInfo.nexthop.ip_address.isZero() || !m_neighOrch->getNeighborEntry(session.nexthopInfo.nexthop, session.neighborInfo.neighbor, session.neighborInfo.mac))) { @@ -770,8 +770,8 @@ void MirrorOrch::updateNextHop(const NextHopUpdate& update) // This is the ECMP scenario that the new next hop group contains the previous // next hop. There is no need to update this session's monitor port. - if (update.nexthopGroup != IpAddresses() && - update.nexthopGroup.getIpAddresses().count(session.nexthopInfo.nexthop)) + if (update.nexthopGroup != NextHopGroupKey() && + update.nexthopGroup.getNextHops().count(session.nexthopInfo.nexthop)) { continue; @@ -780,13 +780,13 @@ void MirrorOrch::updateNextHop(const NextHopUpdate& update) SWSS_LOG_NOTICE("Updating mirror session %s with route %s", name.c_str(), update.prefix.to_string().c_str()); - if (update.nexthopGroup != IpAddresses()) + if (update.nexthopGroup != NextHopGroupKey()) { - session.nexthopInfo.nexthop = *update.nexthopGroup.getIpAddresses().begin(); + session.nexthopInfo.nexthop = *update.nexthopGroup.getNextHops().begin(); } else { - session.nexthopInfo.nexthop = IpAddress(); + session.nexthopInfo.nexthop = NextHopKey(); } // Resolve the neighbor of the new next hop @@ -808,7 +808,7 @@ void MirrorOrch::updateNeighbor(const NeighborUpdate& update) // Check if the session's destination IP matches the neighbor's update IP // or if the session's next hop IP matches the neighbor's update IP if (session.dstIp != update.entry.ip_address && - session.nexthopInfo.nexthop != update.entry.ip_address) + session.nexthopInfo.nexthop.ip_address != update.entry.ip_address) { continue; } diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index 765b24cb5f..9cbe60840b 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -36,7 +36,7 @@ struct MirrorEntry struct { IpPrefix prefix; - IpAddress nexthop; + NextHopKey nexthop; } nexthopInfo; struct diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 1eabb8e3aa..a28ad29846 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -21,12 +21,12 @@ NeighOrch::NeighOrch(DBConnector *db, string tableName, IntfsOrch *intfsOrch) : SWSS_LOG_ENTER(); } -bool NeighOrch::hasNextHop(IpAddress ipAddress) +bool NeighOrch::hasNextHop(const NextHopKey &nexthop) { - return m_syncdNextHops.find(ipAddress) != m_syncdNextHops.end(); + return m_syncdNextHops.find(nexthop) != m_syncdNextHops.end(); } -bool NeighOrch::addNextHop(IpAddress ipAddress, string alias) +bool NeighOrch::addNextHop(const IpAddress &ipAddress, const string &alias) { SWSS_LOG_ENTER(); @@ -38,7 +38,8 @@ bool NeighOrch::addNextHop(IpAddress ipAddress, string alias) return false; } - assert(!hasNextHop(ipAddress)); + NextHopKey nexthop = { ipAddress, alias }; + assert(!hasNextHop(nexthop)); sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(alias); vector next_hop_attrs; @@ -72,8 +73,7 @@ bool NeighOrch::addNextHop(IpAddress ipAddress, string alias) next_hop_entry.next_hop_id = next_hop_id; next_hop_entry.ref_count = 0; next_hop_entry.nh_flags = 0; - next_hop_entry.if_alias = alias; - m_syncdNextHops[ipAddress] = next_hop_entry; + m_syncdNextHops[nexthop] = next_hop_entry; m_intfsOrch->increaseRouterIntfsRefCount(alias); @@ -92,7 +92,7 @@ bool NeighOrch::addNextHop(IpAddress ipAddress, string alias) // is processed after incoming port is down. if (p.m_oper_status == SAI_PORT_OPER_STATUS_DOWN) { - if (setNextHopFlag(ipAddress, NHFLAGS_IFDOWN) == false) + if (setNextHopFlag(nexthop, NHFLAGS_IFDOWN) == false) { SWSS_LOG_WARN("Failed to set NHFLAGS_IFDOWN on nexthop %s for interface %s", ipAddress.to_string().c_str(), alias.c_str()); @@ -101,11 +101,11 @@ bool NeighOrch::addNextHop(IpAddress ipAddress, string alias) return true; } -bool NeighOrch::setNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag) +bool NeighOrch::setNextHopFlag(const NextHopKey &nexthop, const uint32_t nh_flag) { SWSS_LOG_ENTER(); - auto nhop = m_syncdNextHops.find(ipaddr); + auto nhop = m_syncdNextHops.find(nexthop); bool rc = false; assert(nhop != m_syncdNextHops.end()); @@ -120,7 +120,7 @@ bool NeighOrch::setNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag) switch (nh_flag) { case NHFLAGS_IFDOWN: - rc = gRouteOrch->invalidnexthopinNextHopGroup(ipaddr); + rc = gRouteOrch->invalidnexthopinNextHopGroup(nexthop); break; default: assert(0); @@ -130,11 +130,11 @@ bool NeighOrch::setNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag) return rc; } -bool NeighOrch::clearNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag) +bool NeighOrch::clearNextHopFlag(const NextHopKey &nexthop, const uint32_t nh_flag) { SWSS_LOG_ENTER(); - auto nhop = m_syncdNextHops.find(ipaddr); + auto nhop = m_syncdNextHops.find(nexthop); bool rc = false; assert(nhop != m_syncdNextHops.end()); @@ -149,7 +149,7 @@ bool NeighOrch::clearNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag switch (nh_flag) { case NHFLAGS_IFDOWN: - rc = gRouteOrch->validnexthopinNextHopGroup(ipaddr); + rc = gRouteOrch->validnexthopinNextHopGroup(nexthop); break; default: assert(0); @@ -159,11 +159,11 @@ bool NeighOrch::clearNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag return rc; } -bool NeighOrch::isNextHopFlagSet(const IpAddress &ipaddr, const uint32_t nh_flag) +bool NeighOrch::isNextHopFlagSet(const NextHopKey &nexthop, const uint32_t nh_flag) { SWSS_LOG_ENTER(); - auto nhop = m_syncdNextHops.find(ipaddr); + auto nhop = m_syncdNextHops.find(nexthop); assert(nhop != m_syncdNextHops.end()); @@ -182,7 +182,7 @@ bool NeighOrch::ifChangeInformNextHop(const string &alias, bool if_up) for (auto nhop = m_syncdNextHops.begin(); nhop != m_syncdNextHops.end(); ++nhop) { - if (nhop->second.if_alias != alias) + if (nhop->first.alias != alias) { continue; } @@ -209,58 +209,59 @@ bool NeighOrch::ifChangeInformNextHop(const string &alias, bool if_up) return rc; } -bool NeighOrch::removeNextHop(IpAddress ipAddress, string alias) +bool NeighOrch::removeNextHop(const IpAddress &ipAddress, const string &alias) { SWSS_LOG_ENTER(); - assert(hasNextHop(ipAddress)); + NextHopKey nexthop = { ipAddress, alias }; + assert(hasNextHop(nexthop)); - if (m_syncdNextHops[ipAddress].ref_count > 0) + if (m_syncdNextHops[nexthop].ref_count > 0) { SWSS_LOG_ERROR("Failed to remove still referenced next hop %s on %s", ipAddress.to_string().c_str(), alias.c_str()); return false; } - m_syncdNextHops.erase(ipAddress); + m_syncdNextHops.erase(nexthop); m_intfsOrch->decreaseRouterIntfsRefCount(alias); return true; } -sai_object_id_t NeighOrch::getNextHopId(const IpAddress &ipAddress) +sai_object_id_t NeighOrch::getNextHopId(const NextHopKey &nexthop) { - assert(hasNextHop(ipAddress)); - return m_syncdNextHops[ipAddress].next_hop_id; + assert(hasNextHop(nexthop)); + return m_syncdNextHops[nexthop].next_hop_id; } -int NeighOrch::getNextHopRefCount(const IpAddress &ipAddress) +int NeighOrch::getNextHopRefCount(const NextHopKey &nexthop) { - assert(hasNextHop(ipAddress)); - return m_syncdNextHops[ipAddress].ref_count; + assert(hasNextHop(nexthop)); + return m_syncdNextHops[nexthop].ref_count; } -void NeighOrch::increaseNextHopRefCount(const IpAddress &ipAddress) +void NeighOrch::increaseNextHopRefCount(const NextHopKey &nexthop) { - assert(hasNextHop(ipAddress)); - m_syncdNextHops[ipAddress].ref_count ++; + assert(hasNextHop(nexthop)); + m_syncdNextHops[nexthop].ref_count ++; } -void NeighOrch::decreaseNextHopRefCount(const IpAddress &ipAddress) +void NeighOrch::decreaseNextHopRefCount(const NextHopKey &nexthop) { - assert(hasNextHop(ipAddress)); - m_syncdNextHops[ipAddress].ref_count --; + assert(hasNextHop(nexthop)); + m_syncdNextHops[nexthop].ref_count --; } -bool NeighOrch::getNeighborEntry(const IpAddress &ipAddress, NeighborEntry &neighborEntry, MacAddress &macAddress) +bool NeighOrch::getNeighborEntry(const NextHopKey &nexthop, NeighborEntry &neighborEntry, MacAddress &macAddress) { - if (!hasNextHop(ipAddress)) + if (!hasNextHop(nexthop)) { return false; } for (const auto &entry : m_syncdNeighbors) { - if (entry.first.ip_address == ipAddress) + if (entry.first.ip_address == nexthop.ip_address && entry.first.alias == nexthop.alias) { neighborEntry = entry.first; macAddress = entry.second; @@ -271,6 +272,18 @@ bool NeighOrch::getNeighborEntry(const IpAddress &ipAddress, NeighborEntry &neig return false; } +bool NeighOrch::getNeighborEntry(const IpAddress &ipAddress, NeighborEntry &neighborEntry, MacAddress &macAddress) +{ + string alias = m_intfsOrch->getRouterIntfsAlias(ipAddress); + if (alias.empty()) + { + return false; + } + + NextHopKey nexthop(ipAddress, alias); + return getNeighborEntry(nexthop, neighborEntry, macAddress); +} + void NeighOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -369,7 +382,7 @@ void NeighOrch::doTask(Consumer &consumer) } } -bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) +bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress &macAddress) { SWSS_LOG_ENTER(); @@ -378,6 +391,11 @@ bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) string alias = neighborEntry.alias; sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(alias); + if (rif_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to get rif_id for %s", alias.c_str()); + return false; + } sai_neighbor_entry_t neighbor_entry; neighbor_entry.rif_id = rif_id; @@ -463,20 +481,21 @@ bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) return true; } -bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) +bool NeighOrch::removeNeighbor(const NeighborEntry &neighborEntry) { SWSS_LOG_ENTER(); sai_status_t status; IpAddress ip_address = neighborEntry.ip_address; string alias = neighborEntry.alias; + NextHopKey nexthop = { ip_address, alias }; if (m_syncdNeighbors.find(neighborEntry) == m_syncdNeighbors.end()) { return true; } - if (m_syncdNextHops[ip_address].ref_count > 0) + if (m_syncdNextHops[nexthop].ref_count > 0) { SWSS_LOG_INFO("Failed to remove still referenced neighbor %s on %s", m_syncdNeighbors[neighborEntry].to_string().c_str(), alias.c_str()); @@ -490,7 +509,7 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) neighbor_entry.switch_id = gSwitchId; copy(neighbor_entry.ip_address, ip_address); - sai_object_id_t next_hop_id = m_syncdNextHops[ip_address].next_hop_id; + sai_object_id_t next_hop_id = m_syncdNextHops[nexthop].next_hop_id; status = sai_next_hop_api->remove_next_hop(next_hop_id); if (status != SAI_STATUS_SUCCESS) { diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 71257d1415..1fc1ed6cd0 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -7,42 +7,68 @@ #include "intfsorch.h" #include "ipaddress.h" +#include "tokenize.h" #define NHFLAGS_IFDOWN 0x1 // nexthop's outbound i/f is down -struct NeighborEntry +struct NextHopKey { IpAddress ip_address; // neighbor IP address string alias; // incoming interface alias - bool operator<(const NeighborEntry &o) const + NextHopKey() = default; + NextHopKey(const std::string &ipstr, const std::string &alias) : ip_address(ipstr), alias(alias) {} + NextHopKey(const IpAddress &ip, const std::string &alias) : ip_address(ip), alias(alias) {} + NextHopKey(const std::string &str) + { + if (str.find(',') != string::npos) + { + std::string err = "Error converting " + str + " to NextHop"; + throw std::invalid_argument(err); + } + auto keys = tokenize(str, '|'); + if (keys.size() != 2) + { + std::string err = "Error converting " + str + " to NextHop"; + throw std::invalid_argument(err); + } + ip_address = keys[0]; + alias = keys[1]; + } + const std::string to_string() const + { + return ip_address.to_string() + "|" + alias; + } + + bool operator<(const NextHopKey &o) const { return tie(ip_address, alias) < tie(o.ip_address, o.alias); } - bool operator==(const NeighborEntry &o) const + bool operator==(const NextHopKey &o) const { return (ip_address == o.ip_address) && (alias == o.alias); } - bool operator!=(const NeighborEntry &o) const + bool operator!=(const NextHopKey &o) const { return !(*this == o); } }; +typedef NextHopKey NeighborEntry; + struct NextHopEntry { sai_object_id_t next_hop_id; // next hop id int ref_count; // reference count uint32_t nh_flags; // flags - string if_alias; // i/f name alias }; /* NeighborTable: NeighborEntry, neighbor MAC address */ typedef map NeighborTable; -/* NextHopTable: next hop IP address, NextHopEntry */ -typedef map NextHopTable; +/* NextHopTable: NextHopKey, NextHopEntry */ +typedef map NextHopTable; struct NeighborUpdate { @@ -56,18 +82,19 @@ class NeighOrch : public Orch, public Subject public: NeighOrch(DBConnector *db, string tableName, IntfsOrch *intfsOrch); - bool hasNextHop(IpAddress); + bool hasNextHop(const NextHopKey&); - sai_object_id_t getNextHopId(const IpAddress&); - int getNextHopRefCount(const IpAddress&); + sai_object_id_t getNextHopId(const NextHopKey&); + int getNextHopRefCount(const NextHopKey&); - void increaseNextHopRefCount(const IpAddress&); - void decreaseNextHopRefCount(const IpAddress&); + void increaseNextHopRefCount(const NextHopKey&); + void decreaseNextHopRefCount(const NextHopKey&); + bool getNeighborEntry(const NextHopKey&, NeighborEntry&, MacAddress&); bool getNeighborEntry(const IpAddress&, NeighborEntry&, MacAddress&); bool ifChangeInformNextHop(const string &, bool); - bool isNextHopFlagSet(const IpAddress &, const uint32_t); + bool isNextHopFlagSet(const NextHopKey &, const uint32_t); private: IntfsOrch *m_intfsOrch; @@ -75,14 +102,14 @@ class NeighOrch : public Orch, public Subject NeighborTable m_syncdNeighbors; NextHopTable m_syncdNextHops; - bool addNextHop(IpAddress, string); - bool removeNextHop(IpAddress, string); + bool addNextHop(const IpAddress&, const string&); + bool removeNextHop(const IpAddress&, const string&); - bool addNeighbor(NeighborEntry, MacAddress); - bool removeNeighbor(NeighborEntry); + bool addNeighbor(const NeighborEntry&, const MacAddress&); + bool removeNeighbor(const NeighborEntry&); - bool setNextHopFlag(const IpAddress &, const uint32_t); - bool clearNextHopFlag(const IpAddress &, const uint32_t); + bool setNextHopFlag(const NextHopKey &, const uint32_t); + bool clearNextHopFlag(const NextHopKey &, const uint32_t); void doTask(Consumer &consumer); }; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index fc3e932c0b..6cad97e6aa 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -94,7 +94,7 @@ bool OrchDaemon::init() gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, vrf_orch); gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, gIntfsOrch); - gRouteOrch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, gNeighOrch); + gRouteOrch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, gNeighOrch, gIntfsOrch, vrf_orch); CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 8187344e71..75cb7036fb 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -4,6 +4,8 @@ #include "swssnet.h" #include "crmorch.h" +#define VRF_PREFIX "Vrf" + extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gSwitchId; @@ -12,7 +14,6 @@ extern sai_route_api_t* sai_route_api; extern sai_switch_api_t* sai_switch_api; extern PortsOrch *gPortsOrch; -extern IntfsOrch *gIntfsOrch; extern CrmOrch *gCrmOrch; /* Default maximum number of next hop groups */ @@ -21,9 +22,11 @@ extern CrmOrch *gCrmOrch; const int routeorch_pri = 5; -RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : +RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch, IntfsOrch *intfsOrch, VRFOrch *vrfOrch) : Orch(db, tableName, routeorch_pri), m_neighOrch(neighOrch), + m_intfsOrch(intfsOrch), + m_vrfOrch(vrfOrch), m_nextHopGroupCount(0), m_resync(false) { @@ -81,7 +84,7 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); /* Add default IPv4 route into the m_syncdRoutes */ - m_syncdRoutes[default_ip_prefix] = IpAddresses(); + m_syncdRoutes[gVirtualRouterId][default_ip_prefix] = NextHopGroupKey(); SWSS_LOG_NOTICE("Create IPv4 default route with packet action drop"); @@ -100,55 +103,48 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); /* Add default IPv6 route into the m_syncdRoutes */ - m_syncdRoutes[v6_default_ip_prefix] = IpAddresses(); + m_syncdRoutes[gVirtualRouterId][v6_default_ip_prefix] = NextHopGroupKey(); SWSS_LOG_NOTICE("Create IPv6 default route with packet action drop"); } -bool RouteOrch::hasNextHopGroup(const IpAddresses& ipAddresses) const +bool RouteOrch::hasNextHopGroup(const NextHopGroupKey& nexthops) const { - return m_syncdNextHopGroups.find(ipAddresses) != m_syncdNextHopGroups.end(); + return m_syncdNextHopGroups.find(nexthops) != m_syncdNextHopGroups.end(); } -sai_object_id_t RouteOrch::getNextHopGroupId(const IpAddresses& ipAddresses) +sai_object_id_t RouteOrch::getNextHopGroupId(const NextHopGroupKey& nexthops) { - assert(hasNextHopGroup(ipAddresses)); - return m_syncdNextHopGroups[ipAddresses].next_hop_group_id; + assert(hasNextHopGroup(nexthops)); + return m_syncdNextHopGroups[nexthops].next_hop_group_id; } -void RouteOrch::attach(Observer *observer, const IpAddress& dstAddr) +void RouteOrch::attach(Observer *observer, const IpAddress& dstAddr, sai_object_id_t vrf_id) { SWSS_LOG_ENTER(); - auto observerEntry = m_nextHopObservers.find(dstAddr); + Host host = std::make_pair(vrf_id, dstAddr); + auto observerEntry = m_nextHopObservers.find(host); /* Create a new observer entry if no current observer is observing this * IP address */ if (observerEntry == m_nextHopObservers.end()) { - m_nextHopObservers.emplace(dstAddr, NextHopObserverEntry()); - observerEntry = m_nextHopObservers.find(dstAddr); + m_nextHopObservers.emplace(host, NextHopObserverEntry()); + observerEntry = m_nextHopObservers.find(host); /* Find the prefixes that cover the destination IP */ - for (auto route : m_syncdRoutes) + if (m_syncdRoutes.find(vrf_id) != m_syncdRoutes.end()) { - if (route.first.isAddressInSubnet(dstAddr)) + for (auto route : m_syncdRoutes.at(vrf_id)) { - SWSS_LOG_INFO("Prefix %s covers destination address", - route.first.to_string().c_str()); - observerEntry->second.routeTable.emplace( - route.first, route.second); - } - } - - /* Find the subnets that cover the destination IP - * The next hop of the subnet routes is left empty */ - for (auto prefix : gIntfsOrch->getSubnetRoutes()) - { - if (prefix.isAddressInSubnet(dstAddr)) - { - observerEntry->second.routeTable.emplace( - prefix, IpAddresses()); + if (route.first.isAddressInSubnet(dstAddr)) + { + SWSS_LOG_INFO("Prefix %s covers destination address", + route.first.to_string().c_str()); + observerEntry->second.routeTable.emplace( + route.first, route.second); + } } } } @@ -163,15 +159,15 @@ void RouteOrch::attach(Observer *observer, const IpAddress& dstAddr) auto route = observerEntry->second.routeTable.rbegin(); if (route != observerEntry->second.routeTable.rend()) { - NextHopUpdate update = { dstAddr, route->first, route->second }; + NextHopUpdate update = { vrf_id, dstAddr, route->first, route->second }; observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, static_cast(&update)); } } -void RouteOrch::detach(Observer *observer, const IpAddress& dstAddr) +void RouteOrch::detach(Observer *observer, const IpAddress& dstAddr, sai_object_id_t vrf_id) { SWSS_LOG_ENTER(); - auto observerEntry = m_nextHopObservers.find(dstAddr); + auto observerEntry = m_nextHopObservers.find(std::make_pair(vrf_id, dstAddr)); if (observerEntry == m_nextHopObservers.end()) { @@ -189,7 +185,7 @@ void RouteOrch::detach(Observer *observer, const IpAddress& dstAddr) } } -bool RouteOrch::validnexthopinNextHopGroup(const IpAddress &ipaddr) +bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop) { SWSS_LOG_ENTER(); @@ -200,7 +196,7 @@ bool RouteOrch::validnexthopinNextHopGroup(const IpAddress &ipaddr) nhopgroup != m_syncdNextHopGroups.end(); ++nhopgroup) { - if (!(nhopgroup->first.contains(ipaddr))) + if (!(nhopgroup->first.contains(nexthop))) { continue; } @@ -213,7 +209,7 @@ bool RouteOrch::validnexthopinNextHopGroup(const IpAddress &ipaddr) nhgm_attrs.push_back(nhgm_attr); nhgm_attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; - nhgm_attr.value.oid = m_neighOrch->getNextHopId(ipaddr); + nhgm_attr.value.oid = m_neighOrch->getNextHopId(nexthop); nhgm_attrs.push_back(nhgm_attr); status = sai_next_hop_group_api->create_next_hop_group_member(&nexthop_id, gSwitchId, @@ -228,13 +224,13 @@ bool RouteOrch::validnexthopinNextHopGroup(const IpAddress &ipaddr) } gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); - nhopgroup->second.nhopgroup_members[ipaddr] = nexthop_id; + nhopgroup->second.nhopgroup_members[nexthop] = nexthop_id; } return true; } -bool RouteOrch::invalidnexthopinNextHopGroup(const IpAddress &ipaddr) +bool RouteOrch::invalidnexthopinNextHopGroup(const NextHopKey &nexthop) { SWSS_LOG_ENTER(); @@ -245,12 +241,12 @@ bool RouteOrch::invalidnexthopinNextHopGroup(const IpAddress &ipaddr) nhopgroup != m_syncdNextHopGroups.end(); ++nhopgroup) { - if (!(nhopgroup->first.contains(ipaddr))) + if (!(nhopgroup->first.contains(nexthop))) { continue; } - nexthop_id = nhopgroup->second.nhopgroup_members[ipaddr]; + nexthop_id = nhopgroup->second.nhopgroup_members[nexthop]; status = sai_next_hop_group_api->remove_next_hop_group_member(nexthop_id); if (status != SAI_STATUS_SUCCESS) @@ -297,11 +293,22 @@ void RouteOrch::doTask(Consumer& consumer) { /* Mark all current routes as dirty (DEL) in consumer.m_toSync map */ SWSS_LOG_NOTICE("Start resync routes\n"); - for (auto i : m_syncdRoutes) + for (auto j : m_syncdRoutes) { - vector v; - auto x = KeyOpFieldsValuesTuple(i.first.to_string(), DEL_COMMAND, v); - consumer.m_toSync[i.first.to_string()] = x; + string vrf; + + if (j.first != gVirtualRouterId) + { + vrf = m_vrfOrch->getVRFname(j.first) + ":"; + } + + for (auto i : j.second) + { + vector v; + key = vrf + i.first.to_string(); + auto x = KeyOpFieldsValuesTuple(key, DEL_COMMAND, v); + consumer.m_toSync[key] = x; + } } m_resync = true; } @@ -321,50 +328,108 @@ void RouteOrch::doTask(Consumer& consumer) continue; } - IpPrefix ip_prefix = IpPrefix(key); + sai_object_id_t vrf_id; + IpPrefix ip_prefix; + + if (!key.compare(0, strlen(VRF_PREFIX), VRF_PREFIX)) + { + size_t found = key.find(':'); + string vrf_name = key.substr(0, found); + + if (!m_vrfOrch->isVRFexists(vrf_name)) + { + it++; + continue; + } + vrf_id = m_vrfOrch->getVRFid(vrf_name); + ip_prefix = IpPrefix(key.substr(found+1)); + } + else + { + vrf_id = gVirtualRouterId; + ip_prefix = IpPrefix(key); + } if (op == SET_COMMAND) { - IpAddresses ip_addresses; - string alias; + string ips; + string aliases; + bool excp_intfs_flag = false; for (auto i : kfvFieldsValues(t)) { if (fvField(i) == "nexthop") - ip_addresses = IpAddresses(fvValue(i)); + ips = fvValue(i); if (fvField(i) == "ifname") - alias = fvValue(i); + aliases = fvValue(i); } + vector ipv = tokenize(ips, ','); + vector alsv = tokenize(aliases, ','); - // TODO: set to blackhold if nexthop is empty? - if (ip_addresses.getSize() == 0) + for (auto alias : alsv) { - it = consumer.m_toSync.erase(it); - continue; + if (alias == "eth0" || alias == "lo" || alias == "docker0") + { + excp_intfs_flag = true; + break; + } } // TODO: cannot trust m_portsOrch->getPortIdByAlias because sometimes alias is empty - // TODO: need to split aliases with ',' and verify the next hops? - if (alias == "eth0" || alias == "lo" || alias == "docker0") + if (excp_intfs_flag) { /* If any existing routes are updated to point to the * above interfaces, remove them from the ASIC. */ - if (m_syncdRoutes.find(ip_prefix) != m_syncdRoutes.end()) + if (removeRoute(vrf_id, ip_prefix)) + it = consumer.m_toSync.erase(it); + else + it++; + continue; + } + + string nhg_str = ipv[0] + "|" + alsv[0]; + for (uint32_t i = 1; i < ipv.size(); i++) + { + nhg_str += "," + ipv[i] + "|" + alsv[i]; + } + + NextHopGroupKey nhg(nhg_str); + + if (ipv.size() == 1 && IpAddress(ipv[0]).isZero()) + { + /* blackhole to be done */ + if (alsv[0] == "unknown") + { + /* add addBlackholeRoute or addRoute support empty nhg */ + it = consumer.m_toSync.erase(it); + } + /* skip prefix which is linklocal or multicast */ + else if (ip_prefix.getIp().getAddrScope() != IpAddress::GLOBAL_SCOPE) + { + it = consumer.m_toSync.erase(it); + } + /* fullmask subnet route is same as ip2me route */ + else if (ip_prefix.isFullMask() && m_intfsOrch->isPrefixSubnet(ip_prefix, alsv[0])) + { + it = consumer.m_toSync.erase(it); + } + /* subnet route, vrf leaked route, etc */ + else { - if (removeRoute(ip_prefix)) + if (addRoute(vrf_id, ip_prefix, nhg)) it = consumer.m_toSync.erase(it); else it++; } - else - it = consumer.m_toSync.erase(it); continue; } - if (m_syncdRoutes.find(ip_prefix) == m_syncdRoutes.end() || m_syncdRoutes[ip_prefix] != ip_addresses) + if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() || + m_syncdRoutes.at(vrf_id).find(ip_prefix) == m_syncdRoutes.at(vrf_id).end() || + m_syncdRoutes.at(vrf_id).at(ip_prefix) != nhg) { - if (addRoute(ip_prefix, ip_addresses)) + if (addRoute(vrf_id, ip_prefix, nhg)) it = consumer.m_toSync.erase(it); else it++; @@ -375,16 +440,11 @@ void RouteOrch::doTask(Consumer& consumer) } else if (op == DEL_COMMAND) { - if (m_syncdRoutes.find(ip_prefix) != m_syncdRoutes.end()) - { - if (removeRoute(ip_prefix)) - it = consumer.m_toSync.erase(it); - else - it++; - } - else - /* Cannot locate the route */ + /* Cannot locate the route or remove succeed */ + if (removeRoute(vrf_id, ip_prefix)) it = consumer.m_toSync.erase(it); + else + it++; } else { @@ -394,13 +454,13 @@ void RouteOrch::doTask(Consumer& consumer) } } -void RouteOrch::notifyNextHopChangeObservers(IpPrefix prefix, IpAddresses nexthops, bool add) +void RouteOrch::notifyNextHopChangeObservers(sai_object_id_t vrf_id, const IpPrefix &prefix, const NextHopGroupKey &nexthops, bool add) { SWSS_LOG_ENTER(); for (auto& entry : m_nextHopObservers) { - if (!prefix.isAddressInSubnet(entry.first)) + if (vrf_id != entry.first.first || !prefix.isAddressInSubnet(entry.first.second)) { continue; } @@ -408,7 +468,7 @@ void RouteOrch::notifyNextHopChangeObservers(IpPrefix prefix, IpAddresses nextho if (add) { bool update_required = false; - NextHopUpdate update = { entry.first, prefix, nexthops }; + NextHopUpdate update = { vrf_id, entry.first.second, prefix, nexthops }; /* Table should not be empty. Default route should always exists. */ assert(!entry.second.routeTable.empty()); @@ -459,7 +519,7 @@ void RouteOrch::notifyNextHopChangeObservers(IpPrefix prefix, IpAddresses nextho assert(!entry.second.routeTable.empty()); auto route = entry.second.routeTable.rbegin(); - NextHopUpdate update = { entry.first, route->first, route->second }; + NextHopUpdate update = { vrf_id, entry.first.second, route->first, route->second }; for (auto observer : entry.second.observers) { @@ -475,57 +535,62 @@ void RouteOrch::notifyNextHopChangeObservers(IpPrefix prefix, IpAddresses nextho } } -void RouteOrch::increaseNextHopRefCount(IpAddresses ipAddresses) +void RouteOrch::increaseNextHopRefCount(const NextHopGroupKey &nexthops) { /* Return when there is no next hop (dropped) */ - if (ipAddresses.getSize() == 0) + if (nexthops.getSize() == 0) { return; } - else if (ipAddresses.getSize() == 1) + else if (nexthops.getSize() == 1) { - IpAddress ip_address(ipAddresses.to_string()); - m_neighOrch->increaseNextHopRefCount(ip_address); + NextHopKey nexthop(nexthops.to_string()); + if (nexthop.ip_address.isZero()) + m_intfsOrch->increaseRouterIntfsRefCount(nexthop.alias); + else + m_neighOrch->increaseNextHopRefCount(nexthop); } else { - m_syncdNextHopGroups[ipAddresses].ref_count ++; + m_syncdNextHopGroups[nexthops].ref_count ++; } } -void RouteOrch::decreaseNextHopRefCount(IpAddresses ipAddresses) +void RouteOrch::decreaseNextHopRefCount(const NextHopGroupKey &nexthops) { /* Return when there is no next hop (dropped) */ - if (ipAddresses.getSize() == 0) + if (nexthops.getSize() == 0) { return; } - else if (ipAddresses.getSize() == 1) + else if (nexthops.getSize() == 1) { - IpAddress ip_address(ipAddresses.to_string()); - - m_neighOrch->decreaseNextHopRefCount(ip_address); + NextHopKey nexthop(nexthops.to_string()); + if (nexthop.ip_address.isZero()) + m_intfsOrch->decreaseRouterIntfsRefCount(nexthop.alias); + else + m_neighOrch->decreaseNextHopRefCount(nexthop); } else { - m_syncdNextHopGroups[ipAddresses].ref_count --; + m_syncdNextHopGroups[nexthops].ref_count --; } } -bool RouteOrch::isRefCounterZero(const IpAddresses& ipAddresses) const +bool RouteOrch::isRefCounterZero(const NextHopGroupKey &nexthops) const { - if (!hasNextHopGroup(ipAddresses)) + if (!hasNextHopGroup(nexthops)) { return true; } - return m_syncdNextHopGroups.at(ipAddresses).ref_count == 0; + return m_syncdNextHopGroups.at(nexthops).ref_count == 0; } -bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) +bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) { SWSS_LOG_ENTER(); - assert(!hasNextHopGroup(ipAddresses)); + assert(!hasNextHopGroup(nexthops)); if (m_nextHopGroupCount >= m_maxNextHopGroupCount) { @@ -535,8 +600,8 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) } vector next_hop_ids; - set next_hop_set = ipAddresses.getIpAddresses(); - std::map nhopgroup_members_set; + set next_hop_set = nexthops.getNextHops(); + std::map nhopgroup_members_set; /* Assert each IP address exists in m_syncdNextHops table, * and add the corresponding next_hop_id to next_hop_ids. */ @@ -545,7 +610,7 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) if (!m_neighOrch->hasNextHop(it)) { SWSS_LOG_INFO("Failed to get next hop %s in %s", - it.to_string().c_str(), ipAddresses.to_string().c_str()); + it.to_string().c_str(), nexthops.to_string().c_str()); return false; } @@ -570,12 +635,12 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create next hop group %s, rv:%d", - ipAddresses.to_string().c_str(), status); + nexthops.to_string().c_str(), status); return false; } m_nextHopGroupCount ++; - SWSS_LOG_NOTICE("Create next hop group %s", ipAddresses.to_string().c_str()); + SWSS_LOG_NOTICE("Create next hop group %s", nexthops.to_string().c_str()); gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP); @@ -631,18 +696,18 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) * count will increase once the route is successfully syncd. */ next_hop_group_entry.ref_count = 0; - m_syncdNextHopGroups[ipAddresses] = next_hop_group_entry; + m_syncdNextHopGroups[nexthops] = next_hop_group_entry; return true; } -bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) +bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) { SWSS_LOG_ENTER(); sai_object_id_t next_hop_group_id; - auto next_hop_group_entry = m_syncdNextHopGroups.find(ipAddresses); + auto next_hop_group_entry = m_syncdNextHopGroups.find(nexthops); sai_status_t status; assert(next_hop_group_entry != m_syncdNextHopGroups.end()); @@ -653,7 +718,7 @@ bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) } next_hop_group_id = next_hop_group_entry->second.next_hop_group_id; - SWSS_LOG_NOTICE("Delete next hop group %s", ipAddresses.to_string().c_str()); + SWSS_LOG_NOTICE("Delete next hop group %s", nexthops.to_string().c_str()); for (auto nhop = next_hop_group_entry->second.nhopgroup_members.begin(); nhop != next_hop_group_entry->second.nhopgroup_members.end();) @@ -688,21 +753,21 @@ bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) m_nextHopGroupCount --; gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP); - set ip_address_set = ipAddresses.getIpAddresses(); - for (auto it : ip_address_set) + set next_hop_set = nexthops.getNextHops(); + for (auto it : next_hop_set) { m_neighOrch->decreaseNextHopRefCount(it); } - m_syncdNextHopGroups.erase(ipAddresses); + m_syncdNextHopGroups.erase(nexthops); return true; } -void RouteOrch::addTempRoute(IpPrefix ipPrefix, IpAddresses nextHops) +void RouteOrch::addTempRoute(sai_object_id_t vrf_id, const IpPrefix &ipPrefix, const NextHopGroupKey &nextHops) { SWSS_LOG_ENTER(); - auto next_hop_set = nextHops.getIpAddresses(); + auto next_hop_set = nextHops.getNextHops(); /* Remove next hops that are not in m_syncdNextHops */ for (auto it = next_hop_set.begin(); it != next_hop_set.end();) @@ -726,31 +791,52 @@ void RouteOrch::addTempRoute(IpPrefix ipPrefix, IpAddresses nextHops) advance(it, rand() % next_hop_set.size()); /* Set the route's temporary next hop to be the randomly picked one */ - IpAddresses tmp_next_hop((*it).to_string()); - addRoute(ipPrefix, tmp_next_hop); + NextHopGroupKey tmp_next_hop((*it).to_string()); + addRoute(vrf_id, ipPrefix, tmp_next_hop); } -bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) +bool RouteOrch::addRoute(sai_object_id_t vrf_id, const IpPrefix &ipPrefix, const NextHopGroupKey &nextHops) { SWSS_LOG_ENTER(); /* next_hop_id indicates the next hop id or next hop group id of this route */ sai_object_id_t next_hop_id; - auto it_route = m_syncdRoutes.find(ipPrefix); + + if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end()) + { + m_syncdRoutes.emplace(vrf_id, RouteTable()); + m_vrfOrch->increaseVrfRefCount(vrf_id); + } + + auto it_route = m_syncdRoutes.at(vrf_id).find(ipPrefix); /* The route is pointing to a next hop */ if (nextHops.getSize() == 1) { - IpAddress ip_address(nextHops.to_string()); - if (m_neighOrch->hasNextHop(ip_address)) + NextHopKey nexthop(nextHops.to_string()); + if (nexthop.ip_address.isZero()) { - next_hop_id = m_neighOrch->getNextHopId(ip_address); + next_hop_id = m_intfsOrch->getRouterIntfsId(nexthop.alias); + /* rif is not created yet */ + if (next_hop_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to get next hop %s for %s", + nextHops.to_string().c_str(), ipPrefix.to_string().c_str()); + return false; + } } else { - SWSS_LOG_INFO("Failed to get next hop %s for %s", - nextHops.to_string().c_str(), ipPrefix.to_string().c_str()); - return false; + if (m_neighOrch->hasNextHop(nexthop)) + { + next_hop_id = m_neighOrch->getNextHopId(nexthop); + } + else + { + SWSS_LOG_INFO("Failed to get next hop %s for %s", + nextHops.to_string().c_str(), ipPrefix.to_string().c_str()); + return false; + } } } /* The route is pointing to a next hop group */ @@ -766,10 +852,10 @@ bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) /* If the current next hop is part of the next hop group to sync, * then return false and no need to add another temporary route. */ - if (it_route != m_syncdRoutes.end() && it_route->second.getSize() == 1) + if (it_route != m_syncdRoutes.at(vrf_id).end() && it_route->second.getSize() == 1) { - IpAddress ip_address(it_route->second.to_string()); - if (nextHops.contains(ip_address)) + NextHopKey nexthop(it_route->second.to_string()); + if (nextHops.contains(nexthop)) { return false; } @@ -778,7 +864,7 @@ bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) /* Add a temporary route when a next hop group cannot be added, * and there is no temporary route right now or the current temporary * route is not pointing to a member of the next hop group to sync. */ - addTempRoute(ipPrefix, nextHops); + addTempRoute(vrf_id, ipPrefix, nextHops); /* Return false since the original route is not successfully added */ return false; } @@ -789,7 +875,7 @@ bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) /* Sync the route entry */ sai_route_entry_t route_entry; - route_entry.vr_id = gVirtualRouterId; + route_entry.vr_id = vrf_id; route_entry.switch_id = gSwitchId; copy(route_entry.destination, ipPrefix); @@ -801,7 +887,7 @@ bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) * (group) id. The old next hop (group) is then not used and the reference * count will decrease by 1. */ - if (it_route == m_syncdRoutes.end()) + if (it_route == m_syncdRoutes.at(vrf_id).end()) { route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; route_attr.value.oid = next_hop_id; @@ -878,18 +964,33 @@ bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) ipPrefix.to_string().c_str(), nextHops.to_string().c_str()); } - m_syncdRoutes[ipPrefix] = nextHops; + m_syncdRoutes[vrf_id][ipPrefix] = nextHops; - notifyNextHopChangeObservers(ipPrefix, nextHops, true); + notifyNextHopChangeObservers(vrf_id, ipPrefix, nextHops, true); return true; } -bool RouteOrch::removeRoute(IpPrefix ipPrefix) +bool RouteOrch::removeRoute(sai_object_id_t vrf_id, const IpPrefix &ipPrefix) { SWSS_LOG_ENTER(); + auto it_route_table = m_syncdRoutes.find(vrf_id); + if (it_route_table == m_syncdRoutes.end()) + { + SWSS_LOG_INFO("Failed to find route table, vrf_id 0x%lx\n", vrf_id); + return true; + } + + auto it_route = it_route_table->second.find(ipPrefix); + if (it_route == it_route_table->second.end()) + { + SWSS_LOG_INFO("Failed to find route entry, vrf_id 0x%lx, prefix %s\n", vrf_id, + ipPrefix.to_string().c_str()); + return true; + } + sai_route_entry_t route_entry; - route_entry.vr_id = gVirtualRouterId; + route_entry.vr_id = vrf_id; route_entry.switch_id = gSwitchId; copy(route_entry.destination, ipPrefix); @@ -942,39 +1043,42 @@ bool RouteOrch::removeRoute(IpPrefix ipPrefix) } } - /* Remove next hop group entry if ref_count is zero */ - auto it_route = m_syncdRoutes.find(ipPrefix); - if (it_route != m_syncdRoutes.end()) + + /* + * Decrease the reference count only when the route is pointing to a next hop. + * Decrease the reference count when the route is pointing to a next hop group, + * and check whether the reference count decreases to zero. If yes, then we need + * to remove the next hop group. + */ + decreaseNextHopRefCount(it_route->second); + if (it_route->second.getSize() > 1 + && m_syncdNextHopGroups[it_route->second].ref_count == 0) { - /* - * Decrease the reference count only when the route is pointing to a next hop. - * Decrease the reference count when the route is pointing to a next hop group, - * and check whether the reference count decreases to zero. If yes, then we need - * to remove the next hop group. - */ - decreaseNextHopRefCount(it_route->second); - if (it_route->second.getSize() > 1 - && m_syncdNextHopGroups[it_route->second].ref_count == 0) - { - removeNextHopGroup(it_route->second); - } + removeNextHopGroup(it_route->second); } + SWSS_LOG_INFO("Remove route %s with next hop(s) %s", ipPrefix.to_string().c_str(), it_route->second.to_string().c_str()); if (ipPrefix.isDefaultRoute()) { - m_syncdRoutes[ipPrefix] = IpAddresses(); + it_route_table->second[ipPrefix] = NextHopGroupKey(); /* Notify about default route next hop change */ - notifyNextHopChangeObservers(ipPrefix, m_syncdRoutes[ipPrefix], true); + notifyNextHopChangeObservers(vrf_id, ipPrefix, it_route_table->second[ipPrefix], true); } else { - m_syncdRoutes.erase(ipPrefix); + it_route_table->second.erase(ipPrefix); /* Notify about the route next hop removal */ - notifyNextHopChangeObservers(ipPrefix, IpAddresses(), false); + notifyNextHopChangeObservers(vrf_id, ipPrefix, NextHopGroupKey(), false); + + if (it_route_table->second.size() == 0) + { + m_syncdRoutes.erase(vrf_id); + m_vrfOrch->decreaseVrfRefCount(vrf_id); + } } return true; diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index b4d87b6958..b40f51959f 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -9,36 +9,162 @@ #include "ipaddress.h" #include "ipaddresses.h" #include "ipprefix.h" +#include "tokenize.h" #include /* Maximum next hop group number */ #define NHGRP_MAX_SIZE 128 -typedef std::map NextHopGroupMembers; +class NextHopGroupKey +{ +public: + NextHopGroupKey() = default; + + /* ip_string|if_alias separated by ',' */ + NextHopGroupKey(const std::string &nexthops) + { + auto nhv = tokenize(nexthops, ','); + for (const auto &nh : nhv) + { + m_nexthops.insert(nh); + } + } + + inline const std::set &getNextHops() const + { + return m_nexthops; + } + + inline size_t getSize() const + { + return m_nexthops.size(); + } + + inline bool operator<(const NextHopGroupKey &o) const + { + return m_nexthops < o.m_nexthops; + } + + inline bool operator==(const NextHopGroupKey &o) const + { + return m_nexthops == o.m_nexthops; + } + + inline bool operator!=(const NextHopGroupKey &o) const + { + return !(*this == o); + } + + void add(const std::string &ip, const std::string &alias) + { + m_nexthops.emplace(ip, alias); + } + + void add(const std::string &nh) + { + m_nexthops.insert(nh); + } + + void add(const NextHopKey &nh) + { + m_nexthops.insert(nh); + } + + bool contains(const std::string &ip, const std::string &alias) const + { + NextHopKey nh(ip, alias); + return m_nexthops.find(nh) != m_nexthops.end(); + } + + bool contains(const std::string &nh) const + { + return m_nexthops.find(nh) != m_nexthops.end(); + } + + bool contains(const NextHopKey &nh) const + { + return m_nexthops.find(nh) != m_nexthops.end(); + } + + bool contains(const NextHopGroupKey &nhs) const + { + for (const auto &nh : nhs.getNextHops()) + { + if (!contains(nh)) + { + return false; + } + } + return true; + } + + void remove(const std::string &ip, const std::string &alias) + { + NextHopKey nh(ip, alias); + m_nexthops.erase(nh); + } + + void remove(const std::string &nh) + { + m_nexthops.erase(nh); + } + + void remove(const NextHopKey &nh) + { + m_nexthops.erase(nh); + } + + const std::string to_string() const + { + string nhs_str; + + for (auto it = m_nexthops.begin(); it != m_nexthops.end(); ++it) + { + if (it != m_nexthops.begin()) + { + nhs_str += ","; + } + + nhs_str += it->to_string(); + } + + return nhs_str; + } + +private: + std::set m_nexthops; +}; + +typedef std::map NextHopGroupMembers; struct NextHopGroupEntry { sai_object_id_t next_hop_group_id; // next hop group id int ref_count; // reference count - NextHopGroupMembers nhopgroup_members; // ids of members indexed by ip address + NextHopGroupMembers nhopgroup_members; // ids of members indexed by }; struct NextHopUpdate { + sai_object_id_t vrf_id; IpAddress destination; IpPrefix prefix; - IpAddresses nexthopGroup; + NextHopGroupKey nexthopGroup; }; struct NextHopObserverEntry; -/* NextHopGroupTable: next hop group IP addersses, NextHopGroupEntry */ -typedef std::map NextHopGroupTable; -/* RouteTable: destination network, next hop IP address(es) */ -typedef std::map RouteTable; -/* NextHopObserverTable: Destination IP address, next hop observer entry */ -typedef std::map NextHopObserverTable; +/* NextHopGroupTable: NextHopGroupKey, NextHopGroupEntry */ +typedef std::map NextHopGroupTable; +/* RouteTable: destination network, NextHopGroupKey */ +typedef std::map RouteTable; +/* RouteTables: vrf_id, RouteTable */ +typedef std::map RouteTables; +/* Host: vrf_id, IpAddress */ +typedef std::pair Host; +/* NextHopObserverTable: Host, next hop observer entry */ +typedef std::map NextHopObserverTable; struct NextHopObserverEntry { @@ -49,40 +175,42 @@ struct NextHopObserverEntry class RouteOrch : public Orch, public Subject { public: - RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch); + RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch, IntfsOrch *intfsOrch, VRFOrch *vrfOrch); - bool hasNextHopGroup(const IpAddresses&) const; - sai_object_id_t getNextHopGroupId(const IpAddresses&); + bool hasNextHopGroup(const NextHopGroupKey&) const; + sai_object_id_t getNextHopGroupId(const NextHopGroupKey&); - void attach(Observer *, const IpAddress&); - void detach(Observer *, const IpAddress&); + void attach(Observer *, const IpAddress&, sai_object_id_t vrf_id = gVirtualRouterId); + void detach(Observer *, const IpAddress&, sai_object_id_t vrf_id = gVirtualRouterId); - void increaseNextHopRefCount(IpAddresses); - void decreaseNextHopRefCount(IpAddresses); - bool isRefCounterZero(const IpAddresses&) const; + void increaseNextHopRefCount(const NextHopGroupKey&); + void decreaseNextHopRefCount(const NextHopGroupKey&); + bool isRefCounterZero(const NextHopGroupKey&) const; - bool addNextHopGroup(IpAddresses); - bool removeNextHopGroup(IpAddresses); + bool addNextHopGroup(const NextHopGroupKey&); + bool removeNextHopGroup(const NextHopGroupKey&); - bool validnexthopinNextHopGroup(const IpAddress &); - bool invalidnexthopinNextHopGroup(const IpAddress &); + bool validnexthopinNextHopGroup(const NextHopKey&); + bool invalidnexthopinNextHopGroup(const NextHopKey&); - void notifyNextHopChangeObservers(IpPrefix, IpAddresses, bool); + void notifyNextHopChangeObservers(sai_object_id_t, const IpPrefix&, const NextHopGroupKey&, bool); private: NeighOrch *m_neighOrch; + IntfsOrch *m_intfsOrch; + VRFOrch *m_vrfOrch; int m_nextHopGroupCount; int m_maxNextHopGroupCount; bool m_resync; - RouteTable m_syncdRoutes; + RouteTables m_syncdRoutes; NextHopGroupTable m_syncdNextHopGroups; NextHopObserverTable m_nextHopObservers; - void addTempRoute(IpPrefix, IpAddresses); - bool addRoute(IpPrefix, IpAddresses); - bool removeRoute(IpPrefix); + void addTempRoute(sai_object_id_t, const IpPrefix&, const NextHopGroupKey&); + bool addRoute(sai_object_id_t, const IpPrefix&, const NextHopGroupKey&); + bool removeRoute(sai_object_id_t, const IpPrefix&); void doTask(Consumer& consumer); }; diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 8913ab6402..d1ae900869 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -1161,15 +1161,15 @@ bool VNetBitmapObject::addRoute(IpPrefix& ipPrefix, nextHop& nh) } else if (nh.ips.getSize() == 1) { - IpAddress ip_address(nh.ips.to_string()); - if (gNeighOrch->hasNextHop(ip_address)) + NextHopKey nexthop(nh.ips.to_string(), nh.ifname); + if (gNeighOrch->hasNextHop(nexthop)) { - nh_id = gNeighOrch->getNextHopId(ip_address); + nh_id = gNeighOrch->getNextHopId(nexthop); } else { SWSS_LOG_INFO("Failed to get next hop %s for %s", - ip_address.to_string().c_str(), ipPrefix.to_string().c_str()); + nexthop.to_string().c_str(), ipPrefix.to_string().c_str()); return false; } @@ -1735,15 +1735,15 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP } else if (nh.ips.getSize() == 1) { - IpAddress ip_address(nh.ips.to_string()); - if (gNeighOrch->hasNextHop(ip_address)) + NextHopKey nexthop(nh.ips.to_string(), nh.ifname); + if (gNeighOrch->hasNextHop(nexthop)) { - nh_id = gNeighOrch->getNextHopId(ip_address); + nh_id = gNeighOrch->getNextHopId(nexthop); } else { SWSS_LOG_INFO("Failed to get next hop %s for %s", - ip_address.to_string().c_str(), ipPrefix.to_string().c_str()); + nexthop.to_string().c_str(), ipPrefix.to_string().c_str()); return false; } } diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index a67e6734bd..9853d8e1c8 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -78,14 +78,16 @@ bool VRFOrch::addOperation(const Request& request) return false; } - vrf_table_[vrf_name] = router_id; + vrf_table_[vrf_name].vrf_id = router_id; + vrf_table_[vrf_name].ref_count = 0; + vrf_id_table_[router_id] = vrf_name; SWSS_LOG_NOTICE("VRF '%s' was added", vrf_name.c_str()); } else { // Update an existing vrf - sai_object_id_t router_id = it->second; + sai_object_id_t router_id = it->second.vrf_id; for (const auto& attr: attrs) { @@ -114,7 +116,10 @@ bool VRFOrch::delOperation(const Request& request) return true; } - sai_object_id_t router_id = vrf_table_[vrf_name]; + if (vrf_table_[vrf_name].ref_count) + return false; + + sai_object_id_t router_id = vrf_table_[vrf_name].vrf_id; sai_status_t status = sai_virtual_router_api->remove_virtual_router(router_id); if (status != SAI_STATUS_SUCCESS) { @@ -123,6 +128,7 @@ bool VRFOrch::delOperation(const Request& request) } vrf_table_.erase(vrf_name); + vrf_id_table_.erase(router_id); SWSS_LOG_NOTICE("VRF '%s' was removed", vrf_name.c_str()); diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h index 22f5c64307..6a9d8661e9 100644 --- a/orchagent/vrforch.h +++ b/orchagent/vrforch.h @@ -4,7 +4,15 @@ #include "request_parser.h" extern sai_object_id_t gVirtualRouterId; -typedef std::unordered_map VRFTable; + +struct VrfEntry +{ + sai_object_id_t vrf_id; + int ref_count; +}; + +typedef std::unordered_map VRFTable; +typedef std::unordered_map VRFId2NameTable; const request_description_t request_description = { { REQ_T_STRING }, @@ -42,13 +50,43 @@ class VRFOrch : public Orch2 sai_object_id_t getVRFid(const std::string& name) const { if (vrf_table_.find(name) != std::end(vrf_table_)) - { - return vrf_table_.at(name); - } + return vrf_table_.at(name).vrf_id; else - { return gVirtualRouterId; - } + } + + string getVRFname(sai_object_id_t vrf_id) const + { + if (vrf_id == gVirtualRouterId) + return string(""); + if (vrf_id_table_.find(vrf_id) != std::end(vrf_id_table_)) + return vrf_id_table_.at(vrf_id); + else + return string(""); + } + + void increaseVrfRefCount(const std::string& name) + { + if (vrf_table_.find(name) != std::end(vrf_table_)) + vrf_table_.at(name).ref_count++; + } + + void increaseVrfRefCount(sai_object_id_t vrf_id) + { + if (vrf_id != gVirtualRouterId) + increaseVrfRefCount(getVRFname(vrf_id)); + } + + void decreaseVrfRefCount(const std::string& name) + { + if (vrf_table_.find(name) != std::end(vrf_table_)) + vrf_table_.at(name).ref_count--; + } + + void decreaseVrfRefCount(sai_object_id_t vrf_id) + { + if (vrf_id != gVirtualRouterId) + decreaseVrfRefCount(getVRFname(vrf_id)); } private: @@ -56,6 +94,7 @@ class VRFOrch : public Orch2 virtual bool delOperation(const Request& request); VRFTable vrf_table_; + VRFId2NameTable vrf_id_table_; VRFRequest request_; }; From 213bca94ac38238d5e14eb4f3b1a9c4b5297bbfd Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Mon, 3 Jun 2019 02:35:59 -0700 Subject: [PATCH 09/27] revert portsorch to asure --- orchagent/portsorch.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 74ade7e8c7..d423b48a7b 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2823,12 +2823,6 @@ bool PortsOrch::addLag(string lag_alias) lag.m_members = set(); m_portList[lag_alias] = lag; - - /* Add lag name map to counter table */ - FieldValueTuple tuple(lag_alias, sai_serialize_object_id(lag_id)); - vector fields; - fields.push_back(tuple); - m_counterTable->set("", fields); PortUpdate update = { lag, true }; notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); @@ -2862,8 +2856,6 @@ bool PortsOrch::removeLag(Port lag) SWSS_LOG_NOTICE("Remove LAG %s lid:%lx", lag.m_alias.c_str(), lag.m_lag_id); - m_counterTable->del(lag.m_alias); - m_portList.erase(lag.m_alias); PortUpdate update = { lag, false }; From 771d5f1b54bd57d8f15bc24664ab80abf238758f Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 5 Jun 2019 20:05:15 -0700 Subject: [PATCH 10/27] Loopback support VRF --- cfgmgr/intfmgr.cpp | 87 ++++++++++++++++++++---------------- cfgmgr/intfmgr.h | 2 + orchagent/intfsorch.cpp | 86 ++++++++++++++++++----------------- orchagent/intfsorch.h | 2 +- orchagent/request_parser.cpp | 4 +- 5 files changed, 99 insertions(+), 82 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 52f79a66f6..5c7c4f5be1 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -67,6 +67,33 @@ void IntfMgr::setIntfVrf(const string &alias, const string vrfName) EXEC_WITH_ERROR_THROW(cmd.str(), res); } +void IntfMgr::addLoopbackIntf(const string &alias) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " link add " << alias << " mtu 65536 type dummy && "; + cmd << IP_CMD << " link set " << alias << " up"; + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } +} + +void IntfMgr::delLoopbackIntf(const string &alias) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " link del " << alias; + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } +} + int IntfMgr::getIntfIpCount(const string &alias) { stringstream cmd; @@ -174,44 +201,36 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, return false; } - // Set Interface VRF except for lo - if (!is_lo) + /* if intfGeneral has been done then skip */ + if (isIntfGeneralDone(alias)) { - /* if intfGeneral has been done then skip */ - if (isIntfGeneralDone(alias)) - { - return true; - } - if (!vrf_name.empty()) - { - setIntfVrf(alias, vrf_name); - } - m_appIntfTableProducer.set(alias, data); + return true; } - else + if (is_lo) + { + addLoopbackIntf(alias); + } + if (!vrf_name.empty()) { - m_appIntfTableProducer.set("lo", data); + setIntfVrf(alias, vrf_name); } + m_appIntfTableProducer.set(alias, data); m_stateIntfTable.hset(alias, "state", "ok"); } else if (op == DEL_COMMAND) { - // Set Interface VRF except for lo - if (!is_lo) + /* make sure all ip addresses associated with interface are removed, otherwise these ip address would + be set with global vrf and it may cause ip address confliction. */ + if (getIntfIpCount(alias)) { - /* make sure all ip addresses associated with interface are removed, otherwise these ip address would - be set with global vrf and it may cause ip address confliction. */ - if (getIntfIpCount(alias)) - { - return false; - } - setIntfVrf(alias, ""); - m_appIntfTableProducer.del(alias); + return false; } - else + setIntfVrf(alias, ""); + if (is_lo) { - m_appIntfTableProducer.del("lo"); + delLoopbackIntf(alias); } + m_appIntfTableProducer.del(alias); m_stateIntfTable.del(alias); } else @@ -230,8 +249,7 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, string alias(keys[0]); IpPrefix ip_prefix(keys[1]); - bool is_lo = !alias.compare(0, strlen(LOOPBACK_PREFIX), LOOPBACK_PREFIX); - string appKey = (is_lo ? "lo" : keys[0]) + ":" + keys[1]; + string appKey = keys[0] + ":" + keys[1]; if (op == SET_COMMAND) { @@ -245,11 +263,7 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, return false; } - // Set Interface IP except for lo - if (!is_lo) - { - setIntfIp(alias, "add", ip_prefix.to_string(), ip_prefix.isV4()); - } + setIntfIp(alias, "add", ip_prefix.to_string(), ip_prefix.isV4()); std::vector fvVector; FieldValueTuple f("family", ip_prefix.isV4() ? IPV4_NAME : IPV6_NAME); @@ -262,11 +276,8 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, } else if (op == DEL_COMMAND) { - // Set Interface IP except for lo - if (!is_lo) - { - setIntfIp(alias, "del", ip_prefix.to_string(), ip_prefix.isV4()); - } + setIntfIp(alias, "del", ip_prefix.to_string(), ip_prefix.isV4()); + m_appIntfTableProducer.del(appKey); m_stateIntfTable.del(keys[0] + state_db_key_delimiter + keys[1]); } diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 82c000e983..e8ab81bf86 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -29,6 +29,8 @@ class IntfMgr : public Orch bool isIntfStateOk(const string &alias); bool isIntfGeneralDone(const string &alias); int getIntfIpCount(const string &alias); + void addLoopbackIntf(const string &alias); + void delLoopbackIntf(const string &alias); }; } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 324c109ced..40a5e12492 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -34,6 +34,8 @@ const int intfsorch_pri = 35; #define RIF_FLEX_STAT_COUNTER_POLL_MSECS "1000" #define UPDATE_MAPS_SEC 1 +#define LOOPBACK_PREFIX "Loopback" + static const vector rifStatIds = { SAI_ROUTER_INTERFACE_STAT_IN_PACKETS, @@ -81,13 +83,6 @@ sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias) return port.m_rif_id; } -sai_object_id_t IntfsOrch::getVRFid(const string &alias) -{ - Port port; - gPortsOrch->getPort(alias, port); - return port.m_vr_id; -} - bool IntfsOrch::isPrefixSubnet(const IpPrefix &ip_prefix, const string& alias) { if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) @@ -104,7 +99,7 @@ string IntfsOrch::getRouterIntfsAlias(const IpAddress &ip, sai_object_id_t vrf_i { for (const auto &it_intfs: m_syncdIntfses) { - if (getVRFid(it_intfs.first) != vrf_id) + if (it_intfs.second.vrf_id != vrf_id) continue; for (const auto &prefixIt: it_intfs.second.ip_addresses) @@ -186,6 +181,7 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre { IntfsEntry intfs_entry; intfs_entry.ref_count = 0; + intfs_entry.vrf_id = vrf_id; m_syncdIntfses[alias] = intfs_entry; m_vrfOrch->increaseVrfRefCount(vrf_id); } @@ -296,6 +292,7 @@ void IntfsOrch::doTask(Consumer &consumer) string alias(keys[0]); IpPrefix ip_prefix; bool ip_prefix_in_key = false; + bool is_lo = !alias.compare(0, strlen(LOOPBACK_PREFIX), LOOPBACK_PREFIX); if (keys.size() > 1) { @@ -340,38 +337,31 @@ void IntfsOrch::doTask(Consumer &consumer) string op = kfvOp(t); if (op == SET_COMMAND) { - if (alias == "lo") + if (is_lo) { if (!ip_prefix_in_key) { - it = consumer.m_toSync.erase(it); - continue; - } - - bool addIp2Me = false; - // set request for lo may come after warm start restore. - // It is also to prevent dupicate set requests in normal running case. - auto it_intfs = m_syncdIntfses.find(alias); - if (it_intfs == m_syncdIntfses.end()) - { - IntfsEntry intfs_entry; - - intfs_entry.ref_count = 0; - intfs_entry.ip_addresses.insert(ip_prefix); - m_syncdIntfses[alias] = intfs_entry; - addIp2Me = true; + if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) + { + IntfsEntry intfs_entry; + intfs_entry.ref_count = 0; + intfs_entry.vrf_id = vrf_id; + m_syncdIntfses[alias] = intfs_entry; + m_vrfOrch->increaseVrfRefCount(vrf_id); + } } else { - if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix) == 0) - { + if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) + { + it++; + continue; + } + if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix) == 0) + { m_syncdIntfses[alias].ip_addresses.insert(ip_prefix); - addIp2Me = true; - } - } - if (addIp2Me) - { - addIp2MeRoute(vrf_id, ip_prefix); + addIp2MeRoute(m_syncdIntfses[alias].vrf_id, ip_prefix); + } } it = consumer.m_toSync.erase(it); @@ -423,19 +413,33 @@ void IntfsOrch::doTask(Consumer &consumer) } else if (op == DEL_COMMAND) { - if (alias == "lo") + if (is_lo) { - // TODO: handle case for which lo is not in default vrf gVirtualRouterId - if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) + if (!ip_prefix_in_key) { - if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix)) + if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) { - m_syncdIntfses[alias].ip_addresses.erase(ip_prefix); - removeIp2MeRoute(vrf_id, ip_prefix); + if (m_syncdIntfses[alias].ip_addresses.size() == 0) + { + m_vrfOrch->decreaseVrfRefCount(m_syncdIntfses[alias].vrf_id); + m_syncdIntfses.erase(alias); + } + else + { + it++; + continue; + } } - if (m_syncdIntfses[alias].ip_addresses.size() == 0) + } + else + { + if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) { - m_syncdIntfses.erase(alias); + if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix)) + { + m_syncdIntfses[alias].ip_addresses.erase(ip_prefix); + removeIp2MeRoute(m_syncdIntfses[alias].vrf_id, ip_prefix); + } } } diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 84f58976da..1c8d771b81 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -22,6 +22,7 @@ struct IntfsEntry { std::set ip_addresses; int ref_count; + sai_object_id_t vrf_id; }; typedef map IntfsTable; @@ -32,7 +33,6 @@ class IntfsOrch : public Orch IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch); sai_object_id_t getRouterIntfsId(const string&); - sai_object_id_t getVRFid(const string&); bool isPrefixSubnet(const IpPrefix&, const string&); string getRouterIntfsAlias(const IpAddress &ip, sai_object_id_t vrf_id = gVirtualRouterId); diff --git a/orchagent/request_parser.cpp b/orchagent/request_parser.cpp index 610b191454..39c56de0cf 100644 --- a/orchagent/request_parser.cpp +++ b/orchagent/request_parser.cpp @@ -112,9 +112,9 @@ void Request::parseAttrs(const KeyOpFieldsValuesTuple& request) for (auto i = kfvFieldsValues(request).begin(); i != kfvFieldsValues(request).end(); i++) { - if (fvField(*i) == "empty") + if (fvField(*i) == "empty" || fvField(*i) == "NULL") { - // if name of the attribute is 'empty', just skip it. + // if name of the attribute is 'empty' or 'NULL', just skip it. // it's used when we don't have any attributes, but we have to provide one for redis continue; } From e47cbc430d402b497f7bb7b3a43e79732bdc042d Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 12 Jun 2019 00:28:45 -0700 Subject: [PATCH 11/27] VS test for VRF --- tests/test_acl.py | 141 ++++ tests/test_interface.py | 1766 +++++++++++++++++++++++++++++++++++---- tests/test_neighbor.py | 390 +++++++++ tests/test_route.py | 469 ++++++++++- tests/test_vrf.py | 428 +++++----- 5 files changed, 2810 insertions(+), 384 deletions(-) create mode 100644 tests/test_neighbor.py diff --git a/tests/test_acl.py b/tests/test_acl.py index 197026d548..9326fbdefe 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -1206,3 +1206,144 @@ def test_AclRuleIcmpV6(self, dvs, testlog): self.remove_acl_rule(acl_table, acl_rule) self.remove_acl_table(acl_table) + + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + def set_admin_status(self, interface, status): + tbl = swsscommon.Table(self.cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("admin_status", status)]) + tbl.set(interface, fvs) + time.sleep(1) + + def create_l3_intf(self, interface, vrf_name): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + if len(vrf_name) == 0: + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + else: + fvs = swsscommon.FieldValuePairs([("vrf_name", vrf_name)]) + tbl.set(interface, fvs) + time.sleep(1) + + def remove_l3_intf(self, interface): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + tbl._del(interface) + time.sleep(1) + + def add_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(1) + + def remove_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + tbl._del(interface + "|" + ip) + time.sleep(1) + + def add_neighbor(self, interface, ip, mac): + tbl = swsscommon.Table(self.cdb, "NEIGH") + fvs = swsscommon.FieldValuePairs([("neigh", mac)]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(1) + + def remove_neighbor(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "NEIGH") + tbl._del(interface + "|" + ip) + time.sleep(1) + + def test_AclRuleRedirectToNexthop(self, dvs, testlog): + self.setup_db(dvs) + + # bring up interface + self.set_admin_status("Ethernet4", "up") + + # create interface + self.create_l3_intf("Ethernet4", "") + + # assign IP to interface + self.add_ip_address("Ethernet4", "10.0.0.1/24") + + # add neighbor + self.add_neighbor("Ethernet4", "10.0.0.2", "00:01:02:03:04:05") + + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + keys = atbl.getKeys() + assert len(keys) == 1 + nhi_oid = keys[0] + + # create ACL_TABLE in config db + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", "Ethernet0")]) + tbl.set("test_redirect", fvs) + + time.sleep(2) + + # create acl rule + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([ + ("priority", "100"), + ("L4_SRC_PORT", "65000"), + ("PACKET_ACTION", "REDIRECT:10.0.0.2|Ethernet4")]) + tbl.set("test_redirect|test_rule1", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs) + + # check acl table in asic db + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "100" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_L4_SRC_PORT": + assert fv[1] == "65000&mask:0xffff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT": + assert fv[1] == nhi_oid + else: + assert False + + # remove acl rule + tbl._del("test_redirect|test_rule1") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + tbl._del("test_redirect") + + time.sleep(1) + + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + assert len(keys) >= 1 + + # remove neighbor + self.remove_neighbor("Ethernet4", "10.0.0.2") + + # remove interface ip + self.remove_ip_address("Ethernet4", "10.0.0.1/24") + + # remove interface + self.remove_l3_intf("Ethernet4") + + # bring down interface + self.set_admin_status("Ethernet4", "down") diff --git a/tests/test_interface.py b/tests/test_interface.py index 7d33f7ed50..c9975b12b0 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -9,39 +9,134 @@ def setup_db(self, dvs): self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - def set_admin_status(self, interface, status): - tbl = swsscommon.Table(self.cdb, "PORT") + def set_admin_status(self, dvs, interface, status): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL" + elif interface.startswith("Vlan"): + tbl_name = "VLAN" + else: + tbl_name = "PORT" + tbl = swsscommon.Table(self.cdb, tbl_name) fvs = swsscommon.FieldValuePairs([("admin_status", status)]) tbl.set(interface, fvs) time.sleep(1) + # when using FRR, route cannot be inserted if the neighbor is not + # connected. thus it is mandatory to force the interface up manually + if interface.startswith("PortChannel"): + dvs.runcmd("bash -c 'echo " + ("1" if status == "up" else "0") +\ + " > /sys/class/net/" + interface + "/carrier'") + time.sleep(1) + + def create_vrf(self, vrf_name): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + initial_entries = set(tbl.getKeys()) + + tbl = swsscommon.Table(self.cdb, "VRF") + fvs = swsscommon.FieldValuePairs([('empty', 'empty')]) + tbl.set(vrf_name, fvs) + time.sleep(1) + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + current_entries = set(tbl.getKeys()) + assert len(current_entries - initial_entries) == 1 + return list(current_entries - initial_entries)[0] + + def remove_vrf(self, vrf_name): + tbl = swsscommon.Table(self.cdb, "VRF") + tbl._del(vrf_name) + time.sleep(1) + + def create_l3_intf(self, interface, vrf_name): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + elif interface.startswith("Loopback"): + tbl_name = "LOOPBACK_INTERFACE" + else: + tbl_name = "INTERFACE" + if len(vrf_name) == 0: + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + else: + fvs = swsscommon.FieldValuePairs([("vrf_name", vrf_name)]) + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl.set(interface, fvs) + time.sleep(1) + + def remove_l3_intf(self, interface): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + elif interface.startswith("Loopback"): + tbl_name = "LOOPBACK_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl._del(interface) + time.sleep(1) + def add_ip_address(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "INTERFACE") + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + elif interface.startswith("Loopback"): + tbl_name = "LOOPBACK_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) tbl.set(interface + "|" + ip, fvs) - time.sleep(2) # IPv6 netlink message needs longer time + time.sleep(1) def remove_ip_address(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "INTERFACE") - tbl._del(interface + "|" + ip); + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + elif interface.startswith("Loopback"): + tbl_name = "LOOPBACK_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl._del(interface + "|" + ip) time.sleep(1) def set_mtu(self, interface, mtu): - tbl = swsscommon.Table(self.cdb, "PORT") + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL" + elif interface.startswith("Vlan"): + tbl_name = "VLAN" + else: + tbl_name = "PORT" + tbl = swsscommon.Table(self.cdb, tbl_name) fvs = swsscommon.FieldValuePairs([("mtu", mtu)]) tbl.set(interface, fvs) time.sleep(1) - def test_InterfaceAddRemoveIpv6Address(self, dvs, testlog): + def test_PortInterfaceAddRemoveIpv6Address(self, dvs, testlog): self.setup_db(dvs) + # create interface + self.create_l3_intf("Ethernet8", "") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + assert fv[0] != "vrf_name" + # bring up interface # NOTE: For IPv6, only when the interface is up will the netlink message # get generated. - self.set_admin_status("Ethernet8", "up") + self.set_admin_status(dvs, "Ethernet8", "up") # assign IP to interface self.add_ip_address("Ethernet8", "fc00::1/126") + time.sleep(2) # IPv6 netlink message needs longer time # check application database tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") @@ -92,14 +187,22 @@ def test_InterfaceAddRemoveIpv6Address(self, dvs, testlog): # remove IP from interface self.remove_ip_address("Ethernet8", "fc00::1/126") + # remove interface + self.remove_l3_intf("Ethernet8") + # bring down interface - self.set_admin_status("Ethernet8", "down") + self.set_admin_status(dvs, "Ethernet8", "down") # check application database tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") intf_entries = tbl.getKeys() assert len(intf_entries) == 0 + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet8" + # check ASIC database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") for key in tbl.getKeys(): @@ -109,9 +212,22 @@ def test_InterfaceAddRemoveIpv6Address(self, dvs, testlog): if route["dest"] == "fc00::1/128": assert False - def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): + def test_PortInterfaceAddRemoveIpv4Address(self, dvs, testlog): self.setup_db(dvs) + # bring up interface + self.set_admin_status(dvs, "Ethernet8", "up") + + # create interface + self.create_l3_intf("Ethernet8", "") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + assert fv[0] != "vrf_name" + # assign IP to interface self.add_ip_address("Ethernet8", "10.0.0.4/31") @@ -164,11 +280,22 @@ def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): # remove IP from interface self.remove_ip_address("Ethernet8", "10.0.0.4/31") + # remove interface + self.remove_l3_intf("Ethernet8") + + # bring down interface + self.set_admin_status(dvs, "Ethernet8", "down") + # check application database tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") intf_entries = tbl.getKeys() assert len(intf_entries) == 0 + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet8" + # check ASIC database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") for key in tbl.getKeys(): @@ -178,9 +305,19 @@ def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): if route["dest"] == "10.0.0.4/32": assert False - def test_InterfaceSetMtu(self, dvs, testlog): + def test_PortInterfaceSetMtu(self, dvs, testlog): self.setup_db(dvs) + # create interface + self.create_l3_intf("Ethernet16", "") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet16") + assert status == True + for fv in fvs: + assert fv[0] != "vrf_name" + # assign IP to interface self.add_ip_address("Ethernet16", "20.0.0.8/29") @@ -208,68 +345,61 @@ def test_InterfaceSetMtu(self, dvs, testlog): # remove IP from interface self.remove_ip_address("Ethernet16", "20.0.0.8/29") -class TestLagRouterInterfaceIpv4(object): - def setup_db(self, dvs): - self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) - self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - - def create_port_channel(self, dvs, alias): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") - fvs = swsscommon.FieldValuePairs([("admin_status", "up"), - ("mtu", "9100")]) - tbl.set(alias, fvs) - time.sleep(1) + # remove interface + self.remove_l3_intf("Ethernet16") - def remove_port_channel(self, dvs, alias): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") - tbl._del(alias) - time.sleep(1) + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet16") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 - def add_port_channel_members(self, dvs, lag, members): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_MEMBER") - fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) - for member in members: - tbl.set(lag + "|" + member, fvs) - time.sleep(1) + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet16" - def remove_port_channel_members(self, dvs, lag, members): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_MEMBER") - for member in members: - tbl._del(lag + "|" + member) - time.sleep(1) + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "20.0.0.8/29": + assert False + if route["dest"] == "20.0.0.8/32": + assert False - def add_ip_address(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) - tbl.set(interface + "|" + ip, fvs) - time.sleep(1) + def test_PortInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): + self.setup_db(dvs) - def remove_ip_address(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_INTERFACE") - tbl._del(interface + "|" + ip); - time.sleep(1) + # bring up interface + self.set_admin_status(dvs, "Ethernet8", "up") - def set_mtu(self, interface, mtu): - tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") - fvs = swsscommon.FieldValuePairs([("mtu", mtu)]) - tbl.set(interface, fvs) - time.sleep(1) + # create vrf + vrf_oid = self.create_vrf("Vrf_0") - def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): - self.setup_db(dvs) + # create interface with vrf + self.create_l3_intf("Ethernet8", "Vrf_0") - # create port channel - self.create_port_channel(dvs, "PortChannel001") + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == "Vrf_0" + vrf_found = True + break + assert vrf_found == True # assign IP to interface - self.add_ip_address("PortChannel001", "30.0.0.4/31") + self.add_ip_address("Ethernet8", "fc00::1/126") + time.sleep(2) # IPv6 netlink message needs longer time # check application database - tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 - assert intf_entries[0] == "30.0.0.4/31" + assert intf_entries[0] == "fc00::1/126" (status, fvs) = tbl.get(tbl.getKeys()[0]) assert status == True @@ -278,7 +408,7 @@ def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): if fv[0] == "scope": assert fv[1] == "global" elif fv[0] == "family": - assert fv[1] == "IPv4" + assert fv[1] == "IPv6" else: assert False @@ -299,23 +429,45 @@ def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): # the default MTU without any configuration is 9100 if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": assert fv[1] == "9100" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid # check ASIC route database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") for key in tbl.getKeys(): route = json.loads(key) - if route["dest"] == "30.0.0.4/31": + if route["dest"] == "fc00::/126": subnet_found = True - if route["dest"] == "30.0.0.4/32": + assert route["vr"] == vrf_oid + if route["dest"] == "fc00::1/128": ip2me_found = True + assert route["vr"] == vrf_oid assert subnet_found and ip2me_found # remove IP from interface - self.remove_ip_address("PortChannel001", "30.0.0.4/31") + self.remove_ip_address("Ethernet8", "fc00::1/126") + + # remove vrf from interface + self.remove_l3_intf("Ethernet8") + + # remove vrf + self.remove_vrf("Vrf_0") + + # bring down interface + self.set_admin_status(dvs, "Ethernet8", "down") # check application database - tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet8" + + tbl = swsscommon.Table(self.pdb, "VRF") intf_entries = tbl.getKeys() assert len(intf_entries) == 0 @@ -323,29 +475,54 @@ def test_InterfaceAddRemoveIpv4Address(self, dvs, testlog): tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") for key in tbl.getKeys(): route = json.loads(key) - if route["dest"] == "30.0.0.4/31": + if route["dest"] == "fc00::/126": assert False - if route["dest"] == "30.0.0.4/32": + if route["dest"] == "fc00::1/128": assert False - # remove port channel - self.remove_port_channel(dvs, "PortChannel001") - - def test_InterfaceSetMtu(self, dvs, testlog): + def test_PortInterfaceAddRemoveIpv4AddressWithVrf(self, dvs, testlog): self.setup_db(dvs) - # create port channel - self.create_port_channel(dvs, "PortChannel002") + # bring up interface + self.set_admin_status(dvs, "Ethernet8", "up") - # add port channel members - self.add_port_channel_members(dvs, "PortChannel002", ["Ethernet0", "Ethernet4"]) + # create vrf + vrf_oid = self.create_vrf("Vrf_0") + + # create interface with vrf + self.create_l3_intf("Ethernet8", "Vrf_0") + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == "Vrf_0" + vrf_found = True + break + assert vrf_found == True # assign IP to interface - self.add_ip_address("PortChannel002", "40.0.0.8/29") + self.add_ip_address("Ethernet8", "10.0.0.4/31") - # configure MTU to interface - self.set_mtu("PortChannel002", "8888") + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False # check ASIC router interface database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") @@ -361,103 +538,1396 @@ def test_InterfaceSetMtu(self, dvs, testlog): for fv in fvs: if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" - # assert the new value set to the router interface + # the default MTU without any configuration is 9100 if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": - assert fv[1] == "8888" + assert fv[1] == "9100" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid - # check ASIC port database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - port_entries = tbl.getKeys() + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + subnet_found = True + assert route["vr"] == vrf_oid + if route["dest"] == "10.0.0.4/32": + ip2me_found = True + assert route["vr"] == vrf_oid - for key in port_entries: - (status, fvs) = tbl.get(key) - assert status == True - # a member port configured with MTU will have six field/value tuples - if len(fvs) == 6: - for fv in fvs: - # asser the new value 8888 + 22 = 8910 set to the port - if fv[0] == "SAI_PORT_ATTR_MTU": - assert fv[1] == "8910" + assert subnet_found and ip2me_found # remove IP from interface - self.remove_ip_address("PortChannel002", "40.0.0.8/29") - - # remove port channel members - self.remove_port_channel_members(dvs, "PortChannel002", ["Ethernet0", "Ethernet4"]) - - # remove port channel - self.remove_port_channel(dvs, "PortChannel002") - -class TestLoopbackRouterInterface(object): - def setup_db(self, dvs): - self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) - self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - - def createLoIntf(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "LOOPBACK_INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) - tbl.set(interface + "|" + ip, fvs) - time.sleep(1) + self.remove_ip_address("Ethernet8", "10.0.0.4/31") - def removeLoIntf(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "LOOPBACK_INTERFACE") - tbl._del(interface + "|" + ip); - time.sleep(1) + # remove interface + self.remove_l3_intf("Ethernet8") - def test_InterfacesCreateRemove(self, dvs, testlog): - self.setup_db(dvs) + # remove vrf + self.remove_vrf("Vrf_0") - # Create loopback interfaces - self.createLoIntf("Loopback0", "10.1.0.1/32") - self.createLoIntf("Loopback1", "10.1.0.2/32") + # bring down interface + self.set_admin_status(dvs, "Ethernet8", "down") - # Check configuration database - tbl = swsscommon.Table(self.cdb, "LOOPBACK_INTERFACE") + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 - assert len(intf_entries) == 2 - assert "Loopback0|10.1.0.1/32" in intf_entries - assert "Loopback1|10.1.0.2/32" in intf_entries - - # Check application database - tbl = swsscommon.Table(self.pdb, "INTF_TABLE:lo") + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet8" - assert len(intf_entries) == 2 - assert "10.1.0.1/32" in intf_entries - assert "10.1.0.2/32" in intf_entries + tbl = swsscommon.Table(self.pdb, "VRF") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 - # Check ASIC database + # check ASIC database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") for key in tbl.getKeys(): route = json.loads(key) - if route["dest"] == "10.1.0.1/32": - lo0_ip2me_found = True - if route["dest"] == "10.1.0.2/32": - lo1_ip2me_found = True + if route["dest"] == "10.0.0.4/31": + assert False + if route["dest"] == "10.0.0.4/32": + assert False - assert lo0_ip2me_found and lo1_ip2me_found + def test_PortInterfaceAddSameIpv4AddressWithDiffVrf(self, dvs, testlog): + self.setup_db(dvs) + + for i in [0, 4]: + # record ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + old_intf_entries = set(tbl.getKeys()) + + # record ASIC router entry database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + old_route_entries = set(tbl.getKeys()) + + intf_name = "Ethernet" + str(i) + vrf_name = "Vrf_" + str(i) + + # bring up interface + self.set_admin_status(dvs, intf_name, "up") - # Remove lopback interfaces - self.removeLoIntf("Loopback0", "10.1.0.1/32") - self.removeLoIntf("Loopback1", "10.1.0.2/32") + # create vrf + vrf_oid = self.create_vrf(vrf_name) - # Check configuration database - tbl = swsscommon.Table(self.cdb, "LOOPBACK_INTERFACE") + # create interface with vrf + self.create_l3_intf(intf_name, vrf_name) + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get(intf_name) + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == vrf_name + vrf_found = True + break + assert vrf_found == True + + # assign IP to interface + self.add_ip_address(intf_name, "10.0.0.4/31") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:" + intf_name) + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + current_intf_entries = set(tbl.getKeys()) + intf_entries = list(current_intf_entries - old_intf_entries) + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + current_route_entries = set(tbl.getKeys()) + route_entries = list(current_route_entries - old_route_entries) + + for key in route_entries: + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + subnet_found = True + assert route["vr"] == vrf_oid + if route["dest"] == "10.0.0.4/32": + ip2me_found = True + assert route["vr"] == vrf_oid + + assert subnet_found and ip2me_found + + + for i in [0, 4]: + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + old_intf_entries = set(tbl.getKeys()) + + intf_name = "Ethernet" + str(i) + vrf_name = "Vrf_" + str(i) + + # remove IP from interface + self.remove_ip_address(intf_name, "10.0.0.4/31") + + # remove interface + self.remove_l3_intf(intf_name) + + # remove vrf + self.remove_vrf(vrf_name) + + # bring down interface + self.set_admin_status(dvs, intf_name, "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:" + intf_name) + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") intf_entries = tbl.getKeys() - assert len(intf_entries) == 0 + for entry in intf_entries: + assert entry[0] != "Ethernet0" and entry[0] != "Ethernet4" - # Check application database - tbl = swsscommon.Table(self.pdb, "INTF_TABLE:lo") + tbl = swsscommon.Table(self.pdb, "VRF") intf_entries = tbl.getKeys() assert len(intf_entries) == 0 - # Check ASIC database + # check ASIC database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") for key in tbl.getKeys(): route = json.loads(key) - if route["dest"] == "10.1.0.1/32": + if route["dest"] == "10.0.0.4/31": assert False - if route["dest"] == "10.1.0.2/32": + if route["dest"] == "10.0.0.4/32": + assert False + + def create_port_channel(self, alias): + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"), + ("mtu", "9100")]) + tbl.set(alias, fvs) + time.sleep(1) + + def remove_port_channel(self, alias): + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + tbl._del(alias) + time.sleep(1) + + def add_port_channel_members(self, lag, members): + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_MEMBER") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + for member in members: + tbl.set(lag + "|" + member, fvs) + time.sleep(1) + + def remove_port_channel_members(self, lag, members): + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_MEMBER") + for member in members: + tbl._del(lag + "|" + member) + time.sleep(1) + + def test_LagInterfaceAddRemoveIpv6Address(self, dvs, testlog): + self.setup_db(dvs) + + # create port channel + self.create_port_channel("PortChannel001") + + # bring up interface + self.set_admin_status(dvs, "PortChannel001", "up") + + # create l3 interface + self.create_l3_intf("PortChannel001", "") + + # assign IP to interface + self.add_ip_address("PortChannel001", "fc00::1/126") + time.sleep(2) # IPv6 netlink message needs longer time + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "fc00::1/126" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv6" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + subnet_found = True + if route["dest"] == "fc00::1/128": + ip2me_found = True + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("PortChannel001", "fc00::1/126") + + # remove interface + self.remove_l3_intf("PortChannel001") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "PortChannel001" + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + assert False + if route["dest"] == "fc00::1/128": + assert False + + # remove port channel + self.remove_port_channel("PortChannel001") + + def test_LagInterfaceAddRemoveIpv4Address(self, dvs, testlog): + self.setup_db(dvs) + + # create port channel + self.create_port_channel("PortChannel001") + + # bring up interface + self.set_admin_status(dvs, "PortChannel001", "up") + + # create l3 interface + self.create_l3_intf("PortChannel001", "") + + # assign IP to interface + self.add_ip_address("PortChannel001", "30.0.0.4/31") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "30.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "30.0.0.4/31": + subnet_found = True + if route["dest"] == "30.0.0.4/32": + ip2me_found = True + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("PortChannel001", "30.0.0.4/31") + + # remove l3 interface + self.remove_l3_intf("PortChannel001") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "PortChannel001" + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "30.0.0.4/31": + assert False + if route["dest"] == "30.0.0.4/32": + assert False + + # remove port channel + self.remove_port_channel("PortChannel001") + + + def test_LagInterfaceSetMtu(self, dvs, testlog): + self.setup_db(dvs) + + # create port channel + self.create_port_channel("PortChannel002") + + # add port channel members + self.add_port_channel_members("PortChannel002", ["Ethernet0", "Ethernet4"]) + + # create l3 interface + self.create_l3_intf("PortChannel002", "") + + # assign IP to interface + self.add_ip_address("PortChannel002", "40.0.0.8/29") + + # configure MTU to interface + self.set_mtu("PortChannel002", "8888") + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # assert the new value set to the router interface + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "8888" + + # check ASIC port database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + port_entries = tbl.getKeys() + + for key in port_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a member port configured with MTU will have six field/value tuples + if len(fvs) == 6: + for fv in fvs: + # asser the new value 8888 + 22 = 8910 set to the port + if fv[0] == "SAI_PORT_ATTR_MTU": + assert fv[1] == "8910" + + # remove IP from interface + self.remove_ip_address("PortChannel002", "40.0.0.8/29") + + # remove l3 interface + self.remove_l3_intf("PortChannel002") + + # remove port channel members + self.remove_port_channel_members("PortChannel002", ["Ethernet0", "Ethernet4"]) + + # remove port channel + self.remove_port_channel("PortChannel002") + + def test_LagInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create vrf + vrf_oid = self.create_vrf("Vrf_0") + + # create port channel + self.create_port_channel("PortChannel001") + + # bring up interface + self.set_admin_status(dvs, "PortChannel001", "up") + + # create l3 interface + self.create_l3_intf("PortChannel001", "Vrf_0") + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("PortChannel001") + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == "Vrf_0" + vrf_found = True + break + assert vrf_found == True + + # assign IP to interface + self.add_ip_address("PortChannel001", "fc00::1/126") + time.sleep(2) # IPv6 netlink message needs longer time + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "fc00::1/126" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv6" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + subnet_found = True + assert route["vr"] == vrf_oid + if route["dest"] == "fc00::1/128": + ip2me_found = True + assert route["vr"] == vrf_oid + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("PortChannel001", "fc00::1/126") + + # remove interface + self.remove_l3_intf("PortChannel001") + + # remove vrf + self.remove_vrf("Vrf_0") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "PortChannel001" + + tbl = swsscommon.Table(self.pdb, "VRF") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::1/126": + assert False + if route["dest"] == "fc00::1/128": + assert False + + # remove port channel + self.remove_port_channel("PortChannel001") + + + def test_LagInterfaceAddRemoveIpv4AddressWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create port channel + self.create_port_channel("PortChannel001") + + # create vrf + vrf_oid = self.create_vrf("Vrf_0") + + # create interface with vrf + self.create_l3_intf("PortChannel001", "Vrf_0") + + # bring up interface + self.set_admin_status(dvs, "PortChannel001", "up") + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("PortChannel001") + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == "Vrf_0" + vrf_found = True + break + assert vrf_found == True + + # assign IP to interface + self.add_ip_address("PortChannel001", "30.0.0.4/31") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "30.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "30.0.0.4/31": + subnet_found = True + assert route["vr"] == vrf_oid + if route["dest"] == "30.0.0.4/32": + ip2me_found = True + assert route["vr"] == vrf_oid + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("PortChannel001", "30.0.0.4/31") + + # remove l3 interface + self.remove_l3_intf("PortChannel001") + + # remove vrf + self.remove_vrf("Vrf_0") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "PortChannel001" + + tbl = swsscommon.Table(self.pdb, "VRF") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "30.0.0.4/31": + assert False + if route["dest"] == "30.0.0.4/32": + assert False + + # remove port channel + self.remove_port_channel("PortChannel001") + + def create_vlan(self, vlan_id): + tbl = swsscommon.Table(self.cdb, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", vlan_id)]) + tbl.set("Vlan" + vlan_id, fvs) + time.sleep(1) + + def remove_vlan(self, vlan_id): + tbl = swsscommon.Table(self.cdb, "VLAN") + tbl._del("Vlan" + vlan_id) + time.sleep(1) + + def create_vlan_member(self, vlan_id, interface): + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + tbl.set("Vlan" + vlan_id + "|" + interface, fvs) + time.sleep(1) + + def remove_vlan_member(self, vlan_id, interface): + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + tbl._del("Vlan" + vlan_id + "|" + interface) + time.sleep(1) + + def test_VLanInterfaceAddRemoveIpv6Address(self, dvs, testlog): + self.setup_db(dvs) + + # create vlan + self.create_vlan("10") + + # add vlan member + self.create_vlan_member("10", "Ethernet0") + + # bring up interface + self.set_admin_status(dvs, "Ethernet0", "up") + self.set_admin_status(dvs, "Vlan10", "up") + + # create vlan interface + self.create_l3_intf("Vlan10", "") + + # assign IP to interface + self.add_ip_address("Vlan10", "fc00::1/126") + time.sleep(2) # IPv6 netlink message needs longer time + + # check asic database and get vlan_oid + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "fc00::1/126" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv6" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one vlan router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_VLAN" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + subnet_found = True + if route["dest"] == "fc00::1/128": + ip2me_found = True + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("Vlan10", "fc00::1/126") + + # remove interface + self.remove_l3_intf("Vlan10") + + # remove vlan member + self.remove_vlan_member("10", "Ethernet0") + + # remove vlan + self.remove_vlan("10") + + # bring down interface + self.set_admin_status(dvs, "Ethernet0", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Vlan10" + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + assert False + if route["dest"] == "fc00::1/128": + assert False + + def test_VLanInterfaceAddRemoveIpv4Address(self, dvs, testlog): + self.setup_db(dvs) + + # create vlan + self.create_vlan("10") + + # add vlan member + self.create_vlan_member("10", "Ethernet0") + + # bring up interface + self.set_admin_status(dvs, "Ethernet0", "up") + self.set_admin_status(dvs, "Vlan10", "up") + + #create vlan interface + self.create_l3_intf("Vlan10", "") + + # assign IP to interface + self.add_ip_address("Vlan10", "10.0.0.4/31") + + # check asic database and get vlan_oid + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one vlan router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_VLAN" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + subnet_found = True + if route["dest"] == "10.0.0.4/32": + ip2me_found = True + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("Vlan10", "10.0.0.4/31") + + # remove vlan interface + self.remove_l3_intf("Vlan10") + + # remove vlan member + self.remove_vlan_member("10", "Ethernet0") + + # remove vlan + self.remove_vlan("10") + + # bring down interface + self.set_admin_status(dvs, "Ethernet0", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "VLAN_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Vlan10" + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + assert False + if route["dest"] == "10.0.0.4/32": + assert False + + def test_VLanInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create vlan + self.create_vlan("10") + + # add vlan member + self.create_vlan_member("10", "Ethernet0") + + # bring up interface + self.set_admin_status(dvs, "Ethernet0", "up") + self.set_admin_status(dvs, "Vlan10", "up") + + # create vrf + vrf_oid = self.create_vrf("Vrf_0") + + # create vlan interface + self.create_l3_intf("Vlan10", "Vrf_0") + + # assign IP to interface + self.add_ip_address("Vlan10", "fc00::1/126") + time.sleep(2) # IPv6 netlink message needs longer time + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Vlan10") + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == "Vrf_0" + vrf_found = True + break + assert vrf_found == True + + # check asic database and get vlan_oid + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "fc00::1/126" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv6" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one vlan router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_VLAN" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + subnet_found = True + assert route["vr"] == vrf_oid + if route["dest"] == "fc00::1/128": + ip2me_found = True + assert route["vr"] == vrf_oid + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("Vlan10", "fc00::1/126") + + # remove vlan interface + self.remove_l3_intf("Vlan10") + + # remove vlan member + self.remove_vlan_member("10", "Ethernet0") + + # remove vlan + self.remove_vlan("10") + + # bring down interface + self.set_admin_status(dvs, "Ethernet0", "down") + + # remove vrf + self.remove_vrf("Vrf_0") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Vlan10" + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::/126": + assert False + if route["dest"] == "fc00::1/128": + assert False + + def test_VLanInterfaceAddRemoveIpv4AddressWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create vlan + self.create_vlan("10") + + # add vlan member + self.create_vlan_member("10", "Ethernet0") + + # bring up interface + self.set_admin_status(dvs, "Ethernet0", "up") + self.set_admin_status(dvs, "Vlan10", "up") + + # create vrf + vrf_oid = self.create_vrf("Vrf_0") + + # create vlan interface + self.create_l3_intf("Vlan10", "Vrf_0") + + # assign IP to interface + self.add_ip_address("Vlan10", "10.0.0.4/31") + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Vlan10") + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == "Vrf_0" + vrf_found = True + break + assert vrf_found == True + + # check asic database and get vlan_oid + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one vlan router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_VLAN" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + subnet_found = True + assert route["vr"] == vrf_oid + if route["dest"] == "10.0.0.4/32": + ip2me_found = True + assert route["vr"] == vrf_oid + + assert subnet_found and ip2me_found + + # remove IP from interface + self.remove_ip_address("Vlan10", "10.0.0.4/31") + + # remove vlan interface + self.remove_l3_intf("Vlan10") + + # remove vlan member + self.remove_vlan_member("10", "Ethernet0") + + # remove vlan + self.remove_vlan("10") + + # bring down interface + self.set_admin_status(dvs, "Ethernet0", "down") + + # remove vrf + self.remove_vrf("Vrf_0") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "VLAN_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Vlan10" + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + assert False + if route["dest"] == "10.0.0.4/32": + assert False + + def test_LoopbackInterfacesAddRemoveIpv4Address(self, dvs, testlog): + self.setup_db(dvs) + + # Create loopback interfaces + self.create_l3_intf("Loopback0", "") + self.create_l3_intf("Loopback1", "") + + # add ip address + self.add_ip_address("Loopback0", "10.1.0.1/32") + self.add_ip_address("Loopback1", "10.1.0.2/32") + + # Check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback0") + intf_entries = tbl.getKeys() + assert intf_entries[0] == "10.1.0.1/32" + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback1") + intf_entries = tbl.getKeys() + assert intf_entries[0] == "10.1.0.2/32" + + # Check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.1.0.1/32": + lo0_ip2me_found = True + if route["dest"] == "10.1.0.2/32": + lo1_ip2me_found = True + + assert lo0_ip2me_found and lo1_ip2me_found + + # Remove ip address + self.remove_ip_address("Loopback0", "10.1.0.1/32") + self.remove_ip_address("Loopback1", "10.1.0.2/32") + + # Remove interface + self.remove_l3_intf("Loopback0") + self.remove_l3_intf("Loopback1") + + # Check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback0") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback1") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # Check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.1.0.1/32": + assert False + if route["dest"] == "10.1.0.2/32": + assert False + + def test_LoopbackInterfacesAddRemoveIpv6Address(self, dvs, testlog): + self.setup_db(dvs) + + # Create loopback interfaces + self.create_l3_intf("Loopback0", "") + self.create_l3_intf("Loopback1", "") + + # add ip address + self.add_ip_address("Loopback0", "fc00::1/128") + self.add_ip_address("Loopback1", "fd00::1/128") + time.sleep(2) # IPv6 netlink message needs longer time + + # Check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback0") + intf_entries = tbl.getKeys() + assert intf_entries[0] == "fc00::1/128" + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback1") + intf_entries = tbl.getKeys() + assert intf_entries[0] == "fd00::1/128" + + # Check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::1/128": + lo0_ip2me_found = True + if route["dest"] == "fd00::1/128": + lo1_ip2me_found = True + + assert lo0_ip2me_found and lo1_ip2me_found + + # Remove ip address + self.remove_ip_address("Loopback0", "fc00::1/128") + self.remove_ip_address("Loopback1", "fd00::1/128") + + # Remove interface + self.remove_l3_intf("Loopback0") + self.remove_l3_intf("Loopback1") + + # Check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback0") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Loopback1") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # Check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "fc00::1/128": + assert False + if route["dest"] == "fd00::1/128": + assert False + + def test_LoopbackInterfaceIpv4AddressWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + for i in [0, 1]: + # record ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + old_intf_entries = set(tbl.getKeys()) + + # record ASIC router entry database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + old_route_entries = set(tbl.getKeys()) + + intf_name = "Loopback" + str(i) + vrf_name = "Vrf_" + str(i) + + # create vrf + vrf_oid = self.create_vrf(vrf_name) + + # create interface with vrf + self.create_l3_intf(intf_name, vrf_name) + + # check interface's vrf + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get(intf_name) + assert status == True + for fv in fvs: + if fv[0] == "vrf_name": + assert fv[1] == vrf_name + vrf_found = True + break + assert vrf_found == True + + # assign IP to interface + self.add_ip_address(intf_name, "10.0.0.4/32") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:" + intf_name) + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.4/32" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + current_intf_entries = set(tbl.getKeys()) + intf_entries = list(current_intf_entries - old_intf_entries) + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": + assert fv[1] == vrf_oid + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + current_route_entries = set(tbl.getKeys()) + route_entries = list(current_route_entries - old_route_entries) + + for key in route_entries: + route = json.loads(key) + if route["dest"] == "10.0.0.4/32": + ip2me_found = True + assert route["vr"] == vrf_oid + + assert ip2me_found + + + for i in [0, 1]: + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + old_intf_entries = set(tbl.getKeys()) + + intf_name = "Loopback" + str(i) + vrf_name = "Vrf_" + str(i) + + # remove IP from interface + self.remove_ip_address(intf_name, "10.0.0.4/32") + + # remove interface + self.remove_l3_intf(intf_name) + + # remove vrf + self.remove_vrf(vrf_name) + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:" + intf_name) + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Loopback0" and entry[0] != "Loopback1" + + tbl = swsscommon.Table(self.pdb, "VRF") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/32": assert False diff --git a/tests/test_neighbor.py b/tests/test_neighbor.py new file mode 100644 index 0000000000..4cf2196334 --- /dev/null +++ b/tests/test_neighbor.py @@ -0,0 +1,390 @@ +from swsscommon import swsscommon + +import time +import json + +class TestNeighbor(object): + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + def set_admin_status(self, interface, status): + tbl = swsscommon.Table(self.cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("admin_status", status)]) + tbl.set(interface, fvs) + time.sleep(1) + + def create_vrf(self, vrf_name): + tbl = swsscommon.Table(self.cdb, "VRF") + fvs = swsscommon.FieldValuePairs([('empty', 'empty')]) + tbl.set(vrf_name, fvs) + time.sleep(1) + + def remove_vrf(self, vrf_name): + tbl = swsscommon.Table(self.cdb, "VRF") + tbl._del(vrf_name) + time.sleep(1) + + def create_l3_intf(self, interface, vrf_name): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + initial_entries = set(tbl.getKeys()) + + tbl = swsscommon.Table(self.cdb, "INTERFACE") + if len(vrf_name) == 0: + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + else: + fvs = swsscommon.FieldValuePairs([("vrf_name", vrf_name)]) + tbl.set(interface, fvs) + time.sleep(1) + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + current_entries = set(tbl.getKeys()) + assert len(current_entries - initial_entries) == 1 + return list(current_entries - initial_entries)[0] + + def remove_l3_intf(self, interface): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + tbl._del(interface) + time.sleep(1) + + def add_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(2) # IPv6 netlink message needs longer time + + def remove_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + tbl._del(interface + "|" + ip) + time.sleep(1) + + def add_neighbor(self, interface, ip, mac): + tbl = swsscommon.Table(self.cdb, "NEIGH") + fvs = swsscommon.FieldValuePairs([("neigh", mac)]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(1) + + def remove_neighbor(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "NEIGH") + tbl._del(interface + "|" + ip) + time.sleep(1) + + def test_NeighborAddRemoveIpv6(self, dvs, testlog): + self.setup_db(dvs) + + # bring up interface + # NOTE: For IPv6, only when the interface is up will the netlink message + # get generated. + self.set_admin_status("Ethernet8", "up") + + # create interface and get rif_oid + rif_oid = self.create_l3_intf("Ethernet8", "") + + # assign IP to interface + self.add_ip_address("Ethernet8", "2000::1/64") + + # add neighbor + self.add_neighbor("Ethernet8", "2000::2", "00:01:02:03:04:05") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "2000::2" + (status, fvs) = tbl.get(intf_entries[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "neigh": + assert fv[1] == "00:01:02:03:04:05" + elif fv[0] == "family": + assert fv[1] == "IPv6" + else: + assert False + + # check ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + route = json.loads(intf_entries[0]) + assert route["ip"] == "2000::2" + assert route["rif"] == rif_oid + + (status, fvs) = tbl.get(intf_entries[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "00:01:02:03:04:05" + + # remove neighbor + self.remove_neighbor("Ethernet8", "2000::2") + + # remove IP from interface + self.remove_ip_address("Ethernet8", "2000::1/64") + + # remove interface + self.remove_l3_intf("Ethernet8") + + # bring down interface + self.set_admin_status("Ethernet8", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + + def test_NeighborAddRemoveIpv4(self, dvs, testlog): + self.setup_db(dvs) + + # bring up interface + self.set_admin_status("Ethernet8", "up") + + # create interface and get rif_oid + rif_oid = self.create_l3_intf("Ethernet8", "") + + # assign IP to interface + self.add_ip_address("Ethernet8", "10.0.0.1/24") + + # add neighbor + self.add_neighbor("Ethernet8", "10.0.0.2", "00:01:02:03:04:05") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.2" + (status, fvs) = tbl.get(intf_entries[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "neigh": + assert fv[1] == "00:01:02:03:04:05" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + route = json.loads(intf_entries[0]) + assert route["ip"] == "10.0.0.2" + assert route["rif"] == rif_oid + + (status, fvs) = tbl.get(intf_entries[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "00:01:02:03:04:05" + + # remove neighbor + self.remove_neighbor("Ethernet8", "10.0.0.2") + + # remove IP from interface + self.remove_ip_address("Ethernet8", "10.0.0.1/24") + + # remove interface + self.remove_l3_intf("Ethernet8") + + # bring down interface + self.set_admin_status("Ethernet8", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + def test_NeighborAddRemoveIpv6WithVrf(self, dvs, testlog): + self.setup_db(dvs) + + for i in [0, 4]: + # record ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + old_neigh_entries = set(tbl.getKeys()) + + intf_name = "Ethernet" + str(i) + vrf_name = "Vrf_" + str(i) + + # bring up interface + self.set_admin_status(intf_name, "up") + + # create vrf + self.create_vrf(vrf_name) + + # create interface and get rif_oid + rif_oid = self.create_l3_intf(intf_name, vrf_name) + + # assign IP to interface + self.add_ip_address(intf_name, "2000::1/64") + + # add neighbor + self.add_neighbor(intf_name, "2000::2", "00:01:02:03:04:05") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:" + intf_name) + neigh_entries = tbl.getKeys() + assert len(neigh_entries) == 1 + assert neigh_entries[0] == "2000::2" + (status, fvs) = tbl.get(neigh_entries[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "neigh": + assert fv[1] == "00:01:02:03:04:05" + elif fv[0] == "family": + assert fv[1] == "IPv6" + else: + assert False + + # check ASIC neighbor interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + current_neigh_entries = set(tbl.getKeys()) + neigh_entries = list(current_neigh_entries - old_neigh_entries) + assert len(neigh_entries) == 1 + route = json.loads(neigh_entries[0]) + assert route["ip"] == "2000::2" + assert route["rif"] == rif_oid + + (status, fvs) = tbl.get(neigh_entries[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "00:01:02:03:04:05" + + for i in [0, 4]: + # record ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + old_neigh_entries_cnt = len(tbl.getKeys()) + + intf_name = "Ethernet" + str(i) + vrf_name = "Vrf_" + str(i) + + # remove neighbor + self.remove_neighbor(intf_name, "2000::2") + + # remove IP from interface + self.remove_ip_address(intf_name, "2000::1/64") + + # remove interface + self.remove_l3_intf(intf_name) + + # remove vrf + self.remove_vrf(vrf_name) + + # bring down interface + self.set_admin_status(intf_name, "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:" + intf_name) + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC neighbor interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + current_neigh_entries_cnt = len(tbl.getKeys()) + dec_neigh_entries_cnt = (old_neigh_entries_cnt - current_neigh_entries_cnt) + assert dec_neigh_entries_cnt == 1 + + def test_NeighborAddRemoveIpv4WithVrf(self, dvs, testlog): + self.setup_db(dvs) + + for i in [0, 4]: + # record ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + old_neigh_entries = set(tbl.getKeys()) + + intf_name = "Ethernet" + str(i) + vrf_name = "Vrf_" + str(i) + + # bring up interface + self.set_admin_status(intf_name, "up") + + # create vrf + self.create_vrf(vrf_name) + + # create interface and get rif_oid + rif_oid = self.create_l3_intf(intf_name, vrf_name) + + # assign IP to interface + self.add_ip_address(intf_name, "10.0.0.1/24") + + # add neighbor + self.add_neighbor(intf_name, "10.0.0.2", "00:01:02:03:04:05") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:" + intf_name) + neigh_entries = tbl.getKeys() + assert len(neigh_entries) == 1 + assert neigh_entries[0] == "10.0.0.2" + (status, fvs) = tbl.get(neigh_entries[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "neigh": + assert fv[1] == "00:01:02:03:04:05" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC neighbor interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + current_neigh_entries = set(tbl.getKeys()) + neigh_entries = list(current_neigh_entries - old_neigh_entries) + assert len(neigh_entries) == 1 + route = json.loads(neigh_entries[0]) + assert route["ip"] == "10.0.0.2" + assert route["rif"] == rif_oid + + (status, fvs) = tbl.get(neigh_entries[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "00:01:02:03:04:05" + + for i in [0, 4]: + # record ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + old_neigh_entries_cnt = len(tbl.getKeys()) + + intf_name = "Ethernet" + str(i) + vrf_name = "Vrf_" + str(i) + + # remove neighbor + self.remove_neighbor(intf_name, "10.0.0.2") + + # remove IP from interface + self.remove_ip_address(intf_name, "10.0.0.1/24") + + # remove interface + self.remove_l3_intf(intf_name) + + # remove vrf + self.remove_vrf(vrf_name) + + # bring down interface + self.set_admin_status(intf_name, "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:" + intf_name) + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC neighbor interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + current_neigh_entries_cnt = len(tbl.getKeys()) + dec_neigh_entries_cnt = (old_neigh_entries_cnt - current_neigh_entries_cnt) + assert dec_neigh_entries_cnt == 1 \ No newline at end of file diff --git a/tests/test_route.py b/tests/test_route.py index bb8f8de6d4..02fda3b192 100644 --- a/tests/test_route.py +++ b/tests/test_route.py @@ -4,39 +4,460 @@ import time import json -def test_RouteAdd(dvs, testlog): +class TestRoute(object): + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") + def set_admin_status(self, interface, status): + tbl = swsscommon.Table(self.cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("admin_status", status)]) + tbl.set(interface, fvs) + time.sleep(1) - dvs.servers[0].runcmd("ifconfig eth0 10.0.0.1/31") - dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + def create_vrf(self, vrf_name): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + initial_entries = set(tbl.getKeys()) - dvs.servers[1].runcmd("ifconfig eth0 10.0.0.3/31") - dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + tbl = swsscommon.Table(self.cdb, "VRF") + fvs = swsscommon.FieldValuePairs([('empty', 'empty')]) + tbl.set(vrf_name, fvs) + time.sleep(1) - # get neighbor and arp entry - dvs.servers[0].runcmd("ping -c 1 10.0.0.3") + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + current_entries = set(tbl.getKeys()) + assert len(current_entries - initial_entries) == 1 + return list(current_entries - initial_entries)[0] - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) + def remove_vrf(self, vrf_name): + tbl = swsscommon.Table(self.cdb, "VRF") + tbl._del(vrf_name) + time.sleep(1) - pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_ROUTE_ENTRY") + def create_l3_intf(self, interface, vrf_name): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + if len(vrf_name) == 0: + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + else: + fvs = swsscommon.FieldValuePairs([("vrf_name", vrf_name)]) + tbl.set(interface, fvs) + time.sleep(1) - ps.set("2.2.2.0/24", fvs) + def remove_l3_intf(self, interface): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + tbl._del(interface) + time.sleep(1) - # check if route was propagated to ASIC DB + def add_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(1) - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsub) + def remove_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + tbl._del(interface + "|" + ip) + time.sleep(1) - assert len(addobjs) == 1 + def create_route_entry(self, key, pairs): + tbl = swsscommon.ProducerStateTable(self.pdb, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) - rt_key = json.loads(addobjs[0]['key']) + def remove_route_entry(self, key): + tbl = swsscommon.ProducerStateTable(self.pdb, "ROUTE_TABLE") + tbl._del(key) + time.sleep(1) - assert rt_key['dest'] == "2.2.2.0/24" + def test_RouteAddRemoveIpv4Route(self, dvs, testlog): + self.setup_db(dvs) + + # create l3 interface + self.create_l3_intf("Ethernet0", "") + self.create_l3_intf("Ethernet4", "") + + # set ip address + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.0.2/31") + + # bring up interface + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + + # set ip address and default route + dvs.servers[0].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + + dvs.servers[1].runcmd("ip address add 10.0.0.3/31 dev eth0") + dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + + # get neighbor and arp entry + dvs.servers[0].runcmd("ping -c 1 10.0.0.3") + + # add route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 2.2.2.0/24 10.0.0.1\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE") + route_entries = tbl.getKeys() + assert "2.2.2.0/24" in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "2.2.2.0/24": + route_found = True + assert route_found == True + + # remove route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route 2.2.2.0/24 10.0.0.1\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE") + route_entries = tbl.getKeys() + assert "2.2.2.0/24" not in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + assert route["dest"] != "2.2.2.0/24" + + # remove ip address + self.remove_ip_address("Ethernet0", "10.0.0.0/31") + self.remove_ip_address("Ethernet4", "10.0.0.2/31") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + + # remove ip address and default route + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.1/31 dev eth0") + + dvs.servers[1].runcmd("ip route del default dev eth0") + dvs.servers[1].runcmd("ip address del 10.0.0.3/31 dev eth0") + + def test_RouteAddRemoveIpv6Route(self, dvs, testlog): + self.setup_db(dvs) + + # create l3 interface + self.create_l3_intf("Ethernet0", "") + self.create_l3_intf("Ethernet4", "") + + # bring up interface + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + + # set ip address + self.add_ip_address("Ethernet0", "2000::1/64") + self.add_ip_address("Ethernet4", "2001::1/64") + dvs.runcmd("sysctl -w net.ipv6.conf.all.forwarding=1") + + # set ip address and default route + dvs.servers[0].runcmd("ip -6 address add 2000::2/64 dev eth0") + dvs.servers[0].runcmd("ip -6 route add default via 2000::1") + + dvs.servers[1].runcmd("ip -6 address add 2001::2/64 dev eth0") + dvs.servers[1].runcmd("ip -6 route add default via 2001::1") + time.sleep(2) + + # get neighbor entry + dvs.servers[0].runcmd("ping -6 -c 1 2001::2") + + # add route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ipv6 route 3000::0/64 2000::2\"") + time.sleep(2) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE") + route_entries = tbl.getKeys() + assert "3000::/64" in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "3000::/64": + route_found = True + assert route_found == True + + # remove route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ipv6 route 3000::0/64 2000::2\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE") + route_entries = tbl.getKeys() + assert "3000::/64" not in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + assert route["dest"] != "3000::/64" + + # remove ip address + self.remove_ip_address("Ethernet0", "2000::1/64") + self.remove_ip_address("Ethernet4", "2001::1/64") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + + # remove ip address and default route + dvs.servers[0].runcmd("ip -6 route del default dev eth0") + dvs.servers[0].runcmd("ip -6 address del 2000::2/64 dev eth0") + + dvs.servers[1].runcmd("ip -6 route del default dev eth0") + dvs.servers[1].runcmd("ip -6 address del 2001::2/64 dev eth0") + + def test_RouteAddRemoveIpv4RouteWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create vrf + vrf_1_oid = self.create_vrf("Vrf_1") + vrf_2_oid = self.create_vrf("Vrf_2") + + # create l3 interface + self.create_l3_intf("Ethernet0", "Vrf_1") + self.create_l3_intf("Ethernet4", "Vrf_1") + self.create_l3_intf("Ethernet8", "Vrf_2") + self.create_l3_intf("Ethernet12", "Vrf_2") + + # set ip address + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.0.2/31") + self.add_ip_address("Ethernet8", "10.0.0.0/31") + self.add_ip_address("Ethernet12", "10.0.0.2/31") + + # bring up interface + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + self.set_admin_status("Ethernet8", "up") + self.set_admin_status("Ethernet12", "up") + + # set ip address and default route + dvs.servers[0].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + + dvs.servers[1].runcmd("ip address add 10.0.0.3/31 dev eth0") + dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + + dvs.servers[2].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[2].runcmd("ip route add default via 10.0.0.0") + + dvs.servers[3].runcmd("ip address add 10.0.0.3/31 dev eth0") + dvs.servers[3].runcmd("ip route add default via 10.0.0.2") + + time.sleep(1) + + # get neighbor entry + dvs.servers[0].runcmd("ping -c 1 10.0.0.3") + dvs.servers[2].runcmd("ping -c 1 10.0.0.3") + + # add route + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 2.2.2.0/24 10.0.0.1 vrf Vrf_1\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 3.3.3.0/24 10.0.0.1 vrf Vrf_2\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") + route_entries = tbl.getKeys() + assert "2.2.2.0/24" in route_entries + + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") + route_entries = tbl.getKeys() + assert "3.3.3.0/24" in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "2.2.2.0/24" and route["vr"] == vrf_1_oid: + route_Vrf_1_found = True + if route["dest"] == "3.3.3.0/24" and route["vr"] == vrf_2_oid: + route_Vrf_2_found = True + assert route_Vrf_1_found == True and route_Vrf_2_found == True + + # remove route + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route 2.2.2.0/24 10.0.0.1 vrf Vrf_1\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route 3.3.3.0/24 10.0.0.1 vrf Vrf_2\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") + route_entries = tbl.getKeys() + assert "2.2.2.0/24" not in route_entries + + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") + route_entries = tbl.getKeys() + assert "3.3.3.0/24" not in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + assert route["dest"] != "2.2.2.0/24" and route["dest"] != "3.3.3.0/24" + + # remove ip address + self.remove_ip_address("Ethernet0", "10.0.0.0/31") + self.remove_ip_address("Ethernet4", "10.0.0.2/31") + self.remove_ip_address("Ethernet8", "10.0.0.0/31") + self.remove_ip_address("Ethernet12", "10.0.0.2/31") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + self.remove_l3_intf("Ethernet8") + self.remove_l3_intf("Ethernet12") + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + self.set_admin_status("Ethernet8", "down") + self.set_admin_status("Ethernet12", "down") + + # remove vrf + self.remove_vrf("Vrf_1") + self.remove_vrf("Vrf_2") + + # remove ip address and default route + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.1/31 dev eth0") + dvs.servers[1].runcmd("ip route del default dev eth0") + dvs.servers[1].runcmd("ip address del 10.0.0.3/31 dev eth0") + dvs.servers[2].runcmd("ip route del default dev eth0") + dvs.servers[2].runcmd("ip address del 10.0.0.1/31 dev eth0") + dvs.servers[3].runcmd("ip route del default dev eth0") + dvs.servers[3].runcmd("ip address del 10.0.0.3/31 dev eth0") + + + def test_RouteAddRemoveIpv6RouteWithVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create vrf + vrf_1_oid = self.create_vrf("Vrf_1") + vrf_2_oid = self.create_vrf("Vrf_2") + + # create l3 interface + self.create_l3_intf("Ethernet0", "Vrf_1") + self.create_l3_intf("Ethernet4", "Vrf_1") + self.create_l3_intf("Ethernet8", "Vrf_2") + self.create_l3_intf("Ethernet12", "Vrf_2") + + # bring up interface + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + self.set_admin_status("Ethernet8", "up") + self.set_admin_status("Ethernet12", "up") + + # set ip address + self.add_ip_address("Ethernet0", "2000::1/64") + self.add_ip_address("Ethernet4", "2001::1/64") + self.add_ip_address("Ethernet8", "2000::1/64") + self.add_ip_address("Ethernet12", "2001::1/64") + + dvs.runcmd("sysctl -w net.ipv6.conf.all.forwarding=1") + + # set ip address and default route + dvs.servers[0].runcmd("ip -6 address add 2000::2/64 dev eth0") + dvs.servers[0].runcmd("ip -6 route add default via 2000::1") + dvs.servers[1].runcmd("ip -6 address add 2001::2/64 dev eth0") + dvs.servers[1].runcmd("ip -6 route add default via 2001::1") + dvs.servers[2].runcmd("ip -6 address add 2000::2/64 dev eth0") + dvs.servers[2].runcmd("ip -6 route add default via 2000::1") + dvs.servers[3].runcmd("ip -6 address add 2001::2/64 dev eth0") + dvs.servers[3].runcmd("ip -6 route add default via 2001::1") + time.sleep(2) + + # get neighbor entry + dvs.servers[0].runcmd("ping -6 -c 1 2001::2") + dvs.servers[2].runcmd("ping -6 -c 1 2001::2") + time.sleep(2) + + # add route + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ipv6 route 3000::0/64 2000::2 vrf Vrf_1\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ipv6 route 4000::0/64 2000::2 vrf Vrf_2\"") + time.sleep(2) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") + route_entries = tbl.getKeys() + assert "3000::/64" in route_entries + + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") + route_entries = tbl.getKeys() + assert "4000::/64" in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "3000::/64" and route["vr"] == vrf_1_oid: + route_Vrf_1_found = True + if route["dest"] == "4000::/64" and route["vr"] == vrf_2_oid: + route_Vrf_2_found = True + assert route_Vrf_1_found == True and route_Vrf_2_found == True + + + # remove route + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ipv6 route 3000::0/64 2000::2 vrf Vrf_1\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ipv6 route 4000::0/64 2000::2 vrf Vrf_2\"") + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") + route_entries = tbl.getKeys() + assert "3000::/64" not in route_entries + + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") + route_entries = tbl.getKeys() + assert "4000::/64" not in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + assert route["dest"] != "3000::/64" and route["dest"] != "4000::/64" + + # remove ip address + self.remove_ip_address("Ethernet0", "2000::1/64") + self.remove_ip_address("Ethernet4", "2001::1/64") + self.remove_ip_address("Ethernet8", "2000::1/64") + self.remove_ip_address("Ethernet12", "2001::1/64") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + self.remove_l3_intf("Ethernet8") + self.remove_l3_intf("Ethernet12") + + # bring down interface + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + self.set_admin_status("Ethernet8", "down") + self.set_admin_status("Ethernet12", "down") + + # remove vrf + self.remove_vrf("Vrf_1") + self.remove_vrf("Vrf_2") + + # remove ip address and default route + dvs.servers[0].runcmd("ip -6 route del default dev eth0") + dvs.servers[0].runcmd("ip -6 address del 2000::2/64 dev eth0") + dvs.servers[1].runcmd("ip -6 route del default dev eth0") + dvs.servers[1].runcmd("ip -6 address del 2001::2/64 dev eth0") + dvs.servers[2].runcmd("ip -6 route del default dev eth0") + dvs.servers[2].runcmd("ip -6 address del 2000::2/64 dev eth0") + dvs.servers[3].runcmd("ip -6 route del default dev eth0") + dvs.servers[3].runcmd("ip -6 address del 2001::2/64 dev eth0") \ No newline at end of file diff --git a/tests/test_vrf.py b/tests/test_vrf.py index 3dec3e0dc1..fb6c2bc0a2 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -2,238 +2,242 @@ import time import json import random -import time from pprint import pprint +class TestVrf(object): + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - - # FIXME: better to wait until DB create them - time.sleep(1) - - -def create_entry_tbl(db, table, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - + def create_entry(self, tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) -def create_entry_pst(db, table, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) + def create_entry_tbl(self, db, table, key, pairs): + tbl = swsscommon.Table(db, table) + self.create_entry(tbl, key, pairs) + def delete_entry_tbl(self, db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) + def how_many_entries_exist(self, db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - time.sleep(1) + def entries(self, db, table): + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) + def is_vrf_attributes_correct(self, db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = set(tbl.getKeys()) + assert key in keys, "The created key wasn't found" + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" -def entries(db, table): - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) + # filter the fake 'NULL' attribute out + fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) + attr_keys = {entry[0] for entry in fvs} + assert attr_keys == set(expected_attributes.keys()) -def is_vrf_attributes_correct(db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = set(tbl.getKeys()) - assert key in keys, "The created key wasn't found" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - # filter the fake 'NULL' attribute out - fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) - - attr_keys = {entry[0] for entry in fvs} - assert attr_keys == set(expected_attributes.keys()) - - for name, value in fvs: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + for name, value in fvs: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ (value, name, expected_attributes[name]) -def vrf_create(asic_db, appl_db, vrf_name, attributes, expected_attributes): - # check that the vrf wasn't exist before - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 1, "The initial state is incorrect" - - # read existing entries in the DB - initial_entries = entries(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - - # create a fake attribute if we don't have attributes in the request - if len(attributes) == 0: - attributes = [('empty', 'empty')] - - # create the VRF entry in Config DB - create_entry_pst(appl_db, "VRF_TABLE", vrf_name, attributes) - - # check that the vrf entry was created - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 2, "The vrf wasn't created" - - # find the id of the entry which was added - added_entry_id = list(entries(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - initial_entries)[0] - - # check correctness of the created attributes - is_vrf_attributes_correct( - asic_db, - "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", - added_entry_id, - expected_attributes, - ) - - state = { - 'initial_entries': initial_entries, - 'entry_id': added_entry_id, - } - - return state - - -def vrf_remove(asic_db, appl_db, vrf_name, state): - # delete the created vrf entry - delete_entry_pst(appl_db, "VRF_TABLE", vrf_name) + def vrf_create(self, dvs, vrf_name, attributes, expected_attributes): + # check that the vrf wasn't exist before + assert self.how_many_entries_exist(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 1, "The initial state is incorrect" - # check that the vrf entry was removed - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 1, "The vrf wasn't removed" + # read existing entries in the DB + initial_entries = self.entries(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - # check that the correct vrf entry was removed - assert state['initial_entries'] == entries(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER"), "The incorrect entry was removed" + # create a fake attribute if we don't have attributes in the request + if len(attributes) == 0: + attributes = [('empty', 'empty')] + # create the VRF entry in Config DB + self.create_entry_tbl(self.cdb, "VRF", vrf_name, attributes) -def vrf_update(asic_db, appl_db, vrf_name, attributes, expected_attributes, state): - # update the VRF entry in Config DB - create_entry_pst(appl_db, "VRF_TABLE", vrf_name, attributes) + # check vrf created in kernel + (status, rslt) = dvs.runcmd("ip link show " + vrf_name) + assert status == 0 - # check correctness of the created attributes - is_vrf_attributes_correct( - asic_db, - "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", - state['entry_id'], - expected_attributes, - ) - - -def boolean_gen(): - result = random.choice(['false', 'true']) - return result, result - - -def mac_addr_gen(): - ns = [random.randint(0, 255) for _ in xrange(6)] - ns[0] &= 0xfe - mac = ':'.join("%02x" % n for n in ns) - return mac, mac.upper() - - -def packet_action_gen(): - values = [ - ("drop", "SAI_PACKET_ACTION_DROP"), - ("forward", "SAI_PACKET_ACTION_FORWARD"), - ("copy", "SAI_PACKET_ACTION_COPY"), - ("copy_cancel", "SAI_PACKET_ACTION_COPY_CANCEL"), - ("trap", "SAI_PACKET_ACTION_TRAP"), - ("log", "SAI_PACKET_ACTION_LOG"), - ("deny", "SAI_PACKET_ACTION_DENY"), - ("transit", "SAI_PACKET_ACTION_TRANSIT"), - ] - - r = random.choice(values) - return r[0], r[1] - - -def test_VRFOrch_Comprehensive(dvs, testlog): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - attributes = [ - ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), - ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), - ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), - ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), - ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), - ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), - ] - - random.seed(int(time.clock())) - - for n in xrange(2**len(attributes)): - # generate testcases for all combinations of attributes - req_attr = [] + # check application database + tbl = swsscommon.Table(self.pdb, "VRF_TABLE") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == vrf_name exp_attr = {} - vrf_name = "vrf_%d" % n - bmask = 0x1 for an in xrange(len(attributes)): - if (bmask & n) > 0: - req_res, exp_res = attributes[an][2]() - req_attr.append((attributes[an][0], req_res)) - exp_attr[attributes[an][1]] = exp_res - bmask <<= 1 - state = vrf_create(asic_db, appl_db, vrf_name, req_attr, exp_attr) - vrf_remove(asic_db, appl_db, vrf_name, state) - - -def test_VRFOrch(dvs, testlog): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - state = vrf_create(asic_db, appl_db, "vrf0", - [ - ], - { - } - ) - vrf_remove(asic_db, appl_db, "vrf0", state) - - state = vrf_create(asic_db, appl_db, "vrf1", - [ - ('v4', 'true'), - ('src_mac', '02:04:06:07:08:09'), - ], - { - 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE': 'true', - 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS': '02:04:06:07:08:09', - } - ) - vrf_remove(asic_db, appl_db, "vrf1", state) - -def test_VRFOrch_Update(dvs, testlog): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - attributes = [ - ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), - ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), - ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), - ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), - ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), - ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), - ] - - random.seed(int(time.clock())) - - state = vrf_create(asic_db, appl_db, "vrf_a", - [ - ], - { + exp_attr[attributes[an][0]] = attributes[an][1] + self.is_vrf_attributes_correct(self.pdb, "VRF_TABLE", vrf_name, exp_attr) + + # check that the vrf entry was created + assert self.how_many_entries_exist(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 2, "The vrf wasn't created" + + # find the id of the entry which was added + added_entry_id = list(self.entries(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - initial_entries)[0] + + # check correctness of the created attributes + self.is_vrf_attributes_correct( + self.adb, + "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", + added_entry_id, + expected_attributes, + ) + + state = { + 'initial_entries': initial_entries, + 'entry_id': added_entry_id, } - ) - - # try to update each attribute - req_attr = [] - exp_attr = {} - for attr in attributes: - req_res, exp_res = attr[2]() - req_attr.append((attr[0], req_res)) - exp_attr[attr[1]] = exp_res - vrf_update(asic_db, appl_db, "vrf_a", req_attr, exp_attr, state) - - vrf_remove(asic_db, appl_db, "vrf_a", state) + + return state + + + def vrf_remove(self, dvs, vrf_name, state): + # delete the created vrf entry + self.delete_entry_tbl(self.cdb, "VRF", vrf_name) + + # check application database + tbl = swsscommon.Table(self.pdb, "VRF_TABLE") + intf_entries = tbl.getKeys() + assert vrf_name not in intf_entries + + # check that the vrf entry was removed + assert self.how_many_entries_exist(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 1, "The vrf wasn't removed" + + # check that the correct vrf entry was removed + assert state['initial_entries'] == self.entries(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER"), "The incorrect entry was removed" + + + def vrf_update(self, vrf_name, attributes, expected_attributes, state): + # update the VRF entry in Config DB + self.create_entry_tbl(self.cdb, "VRF", vrf_name, attributes) + + # check correctness of the created attributes + self.is_vrf_attributes_correct( + self.adb, + "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", + state['entry_id'], + expected_attributes, + ) + + + def boolean_gen(self): + result = random.choice(['false', 'true']) + return result, result + + + def mac_addr_gen(self): + ns = [random.randint(0, 255) for _ in xrange(6)] + ns[0] &= 0xfe + mac = ':'.join("%02x" % n for n in ns) + return mac, mac.upper() + + + def packet_action_gen(self): + values = [ + ("drop", "SAI_PACKET_ACTION_DROP"), + ("forward", "SAI_PACKET_ACTION_FORWARD"), + ("copy", "SAI_PACKET_ACTION_COPY"), + ("copy_cancel", "SAI_PACKET_ACTION_COPY_CANCEL"), + ("trap", "SAI_PACKET_ACTION_TRAP"), + ("log", "SAI_PACKET_ACTION_LOG"), + ("deny", "SAI_PACKET_ACTION_DENY"), + ("transit", "SAI_PACKET_ACTION_TRANSIT"), + ] + + r = random.choice(values) + return r[0], r[1] + + def test_VRFMgr_Comprehensive(self, dvs, testlog): + self.setup_db(dvs) + + attributes = [ + ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', self.boolean_gen), + ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', self.boolean_gen), + ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', self.mac_addr_gen), + ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', self.packet_action_gen), + ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', self.packet_action_gen), + ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', self.packet_action_gen), + ] + + random.seed(int(time.clock())) + + for n in xrange(2**len(attributes)): + # generate testcases for all combinations of attributes + req_attr = [] + exp_attr = {} + vrf_name = "Vrf_%d" % n + bmask = 0x1 + for an in xrange(len(attributes)): + if (bmask & n) > 0: + req_res, exp_res = attributes[an][2]() + req_attr.append((attributes[an][0], req_res)) + exp_attr[attributes[an][1]] = exp_res + bmask <<= 1 + state = self.vrf_create(dvs, vrf_name, req_attr, exp_attr) + self.vrf_remove(dvs, vrf_name, state) + + + def test_VRFMgr(self, dvs, testlog): + self.setup_db(dvs) + + state = self.vrf_create(dvs, "Vrf0", + [ + ], + { + } + ) + self.vrf_remove(dvs, "Vrf0", state) + + state = self.vrf_create(dvs, "Vrf1", + [ + ('v4', 'true'), + ('src_mac', '02:04:06:07:08:09'), + ], + { + 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE': 'true', + 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS': '02:04:06:07:08:09', + } + ) + self.vrf_remove(dvs, "Vrf1", state) + + def test_VRFMgr_Update(self, dvs, testlog): + self.setup_db(dvs) + + attributes = [ + ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', self.boolean_gen), + ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', self.boolean_gen), + ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', self.mac_addr_gen), + ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', self.packet_action_gen), + ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', self.packet_action_gen), + ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', self.packet_action_gen), + ] + + random.seed(int(time.clock())) + + state = self.vrf_create(dvs, "Vrf_a", + [ + ], + { + } + ) + + # try to update each attribute + req_attr = [] + exp_attr = {} + for attr in attributes: + req_res, exp_res = attr[2]() + req_attr.append((attr[0], req_res)) + exp_attr[attr[1]] = exp_res + self.vrf_update("Vrf_a", req_attr, exp_attr, state) + + self.vrf_remove(dvs, "Vrf_a", state) From 2c64599930d492c5c4a2da76c285a14b184bd249 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 19 Jun 2019 20:44:13 -0700 Subject: [PATCH 12/27] VRF: filter routes pointed to vrf interfaces --- orchagent/routeorch.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 75cb7036fb..e904d8110f 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -404,6 +404,11 @@ void RouteOrch::doTask(Consumer& consumer) /* add addBlackholeRoute or addRoute support empty nhg */ it = consumer.m_toSync.erase(it); } + /* directly connected route to VRF interface which come from kernel */ + else if (!alsv[0].compare(0, strlen(VRF_PREFIX), VRF_PREFIX)) + { + it = consumer.m_toSync.erase(it); + } /* skip prefix which is linklocal or multicast */ else if (ip_prefix.getIp().getAddrScope() != IpAddress::GLOBAL_SCOPE) { From 6a186964ce58d23fed7f58dc960812f52a619dae Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Fri, 21 Jun 2019 01:42:41 -0700 Subject: [PATCH 13/27] Add some new VS test for VRF --- tests/test_acl.py | 5 -- tests/test_interface.py | 32 ++++++++ tests/test_route.py | 159 +++++++++++++++++++++++++++++++++++++- tests/test_vrf.py | 59 ++++++++++++++ tests/test_warm_reboot.py | 122 +++++++++++++++++++++++++++++ 5 files changed, 371 insertions(+), 6 deletions(-) diff --git a/tests/test_acl.py b/tests/test_acl.py index 9326fbdefe..03950d2e53 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -1207,11 +1207,6 @@ def test_AclRuleIcmpV6(self, dvs, testlog): self.remove_acl_table(acl_table) - def setup_db(self, dvs): - self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) - self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - def set_admin_status(self, interface, status): tbl = swsscommon.Table(self.cdb, "PORT") fvs = swsscommon.FieldValuePairs([("admin_status", status)]) diff --git a/tests/test_interface.py b/tests/test_interface.py index c9975b12b0..5abe7eb9d0 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -390,6 +390,10 @@ def test_PortInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): break assert vrf_found == True + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show Ethernet8 | grep Vrf"]) + assert "Vrf_0" in result + # assign IP to interface self.add_ip_address("Ethernet8", "fc00::1/126") time.sleep(2) # IPv6 netlink message needs longer time @@ -462,6 +466,10 @@ def test_PortInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): intf_entries = tbl.getKeys() assert len(intf_entries) == 0 + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show Ethernet8 | grep Vrf"]) + assert "Vrf_0" not in result + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") intf_entries = tbl.getKeys() for entry in intf_entries: @@ -1133,6 +1141,10 @@ def test_LagInterfaceAddRemoveIpv4AddressWithVrf(self, dvs, testlog): break assert vrf_found == True + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show PortChannel001 | grep Vrf"]) + assert "Vrf_0" in result + # assign IP to interface self.add_ip_address("PortChannel001", "30.0.0.4/31") @@ -1192,6 +1204,10 @@ def test_LagInterfaceAddRemoveIpv4AddressWithVrf(self, dvs, testlog): # remove l3 interface self.remove_l3_intf("PortChannel001") + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show PortChannel001 | grep Vrf"]) + assert "Vrf_0" not in result + # remove vrf self.remove_vrf("Vrf_0") @@ -1469,6 +1485,10 @@ def test_VLanInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): # create vlan interface self.create_l3_intf("Vlan10", "Vrf_0") + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show Vlan10 | grep Vrf"]) + assert "Vrf_0" in result + # assign IP to interface self.add_ip_address("Vlan10", "fc00::1/126") time.sleep(2) # IPv6 netlink message needs longer time @@ -1544,6 +1564,10 @@ def test_VLanInterfaceAddRemoveIpv6AddressWithVrf(self, dvs, testlog): # remove vlan interface self.remove_l3_intf("Vlan10") + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show Vlan10 | grep Vrf"]) + assert "Vrf_0" not in result + # remove vlan member self.remove_vlan_member("10", "Ethernet0") @@ -1831,6 +1855,10 @@ def test_LoopbackInterfaceIpv4AddressWithVrf(self, dvs, testlog): # create interface with vrf self.create_l3_intf(intf_name, vrf_name) + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show %s | grep Vrf" % intf_name]) + assert "%s" % vrf_name in result + # check interface's vrf tbl = swsscommon.Table(self.pdb, "INTF_TABLE") (status, fvs) = tbl.get(intf_name) @@ -1911,6 +1939,10 @@ def test_LoopbackInterfaceIpv4AddressWithVrf(self, dvs, testlog): # remove vrf self.remove_vrf(vrf_name) + # check linux kernel + (exitcode, result) = dvs.runcmd(['sh', '-c', "ip link show %s | grep Vrf" % intf_name]) + assert "%s" % vrf_name not in result + # check application database tbl = swsscommon.Table(self.pdb, "INTF_TABLE:" + intf_name) intf_entries = tbl.getKeys() diff --git a/tests/test_route.py b/tests/test_route.py index e23f6a094a..a817011d5a 100644 --- a/tests/test_route.py +++ b/tests/test_route.py @@ -71,9 +71,17 @@ def remove_route_entry(self, key): tbl._del(key) time.sleep(1) + def clear_srv_config(self, dvs): + dvs.servers[0].runcmd("ip address flush dev eth0") + dvs.servers[1].runcmd("ip address flush dev eth0") + dvs.servers[2].runcmd("ip address flush dev eth0") + dvs.servers[3].runcmd("ip address flush dev eth0") + def test_RouteAddRemoveIpv4Route(self, dvs, testlog): self.setup_db(dvs) + self.clear_srv_config(dvs) + # create l3 interface self.create_l3_intf("Ethernet0", "") self.create_l3_intf("Ethernet4", "") @@ -342,7 +350,6 @@ def test_RouteAddRemoveIpv4RouteWithVrf(self, dvs, testlog): dvs.servers[3].runcmd("ip route del default dev eth0") dvs.servers[3].runcmd("ip address del 10.0.0.3/31 dev eth0") - def test_RouteAddRemoveIpv6RouteWithVrf(self, dvs, testlog): self.setup_db(dvs) @@ -461,3 +468,153 @@ def test_RouteAddRemoveIpv6RouteWithVrf(self, dvs, testlog): dvs.servers[2].runcmd("ip -6 address del 2000::2/64 dev eth0") dvs.servers[3].runcmd("ip -6 route del default dev eth0") dvs.servers[3].runcmd("ip -6 address del 2001::2/64 dev eth0") + + def test_RouteAndNexthopInDifferentVrf(self, dvs, testlog): + self.setup_db(dvs) + + # create vrf + vrf_1_oid = self.create_vrf("Vrf_1") + vrf_2_oid = self.create_vrf("Vrf_2") + + # create l3 interface + self.create_l3_intf("Ethernet0", "Vrf_1") + self.create_l3_intf("Ethernet4", "Vrf_1") + self.create_l3_intf("Ethernet8", "Vrf_2") + self.create_l3_intf("Ethernet12", "Vrf_2") + + # set ip address + self.add_ip_address("Ethernet0", "10.0.0.1/24") + self.add_ip_address("Ethernet4", "10.0.1.1/24") + self.add_ip_address("Ethernet8", "20.0.0.1/24") + self.add_ip_address("Ethernet12", "20.0.1.1/24") + + # bring up interface + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + self.set_admin_status("Ethernet8", "up") + self.set_admin_status("Ethernet12", "up") + + # set ip address and default route + dvs.servers[0].runcmd("ip address add 10.0.0.2/24 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.1") + + dvs.servers[1].runcmd("ip address add 10.0.1.2/24 dev eth0") + dvs.servers[1].runcmd("ip route add default via 10.0.1.1") + + dvs.servers[2].runcmd("ip address add 20.0.0.2/24 dev eth0") + dvs.servers[2].runcmd("ip route add default via 20.0.0.1") + + dvs.servers[3].runcmd("ip address add 20.0.1.2/24 dev eth0") + dvs.servers[3].runcmd("ip route add default via 20.0.1.1") + + time.sleep(1) + + # get neighbor entry + dvs.servers[0].runcmd("ping -c 1 10.0.1.2") + dvs.servers[2].runcmd("ping -c 1 20.0.1.2") + + # add route + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 20.0.1.2/32 20.0.1.2 vrf Vrf_1 nexthop-vrf Vrf_2\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 10.0.0.2/32 10.0.0.2 vrf Vrf_2 nexthop-vrf Vrf_1\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") + route_entries = tbl.getKeys() + assert "20.0.1.2/32" in route_entries + + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") + route_entries = tbl.getKeys() + assert "10.0.0.2/32" in route_entries + + # check ASIC neighbor interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + nexthop_entries = tbl.getKeys() + for key in nexthop_entries: + (status, fvs) = tbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_ATTR_IP" and fv[1] == "20.0.1.2": + nexthop2_found = True + nexthop2_oid = key + if fv[0] == "SAI_NEXT_HOP_ATTR_IP" and fv[1] == "10.0.0.2": + nexthop1_found = True + nexthop1_oid = key + + assert nexthop1_found == True and nexthop2_found == True + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + route_entries = tbl.getKeys() + for key in route_entries: + route = json.loads(key) + if route["dest"] == "10.0.0.2/32" and route["vr"] == vrf_2_oid: + (status, fvs) = tbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + assert fv[1] == nexthop1_oid + route1_found = True + if route["dest"] == "20.0.1.2/32" and route["vr"] == vrf_1_oid: + (status, fvs) = tbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + assert fv[1] == nexthop2_oid + route2_found = True + assert route1_found == True and route2_found == True + + # Ping should work + ping_stats = dvs.servers[0].runcmd("ping -c 1 20.0.1.2") + assert ping_stats == 0 + + # remove route + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route 20.0.1.2/32 20.0.1.2 vrf Vrf_1 nexthop-vrf Vrf_2\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route 10.0.0.2/32 10.0.0.2 vrf Vrf_2 nexthop-vrf Vrf_1\"") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") + route_entries = tbl.getKeys() + assert "20.0.1.2/32" not in route_entries + + tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") + route_entries = tbl.getKeys() + assert "10.0.0.2/32" not in route_entries + + # check ASIC route database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + assert route["dest"] != "10.0.0.2/32" and route["dest"] != "20.0.1.2/32" + + # remove ip address + self.remove_ip_address("Ethernet0", "10.0.0.1/24") + self.remove_ip_address("Ethernet4", "10.0.1.1/24") + self.remove_ip_address("Ethernet8", "20.0.0.1/24") + self.remove_ip_address("Ethernet12", "20.0.1.1/24") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + self.remove_l3_intf("Ethernet8") + self.remove_l3_intf("Ethernet12") + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + self.set_admin_status("Ethernet8", "down") + self.set_admin_status("Ethernet12", "down") + + # remove vrf + self.remove_vrf("Vrf_1") + self.remove_vrf("Vrf_2") + + # remove ip address and default route + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.2/24 dev eth0") + dvs.servers[1].runcmd("ip route del default dev eth0") + dvs.servers[1].runcmd("ip address del 10.0.1.2/24 dev eth0") + dvs.servers[2].runcmd("ip route del default dev eth0") + dvs.servers[2].runcmd("ip address del 20.0.0.2/24 dev eth0") + dvs.servers[3].runcmd("ip route del default dev eth0") + dvs.servers[3].runcmd("ip address del 20.0.1.2/24 dev eth0") \ No newline at end of file diff --git a/tests/test_vrf.py b/tests/test_vrf.py index fb6c2bc0a2..c288c5266f 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -116,6 +116,9 @@ def vrf_remove(self, dvs, vrf_name, state): # check that the correct vrf entry was removed assert state['initial_entries'] == self.entries(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER"), "The incorrect entry was removed" + # check vrf was removed from kernel + (status, rslt) = dvs.runcmd("ip link show " + vrf_name) + assert status != 0 def vrf_update(self, vrf_name, attributes, expected_attributes, state): # update the VRF entry in Config DB @@ -241,3 +244,59 @@ def test_VRFMgr_Update(self, dvs, testlog): self.vrf_update("Vrf_a", req_attr, exp_attr, state) self.vrf_remove(dvs, "Vrf_a", state) + + def test_VRFMgr_Capacity(self, dvs, testlog): + self.setup_db(dvs) + + initial_entries_cnt = self.how_many_entries_exist(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + + maximum_vrf_cnt = 999 + + def create_entry(self, tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + def create_entry_tbl(self, db, table, key, pairs): + tbl = swsscommon.Table(db, table) + self.create_entry(tbl, key, pairs) + + # create the VRF entry in Config DB + tbl = swsscommon.Table(self.cdb, "VRF") + fvs = swsscommon.FieldValuePairs([('empty', 'empty')]) + for i in range(maximum_vrf_cnt): + tbl.set("Vrf_%d" % i, fvs) + + # wait for all VRFs pushed to database and linux + time.sleep(15) + + # check app_db + intf_entries_cnt = self.how_many_entries_exist(self.pdb, "VRF_TABLE") + assert intf_entries_cnt == maximum_vrf_cnt + + # check asic_db + current_entries_cnt = self.how_many_entries_exist(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + assert (current_entries_cnt - initial_entries_cnt) == maximum_vrf_cnt + + # check linux kernel + (exitcode, num) = dvs.runcmd(['sh', '-c', "ip link show | grep Vrf | wc -l"]) + assert num.strip() == str(maximum_vrf_cnt) + + # remove VRF from Config DB + for i in range(maximum_vrf_cnt): + tbl._del("Vrf_%d" % i) + + # wait for all VRFs deleted + time.sleep(60) + + # check app_db + intf_entries_cnt = self.how_many_entries_exist(self.pdb, "VRF_TABLE") + assert intf_entries_cnt == 0 + + # check asic_db + current_entries_cnt = self.how_many_entries_exist(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + assert (current_entries_cnt - initial_entries_cnt) == 0 + + # check linux kernel + (exitcode, num) = dvs.runcmd(['sh', '-c', "ip link show | grep Vrf | wc -l"]) + assert num.strip() == '0' diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index e87c644b47..6f2a7e5703 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -1944,3 +1944,125 @@ def test_system_warmreboot_neighbor_syncup(dvs, testlog): intf_tbl._del("Ethernet{}".format(i*4, i*4)) intf_tbl._del("Ethernet{}".format(i*4, i*4)) +def test_VrfMgrdWarmRestart(dvs, testlog): + + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + dvs.runcmd("config warm_restart enable swss") + + # bring up interface + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") + + # create vrf + create_entry_tbl(conf_db, "VRF", "Vrf_1", [('empty', 'empty')]) + create_entry_tbl(conf_db, "VRF", "Vrf_2", [('empty', 'empty')]) + + intf_tbl = swsscommon.Table(conf_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("vrf_name", "Vrf_1")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + intf_tbl.set("Ethernet0|12.0.0.1/24", fvs) + intf_tbl.set("Ethernet4|13.0.0.1/24", fvs) + + time.sleep(1) + + dvs.servers[0].runcmd("ifconfig eth0 12.0.0.2/24") + dvs.servers[0].runcmd("ip route add default via 12.0.0.1") + + dvs.servers[1].runcmd("ifconfig eth0 13.0.0.2/24") + dvs.servers[1].runcmd("ip route add default via 13.0.0.1") + + time.sleep(1) + + # Ping should work between servers via vs port interfaces + ping_stats = dvs.servers[0].runcmd("ping -c 1 13.0.0.2") + assert ping_stats == 0 + time.sleep(1) + + tbl = swsscommon.Table(appl_db, "NEIGH_TABLE") + (status, fvs) = tbl.get("Ethernet0:12.0.0.2") + assert status == True + + (status, fvs) = tbl.get("Ethernet4:13.0.0.2") + assert status == True + + (exitcode, vrf_before) = dvs.runcmd(['sh', '-c', "ip link show | grep Vrf"]) + + dvs.runcmd(['sh', '-c', 'pkill -x vrfmgrd']) + + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE") + + dvs.runcmd(['sh', '-c', 'supervisorctl start vrfmgrd']) + time.sleep(2) + + # kernel vrf config should be kept the same + (exitcode, vrf_after) = dvs.runcmd(['sh', '-c', "ip link show | grep Vrf"]) + assert vrf_after == vrf_before + + # VIRTUAL_ROUTER/ROUTE_ENTRY/NEIGH_ENTRY should be kept the same + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub, ignore=["SAI_OBJECT_TYPE_FDB_ENTRY"]) + assert nadd == 0 + assert ndel == 0 + + # new ip on server 1 + dvs.servers[1].runcmd("ifconfig eth0 13.0.0.3/24") + dvs.servers[1].runcmd("ip route add default via 13.0.0.1") + + # Ping should work between servers via vs port interfaces + ping_stats = dvs.servers[0].runcmd("ping -c 1 13.0.0.3") + assert ping_stats == 0 + + # new neighbor learn on vs + (status, fvs) = tbl.get("Ethernet4:13.0.0.3") + assert status == True + + # flush all neigh entries + dvs.runcmd("ip link set group default arp off") + dvs.runcmd("ip link set group default arp on") + + # remove interface Ethernet4 from vrf_1, add it to vrf_2 + intf_tbl._del("Ethernet4|13.0.0.1/24") + intf_tbl._del("Ethernet4") + time.sleep(1) + + intf_tbl = swsscommon.Table(conf_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("vrf_name", "Vrf_2")]) + intf_tbl.set("Ethernet4", fvs) + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + intf_tbl.set("Ethernet4|13.0.0.1/24", fvs) + time.sleep(1) + + # Ping should not work + ping_stats = dvs.servers[0].runcmd("ping -c 1 13.0.0.3") + assert ping_stats != 0 + + # remove interface Ethernet0 from vrf_1, add it to vrf_2 + intf_tbl._del("Ethernet0|12.0.0.1/24") + intf_tbl._del("Ethernet0") + time.sleep(1) + fvs = swsscommon.FieldValuePairs([("vrf_name", "Vrf_2")]) + intf_tbl.set("Ethernet0", fvs) + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + intf_tbl.set("Ethernet0|12.0.0.1/24", fvs) + time.sleep(1) + + # Ping should work between servers via vs port interfaces + ping_stats = dvs.servers[0].runcmd("ping -c 1 13.0.0.3") + assert ping_stats == 0 + + (status, fvs) = tbl.get("Ethernet4:13.0.0.3") + assert status == True + + intf_tbl._del("Ethernet0|12.0.0.1/24") + intf_tbl._del("Ethernet4|13.0.0.1/24") + intf_tbl._del("Ethernet0") + intf_tbl._del("Ethernet4") + del_entry_tbl(conf_db, "VRF", "Vrf_1") + del_entry_tbl(conf_db, "VRF", "Vrf_2") + dvs.servers[0].runcmd("ifconfig eth0 0") + dvs.servers[1].runcmd("ifconfig eth0 0") + time.sleep(2) From 40755bd9a66903ea4b3753f3c8b0bf4dbfb7c163 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 26 Jun 2019 02:53:43 -0700 Subject: [PATCH 14/27] Filter neighbor netlink state NUD_NOARP for vrf/lo interface --- neighsyncd/neighsync.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index c1f83c3aac..69ca563f2e 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -70,6 +70,11 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) key+= ipStr; int state = rtnl_neigh_get_state(neigh); + if (state == NUD_NOARP) + { + return; + } + bool delete_key = false; if ((nlmsg_type == RTM_DELNEIGH) || (state == NUD_INCOMPLETE) || (state == NUD_FAILED)) From 280c6d3add2223828c7fd56733e4e18e56e1930e Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 10 Jul 2019 20:43:03 -0700 Subject: [PATCH 15/27] Interface set new attributes except vrf --- cfgmgr/intfmgr.cpp | 32 ++++++++++++++++++++++++++++---- cfgmgr/intfmgr.h | 3 ++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 5c7c4f5be1..a03ea89db7 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -51,7 +51,7 @@ void IntfMgr::setIntfIp(const string &alias, const string &opCmd, } } -void IntfMgr::setIntfVrf(const string &alias, const string vrfName) +void IntfMgr::setIntfVrf(const string &alias, const string &vrfName) { stringstream cmd; string res; @@ -118,6 +118,29 @@ bool IntfMgr::isIntfGeneralDone(const string &alias) return false; } +bool IntfMgr::isIntfChangeVrf(const string &alias, const string &vrfName) +{ + vector temp; + + if (m_stateIntfTable.get(alias, temp)) + { + for (auto idx : temp) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == "vrf") + { + if (value == vrfName) + return false; + else + return true; + } + } + } + + return false; +} + bool IntfMgr::isIntfStateOk(const string &alias) { vector temp; @@ -201,9 +224,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, return false; } - /* if intfGeneral has been done then skip */ - if (isIntfGeneralDone(alias)) + /* if to change vrf then skip */ + if (isIntfChangeVrf(alias, vrf_name)) { + SWSS_LOG_ERROR("%s can not change to %s directly, skipping", alias.c_str(), vrf_name.c_str()); return true; } if (is_lo) @@ -215,7 +239,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, setIntfVrf(alias, vrf_name); } m_appIntfTableProducer.set(alias, data); - m_stateIntfTable.hset(alias, "state", "ok"); + m_stateIntfTable.hset(alias, "vrf", vrf_name); } else if (op == DEL_COMMAND) { diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index e8ab81bf86..a03f4a4c08 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -22,12 +22,13 @@ class IntfMgr : public Orch Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable; void setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr, const bool ipv4 = true); - void setIntfVrf(const string &alias, const string vrfName); + void setIntfVrf(const string &alias, const string &vrfName); bool doIntfGeneralTask(const vector& keys, const vector& data, const string& op); bool doIntfAddrTask(const vector& keys, const vector& data, const string& op); void doTask(Consumer &consumer); bool isIntfStateOk(const string &alias); bool isIntfGeneralDone(const string &alias); + bool isIntfChangeVrf(const string &alias, const string &vrfName); int getIntfIpCount(const string &alias); void addLoopbackIntf(const string &alias); void delLoopbackIntf(const string &alias); From 306a40edbc31feb773f5e93ea0349fe7e1d93821 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Tue, 23 Jul 2019 20:07:57 -0700 Subject: [PATCH 16/27] Do overlap checking on all intfs in same vrf --- orchagent/intfsorch.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 856fbcc676..ec4d7bfbeb 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -207,22 +207,30 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre * we should wait until entry with /8 netmask will be removed. * Time frame between those event is quite small.*/ bool overlaps = false; - for (const auto &prefixIt: m_syncdIntfses[alias].ip_addresses) + for (const auto &intfsIt: m_syncdIntfses) { - if (prefixIt.isAddressInSubnet(ip_prefix->getIp()) || - ip_prefix->isAddressInSubnet(prefixIt.getIp())) + if (port.m_vr_id != intfsIt.second.vrf_id) { - overlaps = true; - SWSS_LOG_NOTICE("Router interface %s IP %s overlaps with %s.", port.m_alias.c_str(), - prefixIt.to_string().c_str(), ip_prefix->to_string().c_str()); - break; + continue; } - } - if (overlaps) - { - /* Overlap of IP address network */ - return false; + for (const auto &prefixIt: intfsIt.second.ip_addresses) + { + if (prefixIt.isAddressInSubnet(ip_prefix->getIp()) || + ip_prefix->isAddressInSubnet(prefixIt.getIp())) + { + overlaps = true; + SWSS_LOG_NOTICE("Router interface %s IP %s overlaps with %s.", port.m_alias.c_str(), + prefixIt.to_string().c_str(), ip_prefix->to_string().c_str()); + break; + } + } + + if (overlaps) + { + /* Overlap of IP address network */ + return false; + } } addIp2MeRoute(port.m_vr_id, *ip_prefix); From 340b12584a6331c8c469465b35d1584003b5c6a5 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Tue, 23 Jul 2019 23:39:53 -0700 Subject: [PATCH 17/27] intfmgrd throw to log when execute command failed --- cfgmgr/intfmgr.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index a03ea89db7..6fc849751c 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -64,7 +64,11 @@ void IntfMgr::setIntfVrf(const string &alias, const string &vrfName) { cmd << IP_CMD << " link set " << alias << " nomaster"; } - EXEC_WITH_ERROR_THROW(cmd.str(), res); + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } } void IntfMgr::addLoopbackIntf(const string &alias) @@ -100,7 +104,12 @@ int IntfMgr::getIntfIpCount(const string &alias) string res; cmd << IP_CMD << " address show " << alias << " | grep inet | grep -v 'inet6 fe80:' | wc -l"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return 0; + } return std::stoi(res); } From d2b835e674dbd538e0655714ef0866625d2119b8 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Thu, 22 Aug 2019 02:48:53 -0700 Subject: [PATCH 18/27] Revise codes based on review and test --- cfgmgr/intfmgr.cpp | 8 ++++++-- cfgmgr/vrfmgr.cpp | 21 +++++++++++++++++++-- doc/swss-schema.md | 6 ++++-- orchagent/intfsorch.cpp | 20 +++++++++++++++++--- orchagent/intfsorch.h | 2 +- orchagent/neighorch.h | 21 ++++++++++++++++++--- orchagent/routeorch.cpp | 2 -- orchagent/vrforch.h | 18 ++++++++++++++++++ 8 files changed, 83 insertions(+), 15 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 6fc849751c..3c1fa46925 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -103,7 +103,11 @@ int IntfMgr::getIntfIpCount(const string &alias) stringstream cmd; string res; - cmd << IP_CMD << " address show " << alias << " | grep inet | grep -v 'inet6 fe80:' | wc -l"; + /* query ip address of the device with master name, it is much faster */ + cmd << IP_CMD << " address show " << alias + << " $(" << IP_CMD << " link show " << alias << " | grep -o 'master [^\\s]*')" + << " | grep inet | grep -v 'inet6 fe80:' | wc -l"; + int ret = swss::exec(cmd.str(), res); if (ret) { @@ -144,7 +148,7 @@ bool IntfMgr::isIntfChangeVrf(const string &alias, const string &vrfName) else return true; } - } + } } return false; diff --git a/cfgmgr/vrfmgr.cpp b/cfgmgr/vrfmgr.cpp index c0dc2569f6..fcaaf175ea 100644 --- a/cfgmgr/vrfmgr.cpp +++ b/cfgmgr/vrfmgr.cpp @@ -63,6 +63,18 @@ VrfMgr::VrfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, con break; } } + + cmd.str(""); + cmd.clear(); + cmd << IP_CMD << " rule | grep '^0:'"; + if (swss::exec(cmd.str(), res) == 0) + { + cmd.str(""); + cmd.clear(); + cmd << IP_CMD << " rule add pref 1001 table local && " << IP_CMD << " rule del pref 0 && " + << IP_CMD << " -6 rule add pref 1001 table local && " << IP_CMD << " -6 rule del pref 0"; + EXEC_WITH_ERROR_THROW(cmd.str(), res); + } } uint32_t VrfMgr::getFreeTable(void) @@ -144,8 +156,13 @@ int VrfMgr::getVrfIntfCount(const string& vrfName) stringstream cmd; string res; - cmd << IP_CMD << " link show " << " | grep 'master " << vrfName << "' | wc -l"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + cmd << IP_CMD << " link show master " << vrfName << " | wc -l"; + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return 0; + } return std::stoi(res); } diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 7d934b876b..9e704c3133 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -456,8 +456,10 @@ Stores rules associated with a specific ACL table on the switch. ; it could be: : name of physical port. Example: "Ethernet10" : name of LAG port Example: "PortChannel5" - : next-hop ip address Example: "10.0.0.1" - : next-hop group set of addresses Example: "10.0.0.1,10.0.0.3" + : next-hop ip address (in global) Example: "10.0.0.1" + : next-hop ip address and vrf Example: "10.0.0.2|Vrf2" + : next-hop ip address and ifname Example: "10.0.0.3|Ethernet1" + : next-hop group set of next-hop Example: "10.0.0.1,10.0.0.3|Ethernet1" mirror_action = 1*255VCHAR ; refer to the mirror session diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 239a97ac08..3dae01e6d8 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -83,29 +83,43 @@ sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias) return port.m_rif_id; } -bool IntfsOrch::isPrefixSubnet(const IpPrefix &ip_prefix, const string& alias) +bool IntfsOrch::isPrefixSubnet(const IpPrefix &ip_prefix, const string &alias) { if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) + { return false; + } for (auto &prefixIt: m_syncdIntfses[alias].ip_addresses) { if (prefixIt.getSubnet() == ip_prefix) + { return true; + } } return false; } -string IntfsOrch::getRouterIntfsAlias(const IpAddress &ip, sai_object_id_t vrf_id) +string IntfsOrch::getRouterIntfsAlias(const IpAddress &ip, const string &vrf_name) { + sai_object_id_t vrf_id = gVirtualRouterId; + + if (!vrf_name.empty()) + { + vrf_id = m_vrfOrch->getVRFid(vrf_name); + } + for (const auto &it_intfs: m_syncdIntfses) { if (it_intfs.second.vrf_id != vrf_id) + { continue; - + } for (const auto &prefixIt: it_intfs.second.ip_addresses) { if (prefixIt.isAddressInSubnet(ip)) + { return it_intfs.first; + } } } return string(); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 1c8d771b81..5ac0d047ed 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -34,7 +34,7 @@ class IntfsOrch : public Orch sai_object_id_t getRouterIntfsId(const string&); bool isPrefixSubnet(const IpPrefix&, const string&); - string getRouterIntfsAlias(const IpAddress &ip, sai_object_id_t vrf_id = gVirtualRouterId); + string getRouterIntfsAlias(const IpAddress &ip, const string &vrf_name = ""); void increaseRouterIntfsRefCount(const string&); void decreaseRouterIntfsRefCount(const string&); diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 1fc1ed6cd0..7f3136851a 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -11,6 +11,9 @@ #define NHFLAGS_IFDOWN 0x1 // nexthop's outbound i/f is down +#define VRF_PREFIX "Vrf" +extern IntfsOrch *gIntfsOrch; + struct NextHopKey { IpAddress ip_address; // neighbor IP address @@ -27,13 +30,25 @@ struct NextHopKey throw std::invalid_argument(err); } auto keys = tokenize(str, '|'); - if (keys.size() != 2) + if (keys.size() == 1) + { + ip_address = keys[0]; + alias = gIntfsOrch->getRouterIntfsAlias(ip_address); + } + else if (keys.size() == 2) + { + ip_address = keys[0]; + alias = keys[1]; + if (!alias.compare(0, strlen(VRF_PREFIX), VRF_PREFIX)) + { + alias = gIntfsOrch->getRouterIntfsAlias(ip_address, alias); + } + } + else { std::string err = "Error converting " + str + " to NextHop"; throw std::invalid_argument(err); } - ip_address = keys[0]; - alias = keys[1]; } const std::string to_string() const { diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 50108f1b33..e8252d9922 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -4,8 +4,6 @@ #include "swssnet.h" #include "crmorch.h" -#define VRF_PREFIX "Vrf" - extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gSwitchId; diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h index 6a9d8661e9..93f0c47ef9 100644 --- a/orchagent/vrforch.h +++ b/orchagent/vrforch.h @@ -50,43 +50,61 @@ class VRFOrch : public Orch2 sai_object_id_t getVRFid(const std::string& name) const { if (vrf_table_.find(name) != std::end(vrf_table_)) + { return vrf_table_.at(name).vrf_id; + } else + { return gVirtualRouterId; + } } string getVRFname(sai_object_id_t vrf_id) const { if (vrf_id == gVirtualRouterId) + { return string(""); + } if (vrf_id_table_.find(vrf_id) != std::end(vrf_id_table_)) + { return vrf_id_table_.at(vrf_id); + } else + { return string(""); + } } void increaseVrfRefCount(const std::string& name) { if (vrf_table_.find(name) != std::end(vrf_table_)) + { vrf_table_.at(name).ref_count++; + } } void increaseVrfRefCount(sai_object_id_t vrf_id) { if (vrf_id != gVirtualRouterId) + { increaseVrfRefCount(getVRFname(vrf_id)); + } } void decreaseVrfRefCount(const std::string& name) { if (vrf_table_.find(name) != std::end(vrf_table_)) + { vrf_table_.at(name).ref_count--; + } } void decreaseVrfRefCount(sai_object_id_t vrf_id) { if (vrf_id != gVirtualRouterId) + { decreaseVrfRefCount(getVRFname(vrf_id)); + } } private: From 839683341e4d6fa55827da26f0b2c54659cb2559 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 4 Sep 2019 20:48:55 -0700 Subject: [PATCH 19/27] Fix vrf delete problem by delaying delLink --- cfgmgr/vrfmgr.cpp | 52 ++++++++++++++++++++++++---------------- cfgmgr/vrfmgr.h | 2 +- orchagent/orchdaemon.cpp | 2 +- orchagent/vrforch.cpp | 4 ++++ orchagent/vrforch.h | 5 +++- 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/cfgmgr/vrfmgr.cpp b/cfgmgr/vrfmgr.cpp index fcaaf175ea..dd2cefa14d 100644 --- a/cfgmgr/vrfmgr.cpp +++ b/cfgmgr/vrfmgr.cpp @@ -10,6 +10,7 @@ #define VRF_TABLE_START 1001 #define VRF_TABLE_END 2000 +#define OBJ_PREFIX "OBJ" using namespace swss; @@ -151,20 +152,18 @@ bool VrfMgr::setLink(const string& vrfName) return true; } -int VrfMgr::getVrfIntfCount(const string& vrfName) +bool VrfMgr::isVrfObjExist(const string& vrfName) { - stringstream cmd; - string res; + vector temp; + string key = string(OBJ_PREFIX) + state_db_key_delimiter + vrfName; - cmd << IP_CMD << " link show master " << vrfName << " | wc -l"; - int ret = swss::exec(cmd.str(), res); - if (ret) + if (m_stateVrfTable.get(key, temp)) { - SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); - return 0; + SWSS_LOG_DEBUG("Vrf %s object exist", vrfName.c_str()); + return true; } - return std::stoi(res); + return false; } void VrfMgr::doTask(Consumer &consumer) @@ -201,7 +200,29 @@ void VrfMgr::doTask(Consumer &consumer) } else if (op == DEL_COMMAND) { - if (getVrfIntfCount(vrfName)) + vector temp; + + if (m_stateVrfTable.get(vrfName, temp)) + { + if (!isVrfObjExist(vrfName)) + { + it++; + continue; + } + + m_stateVrfTable.del(vrfName); + + if (consumer.getTableName() == CFG_VRF_TABLE_NAME) + { + m_appVrfTableProducer.del(vrfName); + } + else + { + m_appVnetTableProducer.del(vrfName); + } + } + + if (isVrfObjExist(vrfName)) { it++; continue; @@ -212,17 +233,6 @@ void VrfMgr::doTask(Consumer &consumer) SWSS_LOG_ERROR("Failed to remove vrf netdev %s", vrfName.c_str()); } - m_stateVrfTable.del(vrfName); - - if (consumer.getTableName() == CFG_VRF_TABLE_NAME) - { - m_appVrfTableProducer.del(vrfName); - } - else - { - m_appVnetTableProducer.del(vrfName); - } - SWSS_LOG_NOTICE("Removed vrf netdev %s", vrfName.c_str()); } else diff --git a/cfgmgr/vrfmgr.h b/cfgmgr/vrfmgr.h index d2e089110d..46f96699ba 100644 --- a/cfgmgr/vrfmgr.h +++ b/cfgmgr/vrfmgr.h @@ -21,7 +21,7 @@ class VrfMgr : public Orch private: bool delLink(const string& vrfName); bool setLink(const string& vrfName); - int getVrfIntfCount(const string& vrfName); + bool isVrfObjExist(const string& vrfName); void recycleTable(uint32_t table); uint32_t getFreeTable(void); void handleVnetConfigSet(KeyOpFieldsValuesTuple &t); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 4700ffe487..d69e14bc89 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -109,7 +109,7 @@ bool OrchDaemon::init() gDirectory.set(cfg_vnet_rt_orch); VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, vnet_orch); gDirectory.set(vnet_rt_orch); - VRFOrch *vrf_orch = new VRFOrch(m_applDb, APP_VRF_TABLE_NAME); + VRFOrch *vrf_orch = new VRFOrch(m_applDb, APP_VRF_TABLE_NAME, m_stateDb, STATE_VRF_TABLE_NAME); gDirectory.set(vrf_orch); gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, vrf_orch); diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index 9853d8e1c8..fd53cca151 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -11,6 +11,8 @@ #include "request_parser.h" #include "vrforch.h" +#define OBJ_PREFIX "OBJ" + extern sai_virtual_router_api_t* sai_virtual_router_api; extern sai_object_id_t gSwitchId; @@ -81,6 +83,7 @@ bool VRFOrch::addOperation(const Request& request) vrf_table_[vrf_name].vrf_id = router_id; vrf_table_[vrf_name].ref_count = 0; vrf_id_table_[router_id] = vrf_name; + m_stateVrfTable.hset(string(OBJ_PREFIX) + state_db_key_delimiter + vrf_name, "state", "ok"); SWSS_LOG_NOTICE("VRF '%s' was added", vrf_name.c_str()); } else @@ -129,6 +132,7 @@ bool VRFOrch::delOperation(const Request& request) vrf_table_.erase(vrf_name); vrf_id_table_.erase(router_id); + m_stateVrfTable.del(string(OBJ_PREFIX) + state_db_key_delimiter + vrf_name); SWSS_LOG_NOTICE("VRF '%s' was removed", vrf_name.c_str()); diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h index 93f0c47ef9..64bb55974f 100644 --- a/orchagent/vrforch.h +++ b/orchagent/vrforch.h @@ -38,7 +38,9 @@ class VRFRequest : public Request class VRFOrch : public Orch2 { public: - VRFOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) + VRFOrch(DBConnector *appDb, const std::string& appTableName, DBConnector *stateDb, const std::string& stateTableName) : + Orch2(appDb, appTableName, request_), + m_stateVrfTable(stateDb, stateTableName) { } @@ -114,6 +116,7 @@ class VRFOrch : public Orch2 VRFTable vrf_table_; VRFId2NameTable vrf_id_table_; VRFRequest request_; + Table m_stateVrfTable; }; #endif // __VRFORCH_H From 78c4001c8d9178f62a2936972495efce6d49c290 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Fri, 6 Sep 2019 00:09:41 -0700 Subject: [PATCH 20/27] [test]: Fix set interface in configuration database INTF_NAME entry is not deleted after INTF_NAME|PREFIX deleted --- tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/conftest.py b/tests/conftest.py index c510e441cd..aec99b5700 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -736,6 +736,7 @@ def remove_ip_address(self, interface, ip): tbl_name = "INTERFACE" tbl = swsscommon.Table(self.cdb, tbl_name) tbl._del(interface + "|" + ip); + tbl._del(interface); time.sleep(1) def set_mtu(self, interface, mtu): From 778f324dec9c9a71242e8996a2f38356bb5ac9b3 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Tue, 10 Sep 2019 01:17:16 -0700 Subject: [PATCH 21/27] Fix vs test cases failed --- orchagent/vnetorch.cpp | 6 +++--- tests/test_vnet.py | 13 +++++++++++-- tests/test_vrf.py | 4 ++-- tests/test_warm_reboot.py | 8 ++++---- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 7f3a5e112d..a65289147c 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -1165,7 +1165,7 @@ bool VNetBitmapObject::addRoute(IpPrefix& ipPrefix, nextHop& nh) Port port; RouteInfo routeInfo; - bool is_subnet = (!nh.ips.getSize()) ? true : false; + bool is_subnet = (!nh.ips.getSize() || nh.ips.contains("0.0.0.0")) ? true : false; if (is_subnet && (!gPortsOrch->getPort(nh.ifname, port) || (port.m_rif_id == SAI_NULL_OBJECT_ID))) { @@ -1721,7 +1721,7 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP return true; } - bool is_subnet = (!nh.ips.getSize())?true:false; + bool is_subnet = (!nh.ips.getSize() || nh.ips.contains("0.0.0.0")) ? true : false; Port port; if (is_subnet && (!gPortsOrch->getPort(nh.ifname, port) || (port.m_rif_id == SAI_NULL_OBJECT_ID))) @@ -1745,7 +1745,7 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP } else if (vr_id == port.m_vr_id) { - vr_set.insert(vrf_obj->getVRidEgress()); + vr_set = vrf_obj->getVRids(); } else { diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 5eac68381c..7c6ef8eb2c 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -254,6 +254,10 @@ def delete_vlan_interface(dvs, ifname, ipaddr): time.sleep(2) + delete_entry_tbl(conf_db, "VLAN_INTERFACE", ifname) + + time.sleep(2) + def create_phy_interface(dvs, ifname, vnet_name, ipaddr): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) @@ -296,6 +300,10 @@ def delete_phy_interface(dvs, ifname, ipaddr): time.sleep(2) + delete_entry_tbl(conf_db, "INTERFACE", ifname) + + time.sleep(2) + def create_vnet_entry(dvs, name, tunnel, vni, peer_list, scope=""): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) @@ -545,6 +553,7 @@ def vnet_route_ids(self, dvs, name, local=False): vr_set = set() if local: + vr_set.add(self.vr_map[name].get('ing')) vr_set.add(self.vr_map[name].get('egr')) else: vr_set.add(self.vr_map[name].get('ing')) @@ -577,8 +586,8 @@ def check_router_interface(self, dvs, name, vlan_oid=0): new_rif = get_created_entry(asic_db, self.ASIC_RIF_TABLE, self.rifs) check_object(asic_db, self.ASIC_RIF_TABLE, new_rif, expected_attr) - #IP2ME and subnet routes will be created with every router interface - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 2) + #IP2ME route will be created with every router interface + new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 1) self.rifs.add(new_rif) self.routes.update(new_route) diff --git a/tests/test_vrf.py b/tests/test_vrf.py index c288c5266f..d8d6c9595b 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -268,7 +268,7 @@ def create_entry_tbl(self, db, table, key, pairs): tbl.set("Vrf_%d" % i, fvs) # wait for all VRFs pushed to database and linux - time.sleep(15) + time.sleep(30) # check app_db intf_entries_cnt = self.how_many_entries_exist(self.pdb, "VRF_TABLE") @@ -287,7 +287,7 @@ def create_entry_tbl(self, db, table, key, pairs): tbl._del("Vrf_%d" % i) # wait for all VRFs deleted - time.sleep(60) + time.sleep(120) # check app_db intf_entries_cnt = self.how_many_entries_exist(self.pdb, "VRF_TABLE") diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index fc8ca3debd..425cd17169 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -1365,7 +1365,7 @@ def test_routing_WarmRestart(self, dvs, testlog): assert len(addobjs) == 1 and len(delobjs) == 0 rt_key = json.loads(addobjs[0]['key']) rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.1.3" + assert rt_key == "192.168.1.3/32" assert rt_val == {"ifname": "Ethernet0,Ethernet4,Ethernet8", "nexthop": "111.0.0.2,122.0.0.2,133.0.0.2"} # Verify the changed prefix is seen in sairedis @@ -1403,7 +1403,7 @@ def test_routing_WarmRestart(self, dvs, testlog): assert len(addobjs) == 1 and len(delobjs) == 0 rt_key = json.loads(addobjs[0]['key']) rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.1.3" + assert rt_key == "192.168.1.3/32" assert rt_val == {"ifname": "Ethernet0,Ethernet4", "nexthop": "111.0.0.2,122.0.0.2"} # Verify the changed prefix is seen in sairedis @@ -1440,7 +1440,7 @@ def test_routing_WarmRestart(self, dvs, testlog): assert len(addobjs) == 1 and len(delobjs) == 0 rt_key = json.loads(addobjs[0]['key']) rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "fc00:4:4::1" + assert rt_key == "fc00:4:4::1/128" assert rt_val == {"ifname": "Ethernet0", "nexthop": "1110::2"} # Verify the changed prefix is seen in sairedis @@ -1475,7 +1475,7 @@ def test_routing_WarmRestart(self, dvs, testlog): (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) assert len(addobjs) == 0 and len(delobjs) == 1 rt_key = json.loads(delobjs[0]['key']) - assert rt_key == "fc00:4:4::1" + assert rt_key == "fc00:4:4::1/128" # Verify the changed prefix is seen in sairedis (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) From 3e762aca1c4353fc8347819c1779eed2630d557b Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Mon, 16 Sep 2019 02:02:17 -0700 Subject: [PATCH 22/27] Fix vrf delete problem by delaying delLink --- cfgmgr/vrfmgr.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/cfgmgr/vrfmgr.cpp b/cfgmgr/vrfmgr.cpp index dd2cefa14d..9d0fd0f65e 100644 --- a/cfgmgr/vrfmgr.cpp +++ b/cfgmgr/vrfmgr.cpp @@ -200,32 +200,38 @@ void VrfMgr::doTask(Consumer &consumer) } else if (op == DEL_COMMAND) { - vector temp; - - if (m_stateVrfTable.get(vrfName, temp)) + /* + * Delay delLink until vrf object deleted in orchagent to ensure fpmsyncd can get vrf ifname. + * Now state VRF_TABLE|Vrf represent vrf exist in appDB, if it exist vrf device is always effective. + * VRFOrch add/del state VRF_TABLE|OBJ|Vrf to represent object existence. VNETOrch is not do so now. + */ + if (consumer.getTableName() == CFG_VRF_TABLE_NAME) { - if (!isVrfObjExist(vrfName)) - { - it++; - continue; - } - - m_stateVrfTable.del(vrfName); + vector temp; - if (consumer.getTableName() == CFG_VRF_TABLE_NAME) + if (m_stateVrfTable.get(vrfName, temp)) { + /* VRFOrch add delay so wait */ + if (!isVrfObjExist(vrfName)) + { + it++; + continue; + } + m_appVrfTableProducer.del(vrfName); + m_stateVrfTable.del(vrfName); } - else + + if (isVrfObjExist(vrfName)) { - m_appVnetTableProducer.del(vrfName); + it++; + continue; } } - - if (isVrfObjExist(vrfName)) + else { - it++; - continue; + m_appVnetTableProducer.del(vrfName); + m_stateVrfTable.del(vrfName); } if (!delLink(vrfName)) From bb61e52ce3a7bb007386156e7a29d317b1461f0c Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 25 Sep 2019 02:51:46 -0700 Subject: [PATCH 23/27] create separate file for NextHopKey and NextHopGroupKey --- orchagent/neighorch.h | 62 +----------------- orchagent/nexthopgroupkey.h | 126 ++++++++++++++++++++++++++++++++++++ orchagent/nexthopkey.h | 67 +++++++++++++++++++ orchagent/routeorch.h | 122 +--------------------------------- 4 files changed, 195 insertions(+), 182 deletions(-) create mode 100644 orchagent/nexthopgroupkey.h create mode 100644 orchagent/nexthopkey.h diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 7f3136851a..60c40f9053 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -7,70 +7,10 @@ #include "intfsorch.h" #include "ipaddress.h" -#include "tokenize.h" +#include "nexthopkey.h" #define NHFLAGS_IFDOWN 0x1 // nexthop's outbound i/f is down -#define VRF_PREFIX "Vrf" -extern IntfsOrch *gIntfsOrch; - -struct NextHopKey -{ - IpAddress ip_address; // neighbor IP address - string alias; // incoming interface alias - - NextHopKey() = default; - NextHopKey(const std::string &ipstr, const std::string &alias) : ip_address(ipstr), alias(alias) {} - NextHopKey(const IpAddress &ip, const std::string &alias) : ip_address(ip), alias(alias) {} - NextHopKey(const std::string &str) - { - if (str.find(',') != string::npos) - { - std::string err = "Error converting " + str + " to NextHop"; - throw std::invalid_argument(err); - } - auto keys = tokenize(str, '|'); - if (keys.size() == 1) - { - ip_address = keys[0]; - alias = gIntfsOrch->getRouterIntfsAlias(ip_address); - } - else if (keys.size() == 2) - { - ip_address = keys[0]; - alias = keys[1]; - if (!alias.compare(0, strlen(VRF_PREFIX), VRF_PREFIX)) - { - alias = gIntfsOrch->getRouterIntfsAlias(ip_address, alias); - } - } - else - { - std::string err = "Error converting " + str + " to NextHop"; - throw std::invalid_argument(err); - } - } - const std::string to_string() const - { - return ip_address.to_string() + "|" + alias; - } - - bool operator<(const NextHopKey &o) const - { - return tie(ip_address, alias) < tie(o.ip_address, o.alias); - } - - bool operator==(const NextHopKey &o) const - { - return (ip_address == o.ip_address) && (alias == o.alias); - } - - bool operator!=(const NextHopKey &o) const - { - return !(*this == o); - } -}; - typedef NextHopKey NeighborEntry; struct NextHopEntry diff --git a/orchagent/nexthopgroupkey.h b/orchagent/nexthopgroupkey.h new file mode 100644 index 0000000000..bc3dcef35b --- /dev/null +++ b/orchagent/nexthopgroupkey.h @@ -0,0 +1,126 @@ +#ifndef SWSS_NEXTHOPGROUPKEY_H +#define SWSS_NEXTHOPGROUPKEY_H + +#include "nexthopkey.h" + +class NextHopGroupKey +{ +public: + NextHopGroupKey() = default; + + /* ip_string|if_alias separated by ',' */ + NextHopGroupKey(const std::string &nexthops) + { + auto nhv = tokenize(nexthops, ','); + for (const auto &nh : nhv) + { + m_nexthops.insert(nh); + } + } + + inline const std::set &getNextHops() const + { + return m_nexthops; + } + + inline size_t getSize() const + { + return m_nexthops.size(); + } + + inline bool operator<(const NextHopGroupKey &o) const + { + return m_nexthops < o.m_nexthops; + } + + inline bool operator==(const NextHopGroupKey &o) const + { + return m_nexthops == o.m_nexthops; + } + + inline bool operator!=(const NextHopGroupKey &o) const + { + return !(*this == o); + } + + void add(const std::string &ip, const std::string &alias) + { + m_nexthops.emplace(ip, alias); + } + + void add(const std::string &nh) + { + m_nexthops.insert(nh); + } + + void add(const NextHopKey &nh) + { + m_nexthops.insert(nh); + } + + bool contains(const std::string &ip, const std::string &alias) const + { + NextHopKey nh(ip, alias); + return m_nexthops.find(nh) != m_nexthops.end(); + } + + bool contains(const std::string &nh) const + { + return m_nexthops.find(nh) != m_nexthops.end(); + } + + bool contains(const NextHopKey &nh) const + { + return m_nexthops.find(nh) != m_nexthops.end(); + } + + bool contains(const NextHopGroupKey &nhs) const + { + for (const auto &nh : nhs.getNextHops()) + { + if (!contains(nh)) + { + return false; + } + } + return true; + } + + void remove(const std::string &ip, const std::string &alias) + { + NextHopKey nh(ip, alias); + m_nexthops.erase(nh); + } + + void remove(const std::string &nh) + { + m_nexthops.erase(nh); + } + + void remove(const NextHopKey &nh) + { + m_nexthops.erase(nh); + } + + const std::string to_string() const + { + string nhs_str; + + for (auto it = m_nexthops.begin(); it != m_nexthops.end(); ++it) + { + if (it != m_nexthops.begin()) + { + nhs_str += ","; + } + + nhs_str += it->to_string(); + } + + return nhs_str; + } + +private: + std::set m_nexthops; +}; + +#endif /* SWSS_NEXTHOPGROUPKEY_H */ diff --git a/orchagent/nexthopkey.h b/orchagent/nexthopkey.h new file mode 100644 index 0000000000..3efd544e32 --- /dev/null +++ b/orchagent/nexthopkey.h @@ -0,0 +1,67 @@ +#ifndef SWSS_NEXTHOPKEY_H +#define SWSS_NEXTHOPKEY_H + +#include "ipaddress.h" +#include "tokenize.h" + +#define VRF_PREFIX "Vrf" +extern IntfsOrch *gIntfsOrch; + +struct NextHopKey +{ + IpAddress ip_address; // neighbor IP address + string alias; // incoming interface alias + + NextHopKey() = default; + NextHopKey(const std::string &ipstr, const std::string &alias) : ip_address(ipstr), alias(alias) {} + NextHopKey(const IpAddress &ip, const std::string &alias) : ip_address(ip), alias(alias) {} + NextHopKey(const std::string &str) + { + if (str.find(',') != string::npos) + { + std::string err = "Error converting " + str + " to NextHop"; + throw std::invalid_argument(err); + } + auto keys = tokenize(str, '|'); + if (keys.size() == 1) + { + ip_address = keys[0]; + alias = gIntfsOrch->getRouterIntfsAlias(ip_address); + } + else if (keys.size() == 2) + { + ip_address = keys[0]; + alias = keys[1]; + if (!alias.compare(0, strlen(VRF_PREFIX), VRF_PREFIX)) + { + alias = gIntfsOrch->getRouterIntfsAlias(ip_address, alias); + } + } + else + { + std::string err = "Error converting " + str + " to NextHop"; + throw std::invalid_argument(err); + } + } + const std::string to_string() const + { + return ip_address.to_string() + "|" + alias; + } + + bool operator<(const NextHopKey &o) const + { + return tie(ip_address, alias) < tie(o.ip_address, o.alias); + } + + bool operator==(const NextHopKey &o) const + { + return (ip_address == o.ip_address) && (alias == o.alias); + } + + bool operator!=(const NextHopKey &o) const + { + return !(*this == o); + } +}; + +#endif /* SWSS_NEXTHOPKEY_H */ diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index b40f51959f..6cd3114e23 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -9,133 +9,13 @@ #include "ipaddress.h" #include "ipaddresses.h" #include "ipprefix.h" -#include "tokenize.h" +#include "nexthopgroupkey.h" #include /* Maximum next hop group number */ #define NHGRP_MAX_SIZE 128 -class NextHopGroupKey -{ -public: - NextHopGroupKey() = default; - - /* ip_string|if_alias separated by ',' */ - NextHopGroupKey(const std::string &nexthops) - { - auto nhv = tokenize(nexthops, ','); - for (const auto &nh : nhv) - { - m_nexthops.insert(nh); - } - } - - inline const std::set &getNextHops() const - { - return m_nexthops; - } - - inline size_t getSize() const - { - return m_nexthops.size(); - } - - inline bool operator<(const NextHopGroupKey &o) const - { - return m_nexthops < o.m_nexthops; - } - - inline bool operator==(const NextHopGroupKey &o) const - { - return m_nexthops == o.m_nexthops; - } - - inline bool operator!=(const NextHopGroupKey &o) const - { - return !(*this == o); - } - - void add(const std::string &ip, const std::string &alias) - { - m_nexthops.emplace(ip, alias); - } - - void add(const std::string &nh) - { - m_nexthops.insert(nh); - } - - void add(const NextHopKey &nh) - { - m_nexthops.insert(nh); - } - - bool contains(const std::string &ip, const std::string &alias) const - { - NextHopKey nh(ip, alias); - return m_nexthops.find(nh) != m_nexthops.end(); - } - - bool contains(const std::string &nh) const - { - return m_nexthops.find(nh) != m_nexthops.end(); - } - - bool contains(const NextHopKey &nh) const - { - return m_nexthops.find(nh) != m_nexthops.end(); - } - - bool contains(const NextHopGroupKey &nhs) const - { - for (const auto &nh : nhs.getNextHops()) - { - if (!contains(nh)) - { - return false; - } - } - return true; - } - - void remove(const std::string &ip, const std::string &alias) - { - NextHopKey nh(ip, alias); - m_nexthops.erase(nh); - } - - void remove(const std::string &nh) - { - m_nexthops.erase(nh); - } - - void remove(const NextHopKey &nh) - { - m_nexthops.erase(nh); - } - - const std::string to_string() const - { - string nhs_str; - - for (auto it = m_nexthops.begin(); it != m_nexthops.end(); ++it) - { - if (it != m_nexthops.begin()) - { - nhs_str += ","; - } - - nhs_str += it->to_string(); - } - - return nhs_str; - } - -private: - std::set m_nexthops; -}; - typedef std::map NextHopGroupMembers; struct NextHopGroupEntry From ebd624de8fd1ba80ef69f2cfa2305ad809816184 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 25 Sep 2019 03:05:16 -0700 Subject: [PATCH 24/27] change nexthop delimiter to '@' --- doc/swss-schema.md | 6 +++--- orchagent/nexthopgroupkey.h | 6 +++--- orchagent/nexthopkey.h | 8 +++++--- orchagent/routeorch.cpp | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 255cc88e8c..b58d7cc81f 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -457,9 +457,9 @@ Stores rules associated with a specific ACL table on the switch. : name of physical port. Example: "Ethernet10" : name of LAG port Example: "PortChannel5" : next-hop ip address (in global) Example: "10.0.0.1" - : next-hop ip address and vrf Example: "10.0.0.2|Vrf2" - : next-hop ip address and ifname Example: "10.0.0.3|Ethernet1" - : next-hop group set of next-hop Example: "10.0.0.1,10.0.0.3|Ethernet1" + : next-hop ip address and vrf Example: "10.0.0.2@Vrf2" + : next-hop ip address and ifname Example: "10.0.0.3@Ethernet1" + : next-hop group set of next-hop Example: "10.0.0.1,10.0.0.3@Ethernet1" redirect_action = 1*255CHAR ; redirect parameter ; This parameter defines a destination for redirected packets diff --git a/orchagent/nexthopgroupkey.h b/orchagent/nexthopgroupkey.h index bc3dcef35b..6e90845d90 100644 --- a/orchagent/nexthopgroupkey.h +++ b/orchagent/nexthopgroupkey.h @@ -8,10 +8,10 @@ class NextHopGroupKey public: NextHopGroupKey() = default; - /* ip_string|if_alias separated by ',' */ + /* ip_string@if_alias separated by ',' */ NextHopGroupKey(const std::string &nexthops) { - auto nhv = tokenize(nexthops, ','); + auto nhv = tokenize(nexthops, NHG_DELIMITER); for (const auto &nh : nhv) { m_nexthops.insert(nh); @@ -110,7 +110,7 @@ class NextHopGroupKey { if (it != m_nexthops.begin()) { - nhs_str += ","; + nhs_str += NHG_DELIMITER; } nhs_str += it->to_string(); diff --git a/orchagent/nexthopkey.h b/orchagent/nexthopkey.h index 3efd544e32..a86aac2cc1 100644 --- a/orchagent/nexthopkey.h +++ b/orchagent/nexthopkey.h @@ -4,6 +4,8 @@ #include "ipaddress.h" #include "tokenize.h" +#define NH_DELIMITER '@' +#define NHG_DELIMITER ',' #define VRF_PREFIX "Vrf" extern IntfsOrch *gIntfsOrch; @@ -17,12 +19,12 @@ struct NextHopKey NextHopKey(const IpAddress &ip, const std::string &alias) : ip_address(ip), alias(alias) {} NextHopKey(const std::string &str) { - if (str.find(',') != string::npos) + if (str.find(NHG_DELIMITER) != string::npos) { std::string err = "Error converting " + str + " to NextHop"; throw std::invalid_argument(err); } - auto keys = tokenize(str, '|'); + auto keys = tokenize(str, NH_DELIMITER); if (keys.size() == 1) { ip_address = keys[0]; @@ -45,7 +47,7 @@ struct NextHopKey } const std::string to_string() const { - return ip_address.to_string() + "|" + alias; + return ip_address.to_string() + NH_DELIMITER + alias; } bool operator<(const NextHopKey &o) const diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index ae85a892ec..a73190ad00 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -402,10 +402,10 @@ void RouteOrch::doTask(Consumer& consumer) continue; } - string nhg_str = ipv[0] + "|" + alsv[0]; + string nhg_str = ipv[0] + NH_DELIMITER + alsv[0]; for (uint32_t i = 1; i < ipv.size(); i++) { - nhg_str += "," + ipv[i] + "|" + alsv[i]; + nhg_str += NHG_DELIMITER + ipv[i] + NH_DELIMITER + alsv[i]; } NextHopGroupKey nhg(nhg_str); From a53c365c588e8cdef0092c3a79752eab1e6fa856 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 25 Sep 2019 20:01:36 -0700 Subject: [PATCH 25/27] Revise acl redirect test case --- tests/conftest.py | 5 ++++ tests/test_acl.py | 63 ++++++----------------------------------------- 2 files changed, 13 insertions(+), 55 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index aec99b5700..85bac2329a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -758,6 +758,11 @@ def add_neighbor(self, interface, ip, mac): tbl.set(interface + ":" + ip, fvs) time.sleep(1) + def remove_neighbor(self, interface, ip): + tbl = swsscommon.ProducerStateTable(self.pdb, "NEIGH_TABLE") + tbl._del(interface + ":" + ip) + time.sleep(1) + def setup_db(self): self.pdb = swsscommon.DBConnector(0, self.redis_sock, 0) self.adb = swsscommon.DBConnector(1, self.redis_sock, 0) diff --git a/tests/test_acl.py b/tests/test_acl.py index 41fce57062..83909b7c81 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -1215,62 +1215,18 @@ def test_AclRuleIcmpV6(self, dvs, testlog): self.remove_acl_table(acl_table) - def set_admin_status(self, interface, status): - tbl = swsscommon.Table(self.cdb, "PORT") - fvs = swsscommon.FieldValuePairs([("admin_status", status)]) - tbl.set(interface, fvs) - time.sleep(1) - - def create_l3_intf(self, interface, vrf_name): - tbl = swsscommon.Table(self.cdb, "INTERFACE") - if len(vrf_name) == 0: - fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) - else: - fvs = swsscommon.FieldValuePairs([("vrf_name", vrf_name)]) - tbl.set(interface, fvs) - time.sleep(1) - - def remove_l3_intf(self, interface): - tbl = swsscommon.Table(self.cdb, "INTERFACE") - tbl._del(interface) - time.sleep(1) - - def add_ip_address(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) - tbl.set(interface + "|" + ip, fvs) - time.sleep(1) - - def remove_ip_address(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "INTERFACE") - tbl._del(interface + "|" + ip) - time.sleep(1) - - def add_neighbor(self, interface, ip, mac): - tbl = swsscommon.Table(self.cdb, "NEIGH") - fvs = swsscommon.FieldValuePairs([("neigh", mac)]) - tbl.set(interface + "|" + ip, fvs) - time.sleep(1) - - def remove_neighbor(self, interface, ip): - tbl = swsscommon.Table(self.cdb, "NEIGH") - tbl._del(interface + "|" + ip) - time.sleep(1) - def test_AclRuleRedirectToNexthop(self, dvs, testlog): + dvs.setup_db() self.setup_db(dvs) # bring up interface - self.set_admin_status("Ethernet4", "up") - - # create interface - self.create_l3_intf("Ethernet4", "") + dvs.set_interface_status("Ethernet4", "up") # assign IP to interface - self.add_ip_address("Ethernet4", "10.0.0.1/24") + dvs.add_ip_address("Ethernet4", "10.0.0.1/24") # add neighbor - self.add_neighbor("Ethernet4", "10.0.0.2", "00:01:02:03:04:05") + dvs.add_neighbor("Ethernet4", "10.0.0.2", "00:01:02:03:04:05") atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") keys = atbl.getKeys() @@ -1289,7 +1245,7 @@ def test_AclRuleRedirectToNexthop(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([ ("priority", "100"), ("L4_SRC_PORT", "65000"), - ("PACKET_ACTION", "REDIRECT:10.0.0.2|Ethernet4")]) + ("PACKET_ACTION", "REDIRECT:10.0.0.2@Ethernet4")]) tbl.set("test_redirect|test_rule1", fvs) time.sleep(1) @@ -1340,16 +1296,13 @@ def test_AclRuleRedirectToNexthop(self, dvs, testlog): assert len(keys) >= 1 # remove neighbor - self.remove_neighbor("Ethernet4", "10.0.0.2") + dvs.remove_neighbor("Ethernet4", "10.0.0.2") # remove interface ip - self.remove_ip_address("Ethernet4", "10.0.0.1/24") - - # remove interface - self.remove_l3_intf("Ethernet4") + dvs.remove_ip_address("Ethernet4", "10.0.0.1/24") # bring down interface - self.set_admin_status("Ethernet4", "down") + dvs.set_interface_status("Ethernet4", "down") class TestAclRuleValidation(BaseTestAcl): """ Test class for cases that check if orchagent corectly validates From bbca607bb8fcaa93d97d655aaec419ca6c1e89a7 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Fri, 11 Oct 2019 03:03:30 -0700 Subject: [PATCH 26/27] Address review comments --- cfgmgr/intfmgr.cpp | 11 ++++++++--- cfgmgr/intfmgr.h | 2 +- cfgmgr/vrfmgr.cpp | 14 +++++++------- cfgmgr/vrfmgr.h | 2 +- doc/swss-schema.md | 22 ++++++++++++++++++++++ fpmsyncd/routesync.cpp | 37 +++++++++---------------------------- fpmsyncd/routesync.h | 2 +- orchagent/intfsorch.cpp | 10 ++-------- orchagent/orchdaemon.cpp | 2 +- orchagent/vrforch.cpp | 6 ++---- orchagent/vrforch.h | 10 +++++----- tests/test_route.py | 10 +++++----- tests/test_warm_reboot.py | 8 ++++---- 13 files changed, 68 insertions(+), 68 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 65558da34d..241de64df9 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -17,6 +17,8 @@ using namespace swss; #define VNET_PREFIX "Vnet" #define VRF_PREFIX "Vrf" +#define LOOPBACK_DEFAULT_MTU_STR "65536" + IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME), @@ -84,7 +86,7 @@ void IntfMgr::addLoopbackIntf(const string &alias) stringstream cmd; string res; - cmd << IP_CMD << " link add " << alias << " mtu 65536 type dummy && "; + cmd << IP_CMD << " link add " << alias << " mtu " << LOOPBACK_DEFAULT_MTU_STR << " type dummy && "; cmd << IP_CMD << " link set " << alias << " up"; int ret = swss::exec(cmd.str(), res); if (ret) @@ -112,6 +114,9 @@ int IntfMgr::getIntfIpCount(const string &alias) string res; /* query ip address of the device with master name, it is much faster */ + // ip address show {{intf_name}} + // $(ip link show {{intf_name}} | grep -o 'master [^\\s]*') ==> [master {{vrf_name}}] + // | grep inet | grep -v 'inet6 fe80:' | wc -l cmd << IP_CMD << " address show " << alias << " $(" << IP_CMD << " link show " << alias << " | grep -o 'master [^\\s]*')" << " | grep inet | grep -v 'inet6 fe80:' | wc -l"; @@ -126,7 +131,7 @@ int IntfMgr::getIntfIpCount(const string &alias) return std::stoi(res); } -bool IntfMgr::isIntfGeneralDone(const string &alias) +bool IntfMgr::isIntfCreated(const string &alias) { vector temp; @@ -302,7 +307,7 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, * Don't proceed if port/LAG/VLAN and intfGeneral are not ready yet. * The pending task will be checked periodically and retried. */ - if (!isIntfStateOk(alias) || !isIntfGeneralDone(alias)) + if (!isIntfStateOk(alias) || !isIntfCreated(alias)) { SWSS_LOG_DEBUG("Interface is not ready, skipping %s", alias.c_str()); return false; diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index af7abbd912..4e82baef5e 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -27,7 +27,7 @@ class IntfMgr : public Orch bool doIntfAddrTask(const std::vector& keys, const std::vector& data, const std::string& op); void doTask(Consumer &consumer); bool isIntfStateOk(const std::string &alias); - bool isIntfGeneralDone(const std::string &alias); + bool isIntfCreated(const std::string &alias); bool isIntfChangeVrf(const std::string &alias, const std::string &vrfName); int getIntfIpCount(const std::string &alias); void addLoopbackIntf(const std::string &alias); diff --git a/cfgmgr/vrfmgr.cpp b/cfgmgr/vrfmgr.cpp index 9d0fd0f65e..4be31396b8 100644 --- a/cfgmgr/vrfmgr.cpp +++ b/cfgmgr/vrfmgr.cpp @@ -10,7 +10,7 @@ #define VRF_TABLE_START 1001 #define VRF_TABLE_END 2000 -#define OBJ_PREFIX "OBJ" +#define TABLE_LOCAL_PREF 1001 // after l3mdev-table using namespace swss; @@ -18,7 +18,8 @@ VrfMgr::VrfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, con Orch(cfgDb, tableNames), m_appVrfTableProducer(appDb, APP_VRF_TABLE_NAME), m_appVnetTableProducer(appDb, APP_VNET_TABLE_NAME), - m_stateVrfTable(stateDb, STATE_VRF_TABLE_NAME) + m_stateVrfTable(stateDb, STATE_VRF_TABLE_NAME), + m_stateVrfObjectTable(stateDb, STATE_VRF_OBJECT_TABLE_NAME) { for (uint32_t i = VRF_TABLE_START; i < VRF_TABLE_END; i++) { @@ -72,8 +73,8 @@ VrfMgr::VrfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, con { cmd.str(""); cmd.clear(); - cmd << IP_CMD << " rule add pref 1001 table local && " << IP_CMD << " rule del pref 0 && " - << IP_CMD << " -6 rule add pref 1001 table local && " << IP_CMD << " -6 rule del pref 0"; + cmd << IP_CMD << " rule add pref " << TABLE_LOCAL_PREF << " table local && " << IP_CMD << " rule del pref 0 && " + << IP_CMD << " -6 rule add pref " << TABLE_LOCAL_PREF << " table local && " << IP_CMD << " -6 rule del pref 0"; EXEC_WITH_ERROR_THROW(cmd.str(), res); } } @@ -155,9 +156,8 @@ bool VrfMgr::setLink(const string& vrfName) bool VrfMgr::isVrfObjExist(const string& vrfName) { vector temp; - string key = string(OBJ_PREFIX) + state_db_key_delimiter + vrfName; - if (m_stateVrfTable.get(key, temp)) + if (m_stateVrfObjectTable.get(vrfName, temp)) { SWSS_LOG_DEBUG("Vrf %s object exist", vrfName.c_str()); return true; @@ -203,7 +203,7 @@ void VrfMgr::doTask(Consumer &consumer) /* * Delay delLink until vrf object deleted in orchagent to ensure fpmsyncd can get vrf ifname. * Now state VRF_TABLE|Vrf represent vrf exist in appDB, if it exist vrf device is always effective. - * VRFOrch add/del state VRF_TABLE|OBJ|Vrf to represent object existence. VNETOrch is not do so now. + * VRFOrch add/del state VRF_OBJECT_TABLE|Vrf to represent object existence. VNETOrch is not do so now. */ if (consumer.getTableName() == CFG_VRF_TABLE_NAME) { diff --git a/cfgmgr/vrfmgr.h b/cfgmgr/vrfmgr.h index 40ae13387d..f8da87d318 100644 --- a/cfgmgr/vrfmgr.h +++ b/cfgmgr/vrfmgr.h @@ -30,7 +30,7 @@ class VrfMgr : public Orch std::map m_vrfTableMap; std::set m_freeTables; - Table m_stateVrfTable; + Table m_stateVrfTable, m_stateVrfObjectTable; ProducerStateTable m_appVrfTableProducer, m_appVnetTableProducer; }; diff --git a/doc/swss-schema.md b/doc/swss-schema.md index b58d7cc81f..92a67f01a0 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -878,6 +878,28 @@ Stores information for physical switch ports managed by the switch chip. Ports t full-date = date-fullyear "-" date-month "-" date-mday time-stamp = full-date %x20 partial-time +### INTERFACE_TABLE + ;State for interface status, including two types of key + + key = INTERFACE_TABLE|ifname ; ifname should be Ethernet,Portchannel,Vlan,Loopback + vrf = "" / vrf_name ; interface has been created, global or vrf + + key = INTERFACE_TABLE|ifname|IPprefix + state = "ok" ; IP address has been set to interface + +### VRF_TABLE + ;State for vrf status, vrfmgrd has written it to app_db + + key = VRF_TABLE|vrf_name ; vrf_name start with 'Vrf' or 'Vnet' prefix + state = "ok" ; vrf entry exist in app_db, if yes vrf device must exist + +### VRF_OBJECT_TABLE + ;State for vrf object status, vrf exist in vrforch + + key = VRF_OBJECT_TABLE|vrf_name ; vrf_name start with 'Vrf' prefix + state = "ok" ; vrf entry exist in orchagent + + ## Configuration files What configuration files should we have? Do apps, orch agent each need separate files? diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index 572de27bb8..2250d26d1a 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -61,13 +61,13 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) /* Otherwise, it is a regular route (include VRF route). */ else { - onRouteMsg(nlmsg_type, obj); + onRouteMsg(nlmsg_type, obj, master_name); } } else { - onRouteMsg(nlmsg_type, obj); + onRouteMsg(nlmsg_type, obj, NULL); } } @@ -75,45 +75,31 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) * Handle regular route (include VRF route) * @arg nlmsg_type Netlink message type * @arg obj Netlink object + * @arg vrf Vrf name */ -void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj) +void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char *vrf) { struct rtnl_route *route_obj = (struct rtnl_route *)obj; struct nl_addr *dip; char destipprefix[IFNAMSIZ + MAX_ADDR_SIZE + 2] = {0}; - /* - * Here vrf_index is not real table id but vrf_id in zebra. - * It is vrf interface index in kernel for vrf route and VRF_DEFAULT(0) for global route. - * Now zebra fpm only fill in a uchar, ifindex limited to 255. - */ - unsigned int vrf_index = rtnl_route_get_table(route_obj); - if (vrf_index) + if (vrf) { - if (!getIfName(vrf_index, destipprefix, IFNAMSIZ)) - { - SWSS_LOG_ERROR("Fail to get the VRF name (ifindex %u)", vrf_index); - return; - } /* * Now vrf device name is required to start with VRF_PREFIX, * it is difficult to split vrf_name:ipv6_addr. */ - if (memcmp(destipprefix, VRF_PREFIX, strlen(VRF_PREFIX))) + if (memcmp(vrf, VRF_PREFIX, strlen(VRF_PREFIX))) { - SWSS_LOG_ERROR("Invalid VRF name %s (ifindex %u)", destipprefix, vrf_index); + SWSS_LOG_ERROR("Invalid VRF name %s (ifindex %u)", vrf, rtnl_route_get_table(route_obj)); return; } - destipprefix[strlen(destipprefix)] = ':'; + memcpy(destipprefix, vrf, strlen(vrf)); + destipprefix[strlen(vrf)] = ':'; } dip = rtnl_route_get_dst(route_obj); nl_addr2str(dip, destipprefix + strlen(destipprefix), MAX_ADDR_SIZE); - /* Full mask route append prefix length, or else resync cannot match. */ - if (nl_addr_get_prefixlen(dip) == (8 * nl_addr_get_len(dip))) - { - snprintf(destipprefix + strlen(destipprefix), sizeof(destipprefix) - strlen(destipprefix), "/%u", nl_addr_get_prefixlen(dip)); - } SWSS_LOG_DEBUG("Receive new route message dest ip prefix: %s", destipprefix); /* @@ -226,11 +212,6 @@ void RouteSync::onVnetRouteMsg(int nlmsg_type, struct nl_object *obj, string vne struct nl_addr *dip = rtnl_route_get_dst(route_obj); char destipprefix[MAX_ADDR_SIZE + 1] = {0}; nl_addr2str(dip, destipprefix, MAX_ADDR_SIZE); - /* Full mask route append prefix length, or else resync cannot match. */ - if (nl_addr_get_prefixlen(dip) == (8 * nl_addr_get_len(dip))) - { - snprintf(destipprefix + strlen(destipprefix), sizeof(destipprefix) - strlen(destipprefix), "/%u", nl_addr_get_prefixlen(dip)); - } string vnet_dip = vnet + string(":") + destipprefix; SWSS_LOG_DEBUG("Receive new vnet route message %s", vnet_dip.c_str()); diff --git a/fpmsyncd/routesync.h b/fpmsyncd/routesync.h index 8ab8fc9cb4..d636ca134a 100644 --- a/fpmsyncd/routesync.h +++ b/fpmsyncd/routesync.h @@ -33,7 +33,7 @@ class RouteSync : public NetMsg struct nl_sock *m_nl_sock; /* Handle regular route (include VRF route) */ - void onRouteMsg(int nlmsg_type, struct nl_object *obj); + void onRouteMsg(int nlmsg_type, struct nl_object *obj, char *vrf); /* Handle vnet route */ void onVnetRouteMsg(int nlmsg_type, struct nl_object *obj, string vnet); diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 624b17df10..fc84a39752 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -221,6 +221,8 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre * delete IP with netmask /8. To handle this we in case of overlap * we should wait until entry with /8 netmask will be removed. * Time frame between those event is quite small.*/ + /* NOTE: Overlap checking in this interface is not enough. + * So extend to check in all interfaces of this VRF */ bool overlaps = false; for (const auto &intfsIt: m_syncdIntfses) { @@ -393,14 +395,6 @@ void IntfsOrch::doTask(Consumer &consumer) continue; } - /* Wait for the Interface entry first */ - auto it_intfs = m_syncdIntfses.find(alias); - if (ip_prefix_in_key && it_intfs == m_syncdIntfses.end()) - { - it++; - continue; - } - Port port; if (!gPortsOrch->getPort(alias, port)) { diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index ecdcb4d8a0..d83ecb8b42 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -109,7 +109,7 @@ bool OrchDaemon::init() gDirectory.set(cfg_vnet_rt_orch); VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, vnet_orch); gDirectory.set(vnet_rt_orch); - VRFOrch *vrf_orch = new VRFOrch(m_applDb, APP_VRF_TABLE_NAME, m_stateDb, STATE_VRF_TABLE_NAME); + VRFOrch *vrf_orch = new VRFOrch(m_applDb, APP_VRF_TABLE_NAME, m_stateDb, STATE_VRF_OBJECT_TABLE_NAME); gDirectory.set(vrf_orch); gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, vrf_orch); diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index 4aedcbbba6..62b56e1e0e 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -11,8 +11,6 @@ #include "request_parser.h" #include "vrforch.h" -#define OBJ_PREFIX "OBJ" - using namespace std; using namespace swss; @@ -86,7 +84,7 @@ bool VRFOrch::addOperation(const Request& request) vrf_table_[vrf_name].vrf_id = router_id; vrf_table_[vrf_name].ref_count = 0; vrf_id_table_[router_id] = vrf_name; - m_stateVrfTable.hset(string(OBJ_PREFIX) + state_db_key_delimiter + vrf_name, "state", "ok"); + m_stateVrfObjectTable.hset(vrf_name, "state", "ok"); SWSS_LOG_NOTICE("VRF '%s' was added", vrf_name.c_str()); } else @@ -135,7 +133,7 @@ bool VRFOrch::delOperation(const Request& request) vrf_table_.erase(vrf_name); vrf_id_table_.erase(router_id); - m_stateVrfTable.del(string(OBJ_PREFIX) + state_db_key_delimiter + vrf_name); + m_stateVrfObjectTable.del(vrf_name); SWSS_LOG_NOTICE("VRF '%s' was removed", vrf_name.c_str()); diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h index 8fd7c325b2..41ad55a003 100644 --- a/orchagent/vrforch.h +++ b/orchagent/vrforch.h @@ -12,7 +12,7 @@ struct VrfEntry }; typedef std::unordered_map VRFTable; -typedef std::unordered_map VRFId2NameTable; +typedef std::unordered_map VRFIdNameTable; const request_description_t request_description = { { REQ_T_STRING }, @@ -40,7 +40,7 @@ class VRFOrch : public Orch2 public: VRFOrch(swss::DBConnector *appDb, const std::string& appTableName, swss::DBConnector *stateDb, const std::string& stateTableName) : Orch2(appDb, appTableName, request_), - m_stateVrfTable(stateDb, stateTableName) + m_stateVrfObjectTable(stateDb, stateTableName) { } @@ -75,7 +75,7 @@ class VRFOrch : public Orch2 { return std::string(""); } - } + } void increaseVrfRefCount(const std::string& name) { @@ -114,9 +114,9 @@ class VRFOrch : public Orch2 virtual bool delOperation(const Request& request); VRFTable vrf_table_; - VRFId2NameTable vrf_id_table_; + VRFIdNameTable vrf_id_table_; VRFRequest request_; - swss::Table m_stateVrfTable; + swss::Table m_stateVrfObjectTable; }; #endif // __VRFORCH_H diff --git a/tests/test_route.py b/tests/test_route.py index a817011d5a..e1c12857e3 100644 --- a/tests/test_route.py +++ b/tests/test_route.py @@ -521,11 +521,11 @@ def test_RouteAndNexthopInDifferentVrf(self, dvs, testlog): # check application database tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") route_entries = tbl.getKeys() - assert "20.0.1.2/32" in route_entries + assert "20.0.1.2" in route_entries tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") route_entries = tbl.getKeys() - assert "10.0.0.2/32" in route_entries + assert "10.0.0.2" in route_entries # check ASIC neighbor interface database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") @@ -576,11 +576,11 @@ def test_RouteAndNexthopInDifferentVrf(self, dvs, testlog): # check application database tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_1") route_entries = tbl.getKeys() - assert "20.0.1.2/32" not in route_entries + assert "20.0.1.2" not in route_entries tbl = swsscommon.Table(self.pdb, "ROUTE_TABLE:Vrf_2") route_entries = tbl.getKeys() - assert "10.0.0.2/32" not in route_entries + assert "10.0.0.2" not in route_entries # check ASIC route database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") @@ -617,4 +617,4 @@ def test_RouteAndNexthopInDifferentVrf(self, dvs, testlog): dvs.servers[2].runcmd("ip route del default dev eth0") dvs.servers[2].runcmd("ip address del 20.0.0.2/24 dev eth0") dvs.servers[3].runcmd("ip route del default dev eth0") - dvs.servers[3].runcmd("ip address del 20.0.1.2/24 dev eth0") \ No newline at end of file + dvs.servers[3].runcmd("ip address del 20.0.1.2/24 dev eth0") diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index 425cd17169..fc8ca3debd 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -1365,7 +1365,7 @@ def test_routing_WarmRestart(self, dvs, testlog): assert len(addobjs) == 1 and len(delobjs) == 0 rt_key = json.loads(addobjs[0]['key']) rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.1.3/32" + assert rt_key == "192.168.1.3" assert rt_val == {"ifname": "Ethernet0,Ethernet4,Ethernet8", "nexthop": "111.0.0.2,122.0.0.2,133.0.0.2"} # Verify the changed prefix is seen in sairedis @@ -1403,7 +1403,7 @@ def test_routing_WarmRestart(self, dvs, testlog): assert len(addobjs) == 1 and len(delobjs) == 0 rt_key = json.loads(addobjs[0]['key']) rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.1.3/32" + assert rt_key == "192.168.1.3" assert rt_val == {"ifname": "Ethernet0,Ethernet4", "nexthop": "111.0.0.2,122.0.0.2"} # Verify the changed prefix is seen in sairedis @@ -1440,7 +1440,7 @@ def test_routing_WarmRestart(self, dvs, testlog): assert len(addobjs) == 1 and len(delobjs) == 0 rt_key = json.loads(addobjs[0]['key']) rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "fc00:4:4::1/128" + assert rt_key == "fc00:4:4::1" assert rt_val == {"ifname": "Ethernet0", "nexthop": "1110::2"} # Verify the changed prefix is seen in sairedis @@ -1475,7 +1475,7 @@ def test_routing_WarmRestart(self, dvs, testlog): (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) assert len(addobjs) == 0 and len(delobjs) == 1 rt_key = json.loads(delobjs[0]['key']) - assert rt_key == "fc00:4:4::1/128" + assert rt_key == "fc00:4:4::1" # Verify the changed prefix is seen in sairedis (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) From 798747791bc11eb5aa7e2c7a75ed47daa4169d42 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Sun, 3 Nov 2019 17:31:44 -0800 Subject: [PATCH 27/27] fix compile err after merge --- tests/mock_tests/aclorch_ut.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index ae4267ddb5..c6541dbded 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -352,7 +352,7 @@ namespace aclorch_test gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); ASSERT_EQ(gVrfOrch, nullptr); - gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME); + gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); ASSERT_EQ(gIntfsOrch, nullptr); gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch); @@ -361,7 +361,7 @@ namespace aclorch_test gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch); ASSERT_EQ(gRouteOrch, nullptr); - gRouteOrch = new RouteOrch(m_app_db.get(), APP_ROUTE_TABLE_NAME, gNeighOrch); + gRouteOrch = new RouteOrch(m_app_db.get(), APP_ROUTE_TABLE_NAME, gNeighOrch, gIntfsOrch, gVrfOrch); TableConnector applDbFdb(m_app_db.get(), APP_FDB_TABLE_NAME); TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME);