diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index ba022ac1..b503960d 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -123,6 +123,7 @@ orchagent_SOURCES = \ dash/dashtunnelorch.cpp \ dash/pbutils.cpp \ dash/dashhaorch.cpp \ + dash/dashportmaporch.cpp \ twamporch.cpp \ stporch.cpp diff --git a/orchagent/bulker.h b/orchagent/bulker.h index d41b1177..2153e4fe 100644 --- a/orchagent/bulker.h +++ b/orchagent/bulker.h @@ -39,6 +39,13 @@ typedef sai_status_t (*sai_bulk_set_inbound_routing_entry_attribute_fn) ( _In_ sai_bulk_op_error_mode_t mode, _Out_ sai_status_t *object_statuses); +typedef sai_status_t (*sai_bulk_set_outbound_port_map_port_range_entry_attribute_fn) ( + _In_ uint32_t object_count, + _In_ const sai_outbound_port_map_port_range_entry_t *entry, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses); + static inline bool operator==(const sai_ip_prefix_t& a, const sai_ip_prefix_t& b) { if (a.addr_family != b.addr_family) return false; @@ -139,6 +146,15 @@ static inline bool operator==(const sai_outbound_routing_entry_t& a, const sai_o ; } +static inline bool operator==(const sai_outbound_port_map_port_range_entry_t& a, const sai_outbound_port_map_port_range_entry_t& b) +{ + return a.switch_id == b.switch_id + && a.outbound_port_map_id == b.outbound_port_map_id + && a.dst_port_range.min == b.dst_port_range.min + && a.dst_port_range.max == b.dst_port_range.max + ; +} + static inline std::size_t hash_value(const sai_ip_prefix_t& a) { size_t seed = 0; @@ -276,6 +292,20 @@ namespace std return seed; } }; + + template <> + struct hash + { + size_t operator()(const sai_outbound_port_map_port_range_entry_t& a) const noexcept + { + size_t seed = 0; + boost::hash_combine(seed, a.switch_id); + boost::hash_combine(seed, a.outbound_port_map_id); + boost::hash_combine(seed, a.dst_port_range.min); + boost::hash_combine(seed, a.dst_port_range.max); + return seed; + } + }; } // SAI typedef which is not available in SAI 1.5 @@ -467,6 +497,19 @@ struct SaiBulkerTraits using bulk_remove_entry_fn = sai_bulk_object_remove_fn; }; +template<> +struct SaiBulkerTraits +{ + // Need to bulk port map objects and port map range entries from the same DASH API + // entry_t, bulk_create/remove_entry_fn are only used by EntityBulker so we can use them for + // port map port range bulking w/o affecting port map object bulking + using api_t = sai_dash_outbound_port_map_api_t; + using entry_t = sai_outbound_port_map_port_range_entry_t; + using bulk_create_entry_fn = sai_bulk_create_outbound_port_map_port_range_entry_fn; + using bulk_remove_entry_fn = sai_bulk_remove_outbound_port_map_port_range_entry_fn; + using bulk_set_entry_attribute_fn = sai_bulk_set_outbound_port_map_port_range_entry_attribute_fn; +}; + template class EntityBulker { @@ -921,6 +964,14 @@ inline EntityBulker::EntityBulker(sai_dash_outb set_entries_attribute = nullptr; } +template <> +inline EntityBulker::EntityBulker(sai_dash_outbound_port_map_api_t *api, size_t max_bulk_size) : max_bulk_size(max_bulk_size) +{ + create_entries = api->create_outbound_port_map_port_range_entries; + remove_entries = api->remove_outbound_port_map_port_range_entries; + set_entries_attribute = nullptr; +} + template class ObjectBulker { @@ -1146,8 +1197,8 @@ class ObjectBulker // object_id -> object_status std::unordered_map removing_entries; - typename Ts::bulk_create_entry_fn create_entries; - typename Ts::bulk_remove_entry_fn remove_entries; + sai_bulk_object_create_fn create_entries; + sai_bulk_object_remove_fn remove_entries; // TODO: wait until available in SAI //typename Ts::bulk_set_entry_attribute_fn set_entries_attribute; @@ -1321,3 +1372,12 @@ inline ObjectBulker::ObjectBulker(SaiBulkerTraits +inline ObjectBulker::ObjectBulker(SaiBulkerTraits::api_t *api, sai_object_id_t switch_id, size_t max_bulk_size) : + switch_id(switch_id), + max_bulk_size(max_bulk_size) +{ + create_entries = api->create_outbound_port_maps; + remove_entries = api->remove_outbound_port_maps; +} \ No newline at end of file diff --git a/orchagent/dash/dashportmaporch.cpp b/orchagent/dash/dashportmaporch.cpp new file mode 100644 index 00000000..57d6af81 --- /dev/null +++ b/orchagent/dash/dashportmaporch.cpp @@ -0,0 +1,578 @@ +#include "dashportmaporch.h" +#include "orch.h" +#include "dashorch.h" +#include "taskworker.h" +#include "bulker.h" +#include "pbutils.h" + +extern size_t gMaxBulkSize; +extern sai_dash_outbound_port_map_api_t *sai_dash_outbound_port_map_api; +extern sai_object_id_t gSwitchId; + +static const std::unordered_map + gPortMapRangeActionMap = { + {dash::outbound_port_map_range::PortMapRangeAction::ACTION_SKIP_MAPPING, + SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ACTION_SKIP_MAPPING}, + {dash::outbound_port_map_range::PortMapRangeAction::ACTION_MAP_PRIVATE_LINK_SERVICE, + SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ACTION_MAP_TO_PRIVATE_LINK_SERVICE}}; + +DashPortMapOrch::DashPortMapOrch(swss::DBConnector *db, std::vector &tables, swss::DBConnector *app_state_db, swss::ZmqServer *zmqServer) : ZmqOrch(db, tables, zmqServer), + port_map_bulker_(sai_dash_outbound_port_map_api, gSwitchId, gMaxBulkSize), + port_map_range_bulker_(sai_dash_outbound_port_map_api, gMaxBulkSize) +{ + SWSS_LOG_ENTER(); + dash_port_map_result_table_ = std::make_unique(app_state_db, APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME); + dash_port_map_range_result_table_ = std::make_unique(app_state_db, APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME); +} + +void DashPortMapOrch::doTask(ConsumerBase &consumer) +{ + SWSS_LOG_ENTER(); + + const auto &tn = consumer.getTableName(); + + SWSS_LOG_INFO("Table name: %s", tn.c_str()); + + if (tn == APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME) + { + doTaskPortMapTable(consumer); + } + else if (tn == APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME) + { + doTaskPortMapRangeTable(consumer); + } + else + { + SWSS_LOG_ERROR("Unknown table: %s", tn.c_str()); + } +} + +void DashPortMapOrch::doTaskPortMapTable(ConsumerBase &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + uint32_t result; + + std::map, + DashPortMapBulkContext> + toBulk; + while (it != consumer.m_toSync.end()) + { + swss::KeyOpFieldsValuesTuple tuple = it->second; + std::string port_map_id = kfvKey(tuple); + std::string op = kfvOp(tuple); + auto rc = toBulk.emplace(std::piecewise_construct, + std::forward_as_tuple(port_map_id, op), + std::forward_as_tuple()); + bool inserted = rc.second; + auto &ctxt = rc.first->second; + result = DASH_RESULT_SUCCESS; + SWSS_LOG_INFO("Processing port map entry: %s, operation: %s", port_map_id.c_str(), op.c_str()); + + if (!inserted) + { + ctxt.clear(); + } + + if (op == SET_COMMAND) + { + // the only info we need is the port map ID which is provided in the key + // no need to parse protobuf message here + + if (addPortMap(port_map_id, ctxt)) + { + it = consumer.m_toSync.erase(it); + // the only reason to remove from consumer prior to flush is if the port map already exists, + // so treat it like a success + writeResultToDB(dash_port_map_result_table_, port_map_id, result); + } + else + { + it++; + } + } + else if (op == DEL_COMMAND) + { + if (removePortMap(port_map_id, ctxt)) + { + it = consumer.m_toSync.erase(it); + removeResultFromDB(dash_port_map_result_table_, port_map_id); + } + else + { + it++; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } + + port_map_bulker_.flush(); + + auto it_prev = consumer.m_toSync.begin(); + while (it_prev != it) + { + swss::KeyOpFieldsValuesTuple tuple = it_prev->second; + std::string port_map_id = kfvKey(tuple); + std::string op = kfvOp(tuple); + result = DASH_RESULT_SUCCESS; + auto found = toBulk.find(std::make_pair(port_map_id, op)); + if (found == toBulk.end()) + { + it_prev++; + continue; + } + + auto &ctxt = found->second; + if (ctxt.port_map_oids.empty() && ctxt.port_map_statuses.empty()) + { + it_prev++; + continue; + } + + if (op == SET_COMMAND) + { + if (addPortMapPost(port_map_id, ctxt)) + { + it_prev = consumer.m_toSync.erase(it_prev); + } + else + { + result = DASH_RESULT_FAILURE; + it_prev++; + } + writeResultToDB(dash_port_map_result_table_, port_map_id, result); + } + else if (op == DEL_COMMAND) + { + if (removePortMapPost(port_map_id, ctxt)) + { + it_prev = consumer.m_toSync.erase(it_prev); + removeResultFromDB(dash_port_map_result_table_, port_map_id); + } + else + { + it_prev++; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation %s", op.c_str()); + it_prev = consumer.m_toSync.erase(it_prev); + } + } +} + +bool DashPortMapOrch::addPortMap(const std::string &port_map_id, DashPortMapBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + if (port_map_table_.find(port_map_id) != port_map_table_.end()) + { + SWSS_LOG_WARN("Port map %s already exists", port_map_id.c_str()); + return true; + } + + std::vector attrs; + sai_attribute_t attr; + attr.id = SAI_OUTBOUND_PORT_MAP_ATTR_COUNTER_ID; + attr.value.oid = SAI_NULL_OBJECT_ID; + attrs.push_back(attr); + auto &object_ids = ctxt.port_map_oids; + object_ids.emplace_back(); + port_map_bulker_.create_entry(&object_ids.back(), (uint32_t)attrs.size(), attrs.data()); + SWSS_LOG_INFO("Adding port map %s to bulker", port_map_id.c_str()); + return false; +} + +bool DashPortMapOrch::addPortMapPost(const std::string &port_map_id, DashPortMapBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto &object_ids = ctxt.port_map_oids; + if (object_ids.empty()) + { + return false; + } + + auto it_status = object_ids.begin(); + sai_object_id_t port_map_oid = *it_status++; + if (port_map_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to create port map %s", port_map_id.c_str()); + return false; + } + + port_map_table_[port_map_id] = port_map_oid; + SWSS_LOG_NOTICE("Created port map %s with OID 0x%" PRIx64, port_map_id.c_str(), port_map_oid); + return true; +} + +bool DashPortMapOrch::removePortMap(const std::string &port_map_id, DashPortMapBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto it = port_map_table_.find(port_map_id); + if (it == port_map_table_.end()) + { + SWSS_LOG_WARN("Port map %s not found for removal", port_map_id.c_str()); + return true; + } + + auto &object_statuses = ctxt.port_map_statuses; + object_statuses.emplace_back(); + sai_object_id_t port_map_oid = port_map_table_[port_map_id]; + port_map_bulker_.remove_entry(&object_statuses.back(), port_map_oid); + SWSS_LOG_NOTICE("Removing port map %s with OID 0x%" PRIx64, port_map_id.c_str(), port_map_oid); + + return false; +} + +bool DashPortMapOrch::removePortMapPost(const std::string &port_map_id, DashPortMapBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto &object_statuses = ctxt.port_map_statuses; + if (object_statuses.empty()) + { + return false; + } + + auto it_status = object_statuses.begin(); + sai_status_t status = *it_status++; + if (status != SAI_STATUS_SUCCESS) + { + if (status == SAI_STATUS_NOT_EXECUTED) + { + SWSS_LOG_INFO("Port map %s not removed, will retry later", port_map_id.c_str()); + return false; + } + SWSS_LOG_ERROR("Failed to remove port map %s, status: %s", port_map_id.c_str(), sai_serialize_status(status).c_str()); + task_process_status handle_status = handleSaiCreateStatus((sai_api_t)SAI_API_DASH_OUTBOUND_PORT_MAP, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + port_map_table_.erase(port_map_id); + SWSS_LOG_NOTICE("Removed port map %s", port_map_id.c_str()); + return true; +} + +void DashPortMapOrch::doTaskPortMapRangeTable(ConsumerBase &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + uint32_t result; + + std::map, + DashPortMapRangeBulkContext> + toBulk; + while (it != consumer.m_toSync.end()) + { + swss::KeyOpFieldsValuesTuple tuple = it->second; + std::string key = kfvKey(tuple); + std::string op = kfvOp(tuple); + auto rc = toBulk.emplace(std::piecewise_construct, + std::forward_as_tuple(key, op), + std::forward_as_tuple()); + bool inserted = rc.second; + auto &ctxt = rc.first->second; + result = DASH_RESULT_FAILURE; + SWSS_LOG_INFO("Processing port map range entry: %s, operation: %s", key.c_str(), op.c_str()); + + if (!inserted) + { + ctxt.clear(); + } + + if (!parsePortMapRange(key, ctxt)) + { + SWSS_LOG_ERROR("Failed to parse port map range key: %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + if (!parsePbMessage(kfvFieldsValues(tuple), ctxt.metadata)) + { + SWSS_LOG_ERROR("Failed to parse protobuf message for port map range %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (addPortMapRange(ctxt)) + { + it = consumer.m_toSync.erase(it); + // if we ever remove from consumer early, that means parsing was unsuccessful and a retry will not help, + // so treat it as a failure + writeResultToDB(dash_port_map_range_result_table_, key, result); + } + else + { + it++; + } + } + else if (op == DEL_COMMAND) + { + if (removePortMapRange(ctxt)) + { + it = consumer.m_toSync.erase(it); + removeResultFromDB(dash_port_map_range_result_table_, key); + } + else + { + it++; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } + + port_map_range_bulker_.flush(); + auto it_prev = consumer.m_toSync.begin(); + while (it_prev != it) + { + swss::KeyOpFieldsValuesTuple tuple = it_prev->second; + std::string key = kfvKey(tuple); + std::string op = kfvOp(tuple); + result = DASH_RESULT_SUCCESS; + auto found = toBulk.find(std::make_pair(key, op)); + if (found == toBulk.end()) + { + it_prev++; + continue; + } + auto &ctxt = found->second; + if (ctxt.port_map_range_statuses.empty()) + { + it_prev++; + continue; + } + + if (op == SET_COMMAND) + { + if (addPortMapRangePost(ctxt)) + { + it_prev = consumer.m_toSync.erase(it_prev); + } + else + { + result = DASH_RESULT_FAILURE; + it_prev++; + } + writeResultToDB(dash_port_map_range_result_table_, key, result); + } + else if (op == DEL_COMMAND) + { + if (removePortMapRangePost(ctxt)) + { + it_prev = consumer.m_toSync.erase(it_prev); + removeResultFromDB(dash_port_map_range_result_table_, key); + } + else + { + it_prev++; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation %s", op.c_str()); + it_prev = consumer.m_toSync.erase(it_prev); + } + } +} + +bool DashPortMapOrch::addPortMapRange(DashPortMapRangeBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto parent_it = port_map_table_.find(ctxt.parent_map_id); + if (parent_it == port_map_table_.end()) + { + SWSS_LOG_INFO("Parent port map %s does not exist for port map range", ctxt.parent_map_id.c_str()); + return false; + } + + sai_outbound_port_map_port_range_entry_t entry; + entry.switch_id = gSwitchId; + entry.outbound_port_map_id = parent_it->second; + sai_u32_range_t port_range; + port_range.min = ctxt.start_port; + port_range.max = ctxt.end_port; + entry.dst_port_range = port_range; + + std::vector attrs; + sai_attribute_t attr; + + auto action_it = gPortMapRangeActionMap.find(ctxt.metadata.action()); + if (action_it == gPortMapRangeActionMap.end()) + { + SWSS_LOG_ERROR("Unknown port map range action: %s", dash::outbound_port_map_range::PortMapRangeAction_Name(ctxt.metadata.action()).c_str()); + return true; + } + + attr.id = SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_ACTION; + attr.value.s32 = action_it->second; + attrs.push_back(attr); + + attr.id = SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_BACKEND_IP; + if (!to_sai(ctxt.metadata.backend_ip(), attr.value.ipaddr)) + { + SWSS_LOG_ERROR("Failed to convert backend IP %s to SAI format", ctxt.metadata.backend_ip().DebugString().c_str()); + return true; + } + attrs.push_back(attr); + + attr.id = SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_MATCH_PORT_BASE; + attr.value.u32 = ctxt.start_port; + attrs.push_back(attr); + + attr.id = SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_BACKEND_PORT_BASE; + attr.value.u32 = ctxt.metadata.backend_port_base(); + attrs.push_back(attr); + + auto &object_statuses = ctxt.port_map_range_statuses; + object_statuses.emplace_back(); + port_map_range_bulker_.create_entry(&object_statuses.back(), &entry, (uint32_t)attrs.size(), attrs.data()); + SWSS_LOG_INFO("Adding port map range for %s: start=%d, end=%d", ctxt.parent_map_id.c_str(), ctxt.start_port, ctxt.end_port); + return false; +} + +bool DashPortMapOrch::addPortMapRangePost(DashPortMapRangeBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto &object_statuses = ctxt.port_map_range_statuses; + if (object_statuses.empty()) + { + return false; + } + + auto it_status = object_statuses.begin(); + sai_status_t status = *it_status++; + if (status != SAI_STATUS_SUCCESS) + { + if (status == SAI_STATUS_NOT_EXECUTED) + { + SWSS_LOG_INFO("Port map range for %s not created, will retry later", ctxt.parent_map_id.c_str()); + return false; + } + SWSS_LOG_ERROR("Failed to create port map range for %s, status: %s", ctxt.parent_map_id.c_str(), sai_serialize_status(status).c_str()); + task_process_status handle_status = handleSaiCreateStatus((sai_api_t)SAI_API_DASH_OUTBOUND_PORT_MAP, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + SWSS_LOG_INFO("Created port map range for %s: start=%d, end=%d", ctxt.parent_map_id.c_str(), ctxt.start_port, ctxt.end_port); + return true; +} + +bool DashPortMapOrch::removePortMapRange(DashPortMapRangeBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto parent_it = port_map_table_.find(ctxt.parent_map_id); + if (parent_it == port_map_table_.end()) + { + // this should never happen - it's not possible to create a port map range w/o first creating the parent port map, + // and it's not possible to delete a port map while it still has child port map ranges + SWSS_LOG_ERROR("Parent port map %s not found for port map range removal", ctxt.parent_map_id.c_str()); + return true; + } + + sai_outbound_port_map_port_range_entry_t entry; + entry.switch_id = gSwitchId; + entry.outbound_port_map_id = parent_it->second; + sai_u32_range_t port_range; + port_range.min = ctxt.start_port; + port_range.max = ctxt.end_port; + entry.dst_port_range = port_range; + + auto &object_statuses = ctxt.port_map_range_statuses; + object_statuses.emplace_back(); + port_map_range_bulker_.remove_entry(&object_statuses.back(), &entry); + SWSS_LOG_NOTICE("Removing port map range for %s: start=%d, end=%d", ctxt.parent_map_id.c_str(), ctxt.start_port, ctxt.end_port); + return false; +} + +bool DashPortMapOrch::removePortMapRangePost(DashPortMapRangeBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + auto &object_statuses = ctxt.port_map_range_statuses; + if (object_statuses.empty()) + { + return false; + } + + auto it_status = object_statuses.begin(); + sai_status_t status = *it_status++; + if (status != SAI_STATUS_SUCCESS) + { + if (status == SAI_STATUS_NOT_EXECUTED) + { + SWSS_LOG_INFO("Port map range for %s not removed, will retry later", ctxt.parent_map_id.c_str()); + return false; + } + SWSS_LOG_ERROR("Failed to remove port map range for %s, status: %s", ctxt.parent_map_id.c_str(), sai_serialize_status(status).c_str()); + task_process_status handle_status = handleSaiCreateStatus((sai_api_t)SAI_API_DASH_OUTBOUND_PORT_MAP, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + SWSS_LOG_NOTICE("Removed port map range for %s: start=%d, end=%d", ctxt.parent_map_id.c_str(), ctxt.start_port, ctxt.end_port); + return true; +} + +bool DashPortMapOrch::parsePortMapRange(const std::string &key, DashPortMapRangeBulkContext &ctxt) +{ + SWSS_LOG_ENTER(); + + // Example key format: PORT_MAP_1:1000-2000 + size_t pos = key.find(':'); + if (pos == std::string::npos) + { + SWSS_LOG_ERROR("Invalid port map range key format: %s", key.c_str()); + return false; + } + + ctxt.parent_map_id = key.substr(0, pos); + std::string range = key.substr(pos + 1); + + size_t dash_pos = range.find('-'); + if (dash_pos == std::string::npos) + { + SWSS_LOG_ERROR("Invalid port range format: %s", range.c_str()); + return false; + } + + try + { + ctxt.start_port = std::stoi(range.substr(0, dash_pos)); + ctxt.end_port = std::stoi(range.substr(dash_pos + 1)); + } + catch (const std::invalid_argument &e) + { + SWSS_LOG_ERROR("Invalid port range values in key %s: %s", key.c_str(), e.what()); + return false; + } + + SWSS_LOG_INFO("Parsed port range for %s: start=%d, end=%d", ctxt.parent_map_id.c_str(), ctxt.start_port, ctxt.end_port); + return true; +} \ No newline at end of file diff --git a/orchagent/dash/dashportmaporch.h b/orchagent/dash/dashportmaporch.h new file mode 100644 index 00000000..a35015f0 --- /dev/null +++ b/orchagent/dash/dashportmaporch.h @@ -0,0 +1,68 @@ +#pragma once + +#include "dbconnector.h" +#include "zmqorch.h" +#include "dash_api/outbound_port_map_range.pb.h" +#include "bulker.h" + +struct DashPortMapBulkContext +{ + std::deque port_map_oids; + std::deque port_map_statuses; + + DashPortMapBulkContext() {} + DashPortMapBulkContext(const DashPortMapBulkContext &) = delete; + DashPortMapBulkContext(DashPortMapBulkContext &) = delete; + + void clear() + { + port_map_oids.clear(); + port_map_statuses.clear(); + } +}; + +struct DashPortMapRangeBulkContext +{ + std::string parent_map_id; + int start_port; + int end_port; + dash::outbound_port_map_range::OutboundPortMapRange metadata; + std::deque port_map_range_statuses; + + DashPortMapRangeBulkContext() {} + DashPortMapRangeBulkContext(const DashPortMapRangeBulkContext &) = delete; + DashPortMapRangeBulkContext(DashPortMapRangeBulkContext &) = delete; + + void clear() + { + port_map_range_statuses.clear(); + } +}; + +class DashPortMapOrch : public ZmqOrch +{ +public: + DashPortMapOrch(swss::DBConnector *db, std::vector &tables, swss::DBConnector *app_state_db, swss::ZmqServer *zmqServer); + +private: + void doTask(ConsumerBase &consumer); + void doTaskPortMapTable(ConsumerBase &consumer); + bool addPortMap(const std::string &port_map_id, DashPortMapBulkContext &ctxt); + bool addPortMapPost(const std::string &port_map_id, DashPortMapBulkContext &ctxt); + bool removePortMap(const std::string &port_map_id, DashPortMapBulkContext &ctxt); + bool removePortMapPost(const std::string &port_map_id, DashPortMapBulkContext &ctxt); + void doTaskPortMapRangeTable(ConsumerBase &consumer); + bool addPortMapRange(DashPortMapRangeBulkContext &ctxt); + bool addPortMapRangePost(DashPortMapRangeBulkContext &ctxt); + bool removePortMapRange(DashPortMapRangeBulkContext &ctxt); + bool removePortMapRangePost(DashPortMapRangeBulkContext &ctxt); + + bool parsePortMapRange(const std::string &key, DashPortMapRangeBulkContext &ctxt); + + ObjectBulker port_map_bulker_; + EntityBulker port_map_range_bulker_; + + std::unordered_map port_map_table_; + std::unique_ptr dash_port_map_result_table_; + std::unique_ptr dash_port_map_range_result_table_; +}; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index fe5d817c..141f5475 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -1299,6 +1299,13 @@ bool DpuOrchDaemon::init() DashMeterOrch *dash_meter_orch = new DashMeterOrch(m_applDb, dash_meter_tables, dash_orch, m_dpu_appstateDb, dash_zmq_server); gDirectory.set(dash_meter_orch); + vector dash_port_map_tables = { + APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, + APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME + }; + DashPortMapOrch *dash_port_map_orch = new DashPortMapOrch(m_applDb, dash_port_map_tables, m_dpu_appstateDb, dash_zmq_server); + gDirectory.set(dash_port_map_orch); + addOrchList(dash_acl_orch); addOrchList(dash_vnet_orch); addOrchList(dash_route_orch); @@ -1306,6 +1313,7 @@ bool DpuOrchDaemon::init() addOrchList(dash_tunnel_orch); addOrchList(dash_meter_orch); addOrchList(dash_ha_orch); + addOrchList(dash_port_map_orch); return true; } diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index e318fa2c..b93f5642 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -57,6 +57,7 @@ #include "dash/dashvnetorch.h" #include "dash/dashhaorch.h" #include "dash/dashmeterorch.h" +#include "dash/dashportmaporch.h" #include using namespace swss; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 4500b43e..4b0749fe 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -91,6 +91,7 @@ sai_twamp_api_t* sai_twamp_api; sai_tam_api_t* sai_tam_api; sai_stp_api_t* sai_stp_api; sai_dash_meter_api_t* sai_dash_meter_api; +sai_dash_outbound_port_map_api_t* sai_dash_outbound_port_map_api; sai_dash_trusted_vni_api_t* sai_dash_trusted_vni_api; extern sai_object_id_t gSwitchId; @@ -245,6 +246,7 @@ void initSaiApi() sai_api_query((sai_api_t)SAI_API_DASH_DIRECTION_LOOKUP, (void**)&sai_dash_direction_lookup_api); sai_api_query((sai_api_t)SAI_API_DASH_TUNNEL, (void**)&sai_dash_tunnel_api); sai_api_query((sai_api_t)SAI_API_DASH_HA, (void**)&sai_dash_ha_api); + sai_api_query((sai_api_t)SAI_API_DASH_OUTBOUND_PORT_MAP, (void**)&sai_dash_outbound_port_map_api); sai_api_query(SAI_API_TWAMP, (void **)&sai_twamp_api); sai_api_query(SAI_API_TAM, (void **)&sai_tam_api); sai_api_query(SAI_API_STP, (void **)&sai_stp_api); diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index c7bd5a61..fa5c572b 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -67,6 +67,7 @@ tests_SOURCES = aclorch_ut.cpp \ dashvnetorch_ut.cpp \ dashhaorch_ut.cpp \ dashrouteorch_ut.cpp \ + dashportmaporch_ut.cpp \ twamporch_ut.cpp \ stporch_ut.cpp \ flexcounter_ut.cpp \ @@ -153,6 +154,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/dash/dashvnetorch.cpp \ $(top_srcdir)/orchagent/dash/dashhaorch.cpp \ $(top_srcdir)/orchagent/dash/dashmeterorch.cpp \ + $(top_srcdir)/orchagent/dash/dashportmaporch.cpp \ $(top_srcdir)/cfgmgr/buffermgrdyn.cpp \ $(top_srcdir)/warmrestart/warmRestartAssist.cpp \ $(top_srcdir)/orchagent/dash/pbutils.cpp \ diff --git a/tests/mock_tests/dashportmaporch_ut.cpp b/tests/mock_tests/dashportmaporch_ut.cpp new file mode 100644 index 00000000..3e09afb0 --- /dev/null +++ b/tests/mock_tests/dashportmaporch_ut.cpp @@ -0,0 +1,210 @@ +#define private public +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_sai_api.h" +#include "mock_dash_orch_test.h" + +#include "dash_api/outbound_port_map.pb.h" + +EXTERN_MOCK_FNS + +namespace dashportmaporch_test +{ + DEFINE_SAI_API_COMBINED_MOCK(dash_outbound_port_map, outbound_port_map, outbound_port_map_port_range) + using namespace mock_orch_test; + using ::testing::DoAll; + using ::testing::Return; + using ::testing::SetArgPointee; + using ::testing::SetArrayArgument; + using ::testing::SaveArg; + using ::testing::SaveArgPointee; + using ::testing::Invoke; + using ::testing::InSequence; + + class DashPortMapOrchTest : public MockDashOrchTest + { + void ApplySaiMock() + { + INIT_SAI_API_MOCK(dash_outbound_port_map); + MockSaiApis(); + } + + void PreTearDown() override + { + RestoreSaiApis(); + DEINIT_SAI_API_MOCK(dash_outbound_port_map); + } + + protected: + std::string port_map1 = "PORT_MAP_1"; + int port_map1_start_port = 1000; + int port_map1_end_port = 2000; + int port_map1_backend_port_base = 5000; + swss::IpAddress port_map1_backend_ip = swss::IpAddress("1.2.3.4"); + dash::outbound_port_map_range::OutboundPortMapRange BuildOutboundPortMapRange() + { + dash::outbound_port_map_range::OutboundPortMapRange port_map_range; + port_map_range.mutable_backend_ip()->set_ipv4(port_map1_backend_ip.getV4Addr()); + port_map_range.set_action(dash::outbound_port_map_range::PortMapRangeAction::ACTION_MAP_PRIVATE_LINK_SERVICE); + port_map_range.set_backend_port_base(port_map1_backend_port_base); + return port_map_range; + } + }; + + TEST_F(DashPortMapOrchTest, AddRemovePortMapEntry) + { + dash::outbound_port_map::OutboundPortMap port_map; + + std::vector exp_status = { SAI_STATUS_SUCCESS }; + sai_object_id_t fake_oid = 0x1234; + sai_object_id_t actual_removed_oid; + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_maps).WillOnce(DoAll(SetArgPointee<5>(fake_oid), SetArrayArgument<6>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, remove_outbound_port_maps).WillOnce(DoAll(SaveArgPointee<1>(&actual_removed_oid), SetArrayArgument<3>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, port_map); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, port_map, false); + + EXPECT_EQ(actual_removed_oid, fake_oid); + } + + TEST_F(DashPortMapOrchTest, AddDuplicatePortMap) + { + dash::outbound_port_map::OutboundPortMap port_map; + + std::vector exp_status = { SAI_STATUS_SUCCESS }; + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_maps).Times(1); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, port_map); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, port_map); + } + + TEST_F(DashPortMapOrchTest, RemoveNonexistPortMap) + { + dash::outbound_port_map::OutboundPortMap port_map; + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, remove_outbound_port_maps).Times(0); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, port_map, false); + } + + TEST_F(DashPortMapOrchTest, AddRemovePortMapRange) + { + uint32_t num_attrs; + const sai_attribute_t *attr_start; + std::vector actual_attrs; + sai_outbound_port_map_port_range_entry_t actual_entry; + sai_outbound_port_map_port_range_entry_t removed_entry; + sai_object_id_t fake_oid = 0x1234; + std::vector success_status = { SAI_STATUS_SUCCESS }; + + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_maps) + .WillOnce(DoAll( + SetArgPointee<5>(fake_oid), + SetArrayArgument<6>(success_status.begin(), success_status.end()), + Return(SAI_STATUS_SUCCESS))); + + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_map_port_range_entries) + .WillOnce(DoAll( + SaveArgPointee<1>(&actual_entry), + SaveArgPointee<2>(&num_attrs), + SaveArgPointee<3>(&attr_start), + Return(SAI_STATUS_SUCCESS))); + + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, remove_outbound_port_map_port_range_entries) + .WillOnce(DoAll( + SaveArgPointee<1>(&removed_entry), + SetArrayArgument<3>(success_status.begin(), success_status.end()), + Return(SAI_STATUS_SUCCESS))); + + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, dash::outbound_port_map::OutboundPortMap()); + + std::stringstream key_stream; + key_stream << port_map1 << ":" << port_map1_start_port << "-" << port_map1_end_port; + std::string key = key_stream.str(); + + dash::outbound_port_map_range::OutboundPortMapRange port_map_range = BuildOutboundPortMapRange(); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, key, port_map_range); + + EXPECT_EQ(actual_entry.outbound_port_map_id, fake_oid); + EXPECT_EQ(actual_entry.dst_port_range.min, 1000); + EXPECT_EQ(actual_entry.dst_port_range.max, 2000); + + actual_attrs.assign(attr_start, attr_start + num_attrs); + + for (auto attr : actual_attrs) + { + if (attr.id == SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_ACTION) + { + EXPECT_EQ(attr.value.s32, SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ACTION_MAP_TO_PRIVATE_LINK_SERVICE); + } + else if (attr.id == SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_BACKEND_IP) + { + EXPECT_EQ(attr.value.ipaddr.addr.ip4, port_map1_backend_ip.getV4Addr()); + } + else if (attr.id == SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_MATCH_PORT_BASE) + { + EXPECT_EQ(attr.value.u32, port_map1_start_port); + } + else if (attr.id == SAI_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY_ATTR_BACKEND_PORT_BASE) + { + EXPECT_EQ(attr.value.u32, port_map1_backend_port_base); + } + } + + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, key, port_map_range, false); + EXPECT_EQ(removed_entry.outbound_port_map_id, fake_oid); + EXPECT_EQ(removed_entry.dst_port_range.min, 1000); + EXPECT_EQ(removed_entry.dst_port_range.max, 2000); + } + + TEST_F(DashPortMapOrchTest, AddDuplicatePortMapRange) + { + auto port_map_range = BuildOutboundPortMapRange(); + std::stringstream key_stream; + key_stream << port_map1 << ":" << port_map1_start_port << "-" << port_map1_end_port; + std::string key = key_stream.str(); + + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_map_port_range_entries).Times(2); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, dash::outbound_port_map::OutboundPortMap()); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, key, port_map_range); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, key, port_map_range, true, false); + } + + TEST_F(DashPortMapOrchTest, RemoveNonexistPortMapRange) + { + std::stringstream key_stream; + key_stream << port_map1 << ":" << port_map1_start_port << "-" << port_map1_end_port; + std::string port_map_range_key = key_stream.str(); + + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, remove_outbound_port_map_port_range_entries); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, dash::outbound_port_map::OutboundPortMap()); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, port_map_range_key, dash::outbound_port_map_range::OutboundPortMapRange(), false, false); + } + + TEST_F(DashPortMapOrchTest, AddPortRangeWithoutPortMap) + { + auto port_map_range = BuildOutboundPortMapRange(); + std::stringstream key_stream; + key_stream << port_map1 << ":" << port_map1_start_port << "-" << port_map1_end_port; + std::string key = key_stream.str(); + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_map_port_range_entries).Times(0); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, key, port_map_range, true, false); + } + + TEST_F(DashPortMapOrchTest, RemoveInUsePortMap) + { + auto port_map_range = BuildOutboundPortMapRange(); + std::stringstream key_stream; + key_stream << port_map1 << ":" << port_map1_start_port << "-" << port_map1_end_port; + std::string port_map_range_key = key_stream.str(); + + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_maps); + EXPECT_CALL(*mock_sai_dash_outbound_port_map_api, create_outbound_port_map_port_range_entries); + + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, dash::outbound_port_map::OutboundPortMap()); + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, port_map_range_key, port_map_range); + + SetDashTable(APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, port_map1, dash::outbound_port_map::OutboundPortMap(), false, false); + } +} diff --git a/tests/mock_tests/mock_dash_orch_test.cpp b/tests/mock_tests/mock_dash_orch_test.cpp index 2f87985b..2f5e5ad4 100644 --- a/tests/mock_tests/mock_dash_orch_test.cpp +++ b/tests/mock_tests/mock_dash_orch_test.cpp @@ -2,7 +2,7 @@ namespace mock_orch_test { - + void MockDashOrchTest::SetDashTable(std::string table_name, std::string key, const google::protobuf::Message &message, bool set, bool expect_empty) { auto it = dash_table_orch_map.find(table_name); @@ -10,25 +10,29 @@ namespace mock_orch_test { FAIL() << "Table " << table_name << " not found in dash_table_orch_map."; } - Orch* target_orch = *(it->second); + Orch *target_orch = *(it->second); auto consumer = make_unique( new swss::ConsumerStateTable(m_app_db.get(), table_name), - target_orch, table_name - ); + target_orch, table_name); auto op = set ? SET_COMMAND : DEL_COMMAND; consumer->addToSync( - swss::KeyOpFieldsValuesTuple(key, op, {{"pb", message.SerializeAsString()}}) - ); + swss::KeyOpFieldsValuesTuple(key, op, { { "pb", message.SerializeAsString() } })); target_orch->doTask(*consumer.get()); + auto it2 = consumer->m_toSync.begin(); if (expect_empty) { - auto it = consumer->m_toSync.begin(); - EXPECT_EQ(it, consumer->m_toSync.end()) + EXPECT_EQ(it2, consumer->m_toSync.end()) << "Expected consumer to be empty after operation on table " << table_name << " with key " << key; } + else + { + EXPECT_NE(it2, consumer->m_toSync.end()) + << "Expected consumer to not be empty after operation on table " << table_name + << " with key " << key; + } } dash::appliance::Appliance MockDashOrchTest::BuildApplianceEntry() diff --git a/tests/mock_tests/mock_dash_orch_test.h b/tests/mock_tests/mock_dash_orch_test.h index c554261d..bc2af751 100644 --- a/tests/mock_tests/mock_dash_orch_test.h +++ b/tests/mock_tests/mock_dash_orch_test.h @@ -16,6 +16,8 @@ namespace mock_orch_test {APP_DASH_ROUTE_TABLE_NAME, (Orch**) &m_DashRouteOrch}, {APP_DASH_TUNNEL_TABLE_NAME, (Orch**) &m_DashTunnelOrch}, {APP_DASH_ENI_TABLE_NAME, (Orch**) &m_DashOrch}, + { APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, (Orch **)&m_dashPortMapOrch }, + { APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME, (Orch **)&m_dashPortMapOrch } }; void SetDashTable(std::string table_name, std::string key, const google::protobuf::Message &message, bool set = true, bool expect_empty = true); dash::appliance::Appliance BuildApplianceEntry(); diff --git a/tests/mock_tests/mock_orch_test.cpp b/tests/mock_tests/mock_orch_test.cpp index 04fe6ea0..811c372e 100644 --- a/tests/mock_tests/mock_orch_test.cpp +++ b/tests/mock_tests/mock_orch_test.cpp @@ -326,6 +326,14 @@ void MockOrchTest::SetUp() gDirectory.set(m_DashTunnelOrch); ut_orch_list.push_back((Orch **)&m_DashTunnelOrch); + vector dash_port_map_tables = { + APP_DASH_OUTBOUND_PORT_MAP_TABLE_NAME, + APP_DASH_OUTBOUND_PORT_MAP_RANGE_TABLE_NAME + }; + m_dashPortMapOrch = new DashPortMapOrch(m_app_db.get(), dash_port_map_tables, m_dpu_app_state_db.get(), nullptr); + gDirectory.set(m_dashPortMapOrch); + ut_orch_list.push_back((Orch **)&m_dashPortMapOrch); + ApplyInitialConfigs(); PostSetUp(); } diff --git a/tests/mock_tests/mock_orch_test.h b/tests/mock_tests/mock_orch_test.h index 523c450b..f9f0d8fa 100644 --- a/tests/mock_tests/mock_orch_test.h +++ b/tests/mock_tests/mock_orch_test.h @@ -59,6 +59,7 @@ namespace mock_orch_test DashHaOrch *m_dashHaOrch; DashRouteOrch *m_DashRouteOrch; DashTunnelOrch *m_DashTunnelOrch; + DashPortMapOrch *m_dashPortMapOrch; void PrepareSai(); void SetUp(); diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index d1229930..9ec3712f 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -38,6 +38,7 @@ #include "dashvnetorch.h" #include "dashhaorch.h" #include "dashtunnelorch.h" +#include "dashportmaporch.h" extern int gBatchSize; @@ -113,4 +114,5 @@ extern sai_dash_appliance_api_t* sai_dash_appliance_api; extern sai_dash_outbound_routing_api_t* sai_dash_outbound_routing_api; extern sai_dash_inbound_routing_api_t* sai_dash_inbound_routing_api; extern sai_dash_tunnel_api_t* sai_dash_tunnel_api; -extern sai_dash_trusted_vni_api_t* sai_dash_trusted_vni_api; \ No newline at end of file +extern sai_dash_outbound_port_map_api_t* sai_dash_outbound_port_map_api; +extern sai_dash_trusted_vni_api_t* sai_dash_trusted_vni_api; diff --git a/tests/mock_tests/mock_sai_api.h b/tests/mock_tests/mock_sai_api.h index 5cb76612..629c9969 100644 --- a/tests/mock_tests/mock_sai_api.h +++ b/tests/mock_tests/mock_sai_api.h @@ -267,100 +267,160 @@ This is required since some sai_api do not support this function call yet. #define FOR_EACH_1(action, api_name, x) action(api_name, x) #define FOR_EACH_2(action, api_name, x, ...) action(api_name, x) FOR_EACH_1(action, api_name, __VA_ARGS__) -#define GET_FOR_EACH_MACRO(_1,_2,NAME,...) NAME +#define GET_FOR_EACH_MACRO(_1, _2, NAME, ...) NAME #define FOR_EACH(action, api_name, ...) \ GET_FOR_EACH_MACRO(__VA_ARGS__, FOR_EACH_2, FOR_EACH_1)(action, api_name, __VA_ARGS__) -#define DEFINE_ON_CALL_DEFAULTS(sai_api_name, sai_object_type) \ - ON_CALL(*this, create_##sai_object_type).WillByDefault([this](GENERIC_CREATE_PARAMS(sai_object_type)) { \ - return old_sai_##sai_api_name##_api->create_##sai_object_type(GENERIC_CREATE_ARGS(sai_object_type)); \ - }); \ - ON_CALL(*this, remove_##sai_object_type).WillByDefault([this](GENERIC_REMOVE_PARAMS(sai_object_type)) { \ - return old_sai_##sai_api_name##_api->remove_##sai_object_type(GENERIC_REMOVE_ARGS(sai_object_type)); \ - }); \ +#define DEFINE_ON_CALL_DEFAULTS(sai_api_name, sai_object_type) \ + ON_CALL(*this, create_##sai_object_type).WillByDefault([this](GENERIC_CREATE_PARAMS(sai_object_type)) { \ + return old_sai_##sai_api_name##_api->create_##sai_object_type(GENERIC_CREATE_ARGS(sai_object_type)); \ + }); \ + ON_CALL(*this, remove_##sai_object_type).WillByDefault([this](GENERIC_REMOVE_PARAMS(sai_object_type)) { \ + return old_sai_##sai_api_name##_api->remove_##sai_object_type(GENERIC_REMOVE_ARGS(sai_object_type)); \ + }); \ ON_CALL(*this, set_##sai_object_type##_attribute).WillByDefault([this](sai_object_id_t oid, const sai_attribute_t *attr) { \ - return old_sai_##sai_api_name##_api->set_##sai_object_type##_attribute(oid, attr); \ + return old_sai_##sai_api_name##_api->set_##sai_object_type##_attribute(oid, attr); \ + }); \ + ON_CALL(*this, create_##sai_object_type##s).WillByDefault([this](GENERIC_BULK_CREATE_PARAMS(sai_object_type)) { \ + return old_sai_##sai_api_name##_api->create_##sai_object_type##s(GENERIC_BULK_CREATE_ARGS(sai_object_type)); \ + }); \ + ON_CALL(*this, remove_##sai_object_type##s).WillByDefault([this](GENERIC_BULK_REMOVE_PARAMS(sai_object_type)) { \ + return old_sai_##sai_api_name##_api->remove_##sai_object_type##s(GENERIC_BULK_REMOVE_ARGS(sai_object_type)); \ }); -#define DEFINE_ENTRY_ON_CALL_DEFAULTS(sai_api_name, sai_entry_type) \ - ON_CALL(*this, create_##sai_entry_type##_entry).WillByDefault([this](CREATE_PARAMS(sai_entry_type)) { \ - return old_sai_##sai_api_name##_api->create_##sai_entry_type##_entry(CREATE_ARGS(sai_entry_type)); \ - }); \ - ON_CALL(*this, remove_##sai_entry_type##_entry).WillByDefault([this](REMOVE_PARAMS(sai_entry_type)) { \ - return old_sai_##sai_api_name##_api->remove_##sai_entry_type##_entry(REMOVE_ARGS(sai_entry_type)); \ - }); \ - ON_CALL(*this, create_##sai_entry_type##_entries).WillByDefault([this](CREATE_BULK_PARAMS(sai_entry_type)) { \ - return old_sai_##sai_api_name##_api->create_##sai_entry_type##_entries(CREATE_BULK_ARGS(sai_entry_type)); \ - }); \ - ON_CALL(*this, remove_##sai_entry_type##_entries).WillByDefault([this](REMOVE_BULK_PARAMS(sai_entry_type)) { \ - return old_sai_##sai_api_name##_api->remove_##sai_entry_type##_entries(REMOVE_BULK_ARGS(sai_entry_type)); \ +#define DEFINE_ENTRY_ON_CALL_DEFAULTS(sai_api_type, sai_entry_type) \ + ON_CALL(*this, create_##sai_entry_type##_entry).WillByDefault([this](CREATE_PARAMS(sai_entry_type)) { \ + return old_sai_##sai_api_type##_api->create_##sai_entry_type##_entry(CREATE_ARGS(sai_entry_type)); \ + }); \ + ON_CALL(*this, remove_##sai_entry_type##_entry).WillByDefault([this](REMOVE_PARAMS(sai_entry_type)) { \ + return old_sai_##sai_api_type##_api->remove_##sai_entry_type##_entry(REMOVE_ARGS(sai_entry_type)); \ + }); \ + ON_CALL(*this, create_##sai_entry_type##_entries).WillByDefault([this](CREATE_BULK_PARAMS(sai_entry_type)) { \ + return old_sai_##sai_api_type##_api->create_##sai_entry_type##_entries(CREATE_BULK_ARGS(sai_entry_type)); \ + }); \ + ON_CALL(*this, remove_##sai_entry_type##_entries).WillByDefault([this](REMOVE_BULK_PARAMS(sai_entry_type)) { \ + return old_sai_##sai_api_type##_api->remove_##sai_entry_type##_entries(REMOVE_BULK_ARGS(sai_entry_type)); \ }); -#define DEFINE_MOCK_METHODS(sai_api_name, sai_object_type) \ - MOCK_METHOD4(create_##sai_object_type, sai_status_t(GENERIC_CREATE_PARAMS(sai_object_type))); \ - MOCK_METHOD1(remove_##sai_object_type, sai_status_t(GENERIC_REMOVE_PARAMS(sai_object_type))); \ - MOCK_METHOD2(set_##sai_object_type##_attribute, sai_status_t(sai_object_id_t, const sai_attribute_t *)); -#define DEFINE_MOCK_ENTRY_METHODS(sai_api_name, sai_entry_type) \ - MOCK_METHOD3(create_##sai_entry_type##_entry, sai_status_t(CREATE_PARAMS(sai_entry_type))); \ - MOCK_METHOD1(remove_##sai_entry_type##_entry, sai_status_t(REMOVE_PARAMS(sai_entry_type))); \ +#define DEFINE_MOCK_METHODS(sai_api_name, sai_object_type) \ + MOCK_METHOD4(create_##sai_object_type, sai_status_t(GENERIC_CREATE_PARAMS(sai_object_type))); \ + MOCK_METHOD1(remove_##sai_object_type, sai_status_t(GENERIC_REMOVE_PARAMS(sai_object_type))); \ + MOCK_METHOD2(set_##sai_object_type##_attribute, sai_status_t(sai_object_id_t, const sai_attribute_t *)); \ + MOCK_METHOD7(create_##sai_object_type##s, sai_status_t(GENERIC_BULK_CREATE_PARAMS(sai_object_type))); \ + MOCK_METHOD4(remove_##sai_object_type##s, sai_status_t(GENERIC_BULK_REMOVE_PARAMS(sai_object_type))); +#define DEFINE_ENTRY_MOCK_METHODS(sai_api_name, sai_entry_type) \ + MOCK_METHOD3(create_##sai_entry_type##_entry, sai_status_t(CREATE_PARAMS(sai_entry_type))); \ + MOCK_METHOD1(remove_##sai_entry_type##_entry, sai_status_t(REMOVE_PARAMS(sai_entry_type))); \ MOCK_METHOD6(create_##sai_entry_type##_entries, sai_status_t(CREATE_BULK_PARAMS(sai_entry_type))); \ MOCK_METHOD4(remove_##sai_entry_type##_entries, sai_status_t(REMOVE_BULK_PARAMS(sai_entry_type))); -#define DEFINE_WRAPPER_FUNCTIONS(sai_api_name, sai_object_type) \ - inline sai_status_t mock_create_##sai_object_type(GENERIC_CREATE_PARAMS(sai_object_type)) { \ - return mock_sai_##sai_api_name##_api->create_##sai_object_type(GENERIC_CREATE_ARGS(sai_object_type)); \ - } \ - inline sai_status_t mock_remove_##sai_object_type(GENERIC_REMOVE_PARAMS(sai_object_type)) { \ - return mock_sai_##sai_api_name##_api->remove_##sai_object_type(GENERIC_REMOVE_ARGS(sai_object_type)); \ - } \ - inline sai_status_t mock_set_##sai_object_type##_attribute(sai_object_id_t oid, const sai_attribute_t *attr) { \ - return mock_sai_##sai_api_name##_api->set_##sai_object_type##_attribute(oid, attr); \ +#define DEFINE_WRAPPER_FUNCTIONS(sai_api_name, sai_object_type) \ + inline sai_status_t mock_create_##sai_object_type(GENERIC_CREATE_PARAMS(sai_object_type)) \ + { \ + return mock_sai_##sai_api_name##_api->create_##sai_object_type(GENERIC_CREATE_ARGS(sai_object_type)); \ + } \ + inline sai_status_t mock_remove_##sai_object_type(GENERIC_REMOVE_PARAMS(sai_object_type)) \ + { \ + return mock_sai_##sai_api_name##_api->remove_##sai_object_type(GENERIC_REMOVE_ARGS(sai_object_type)); \ + } \ + inline sai_status_t mock_set_##sai_object_type##_attribute(sai_object_id_t oid, const sai_attribute_t *attr) \ + { \ + return mock_sai_##sai_api_name##_api->set_##sai_object_type##_attribute(oid, attr); \ + } \ + inline sai_status_t mock_create_##sai_object_type##s(GENERIC_BULK_CREATE_PARAMS(sai_object_type)) \ + { \ + return mock_sai_##sai_api_name##_api->create_##sai_object_type##s(GENERIC_BULK_CREATE_ARGS(sai_object_type)); \ + } \ + inline sai_status_t mock_remove_##sai_object_type##s(GENERIC_BULK_REMOVE_PARAMS(sai_object_type)) \ + { \ + return mock_sai_##sai_api_name##_api->remove_##sai_object_type##s(GENERIC_BULK_REMOVE_ARGS(sai_object_type)); \ } -#define DEFINE_ENTRY_WRAPPER_FUNCTIONS(sai_api_name, sai_entry_type) \ - inline sai_status_t mock_create_##sai_entry_type##_entry(CREATE_PARAMS(sai_entry_type)) { \ - return mock_sai_##sai_api_name##_api->create_##sai_entry_type##_entry(CREATE_ARGS(sai_entry_type)); \ - } \ - inline sai_status_t mock_remove_##sai_entry_type##_entry(REMOVE_PARAMS(sai_entry_type)) { \ - return mock_sai_##sai_api_name##_api->remove_##sai_entry_type##_entry(REMOVE_ARGS(sai_entry_type)); \ - } \ - inline sai_status_t mock_create_##sai_entry_type##_entries(CREATE_BULK_PARAMS(sai_entry_type)) { \ +#define DEFINE_WRAPPER_ENTRY_FUNCTIONS(sai_api_name, sai_entry_type) \ + inline sai_status_t mock_create_##sai_entry_type##_entry(CREATE_PARAMS(sai_entry_type)) \ + { \ + return mock_sai_##sai_api_name##_api->create_##sai_entry_type##_entry(CREATE_ARGS(sai_entry_type)); \ + } \ + inline sai_status_t mock_remove_##sai_entry_type##_entry(REMOVE_PARAMS(sai_entry_type)) \ + { \ + return mock_sai_##sai_api_name##_api->remove_##sai_entry_type##_entry(REMOVE_ARGS(sai_entry_type)); \ + } \ + inline sai_status_t mock_create_##sai_entry_type##_entries(CREATE_BULK_PARAMS(sai_entry_type)) \ + { \ return mock_sai_##sai_api_name##_api->create_##sai_entry_type##_entries(CREATE_BULK_ARGS(sai_entry_type)); \ - } \ - inline sai_status_t mock_remove_##sai_entry_type##_entries(REMOVE_BULK_PARAMS(sai_entry_type)) { \ + } \ + inline sai_status_t mock_remove_##sai_entry_type##_entries(REMOVE_BULK_PARAMS(sai_entry_type)) \ + { \ return mock_sai_##sai_api_name##_api->remove_##sai_entry_type##_entries(REMOVE_BULK_ARGS(sai_entry_type)); \ } -#define APPLY_MOCK_FUNCTIONS(sai_api_name, sai_object_type) \ - sai_##sai_api_name##_api->create_##sai_object_type = mock_create_##sai_object_type; \ - sai_##sai_api_name##_api->remove_##sai_object_type = mock_remove_##sai_object_type; \ - sai_##sai_api_name##_api->set_##sai_object_type##_attribute = mock_set_##sai_object_type##_attribute; -#define APPLY_ENTRY_MOCK_FUNCTIONS(sai_api_name, sai_entry_type) \ - sai_##sai_api_name##_api->create_##sai_entry_type##_entry = mock_create_##sai_entry_type##_entry; \ - sai_##sai_api_name##_api->remove_##sai_entry_type##_entry = mock_remove_##sai_entry_type##_entry; \ +#define APPLY_MOCK_FUNCTIONS(sai_api_name, sai_object_type) \ + sai_##sai_api_name##_api->create_##sai_object_type = mock_create_##sai_object_type; \ + sai_##sai_api_name##_api->remove_##sai_object_type = mock_remove_##sai_object_type; \ + sai_##sai_api_name##_api->set_##sai_object_type##_attribute = mock_set_##sai_object_type##_attribute; \ + sai_##sai_api_name##_api->create_##sai_object_type##s = mock_create_##sai_object_type##s; \ + sai_##sai_api_name##_api->remove_##sai_object_type##s = mock_remove_##sai_object_type##s; +#define APPLY_ENTRY_MOCK_FUNCTIONS(sai_api_name, sai_entry_type) \ + sai_##sai_api_name##_api->create_##sai_entry_type##_entry = mock_create_##sai_entry_type##_entry; \ + sai_##sai_api_name##_api->remove_##sai_entry_type##_entry = mock_remove_##sai_entry_type##_entry; \ sai_##sai_api_name##_api->create_##sai_entry_type##_entries = mock_create_##sai_entry_type##_entries; \ sai_##sai_api_name##_api->remove_##sai_entry_type##_entries = mock_remove_##sai_entry_type##_entries; -#define DEFINE_SAI_GENERIC_APIS_MOCK(sai_api_name, ...) \ - static sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ - static sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ - class mock_sai_##sai_api_name##_api_t { \ - public: \ - mock_sai_##sai_api_name##_api_t() { \ - FOR_EACH(DEFINE_ON_CALL_DEFAULTS, sai_api_name, __VA_ARGS__) \ - } \ - FOR_EACH(DEFINE_MOCK_METHODS, sai_api_name, __VA_ARGS__) \ - }; \ - static mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ - FOR_EACH(DEFINE_WRAPPER_FUNCTIONS, sai_api_name, __VA_ARGS__) \ - inline void apply_sai_##sai_api_name##_api_mock() { \ +#define DEFINE_SAI_GENERIC_APIS_MOCK(sai_api_name, ...) \ + static sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ + static sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ + class mock_sai_##sai_api_name##_api_t \ + { \ + public: \ + mock_sai_##sai_api_name##_api_t(){ \ + FOR_EACH(DEFINE_ON_CALL_DEFAULTS, sai_api_name, __VA_ARGS__) \ + } FOR_EACH(DEFINE_MOCK_METHODS, sai_api_name, __VA_ARGS__) \ + }; \ + static mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ + FOR_EACH(DEFINE_WRAPPER_FUNCTIONS, sai_api_name, __VA_ARGS__) \ + inline void apply_sai_##sai_api_name##_api_mock() \ + { \ mock_sai_##sai_api_name##_api = new NiceMock(); \ - old_sai_##sai_api_name##_api = sai_##sai_api_name##_api; \ - ut_sai_##sai_api_name##_api = *sai_##sai_api_name##_api; \ - sai_##sai_api_name##_api = &ut_sai_##sai_api_name##_api; \ - FOR_EACH(APPLY_MOCK_FUNCTIONS, sai_api_name, __VA_ARGS__) \ - } \ - inline void remove_sai_##sai_api_name##_api_mock() { \ - sai_##sai_api_name##_api = old_sai_##sai_api_name##_api; \ - delete mock_sai_##sai_api_name##_api; \ + old_sai_##sai_api_name##_api = sai_##sai_api_name##_api; \ + ut_sai_##sai_api_name##_api = *sai_##sai_api_name##_api; \ + sai_##sai_api_name##_api = &ut_sai_##sai_api_name##_api; \ + FOR_EACH(APPLY_MOCK_FUNCTIONS, sai_api_name, __VA_ARGS__) \ + } \ + inline void remove_sai_##sai_api_name##_api_mock() \ + { \ + sai_##sai_api_name##_api = old_sai_##sai_api_name##_api; \ + delete mock_sai_##sai_api_name##_api; \ + } + +// Use this macro when you need to mock both an object type and an entry type in the same SAI API. +#define DEFINE_SAI_API_COMBINED_MOCK(sai_api_name, sai_object_type, sai_entry_type) \ + static sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ + static sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ + class mock_sai_##sai_api_name##_api_t \ + { \ + public: \ + mock_sai_##sai_api_name##_api_t(){ \ + DEFINE_ENTRY_ON_CALL_DEFAULTS(sai_api_name, sai_entry_type) \ + DEFINE_ON_CALL_DEFAULTS(sai_api_name, sai_object_type) \ + } DEFINE_ENTRY_MOCK_METHODS(sai_api_name, sai_entry_type) \ + DEFINE_MOCK_METHODS(sai_api_name, sai_object_type) \ + }; \ + static mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ + DEFINE_WRAPPER_ENTRY_FUNCTIONS(sai_api_name, sai_entry_type) \ + DEFINE_WRAPPER_FUNCTIONS(sai_api_name, sai_object_type) \ + inline void apply_sai_##sai_api_name##_api_mock() \ + { \ + mock_sai_##sai_api_name##_api = new NiceMock(); \ + \ + old_sai_##sai_api_name##_api = sai_##sai_api_name##_api; \ + ut_sai_##sai_api_name##_api = *sai_##sai_api_name##_api; \ + sai_##sai_api_name##_api = &ut_sai_##sai_api_name##_api; \ + \ + APPLY_ENTRY_MOCK_FUNCTIONS(sai_api_name, sai_entry_type) \ + APPLY_MOCK_FUNCTIONS(sai_api_name, sai_object_type) \ + } \ + inline void remove_sai_##sai_api_name##_api_mock() \ + { \ + sai_##sai_api_name##_api = old_sai_##sai_api_name##_api; \ + delete mock_sai_##sai_api_name##_api; \ } #define DEFINE_SAI_ENTRY_APIS_MOCK(sai_api_name, ...) \ static sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ @@ -369,11 +429,10 @@ This is required since some sai_api do not support this function call yet. public: \ mock_sai_##sai_api_name##_api_t() { \ FOR_EACH(DEFINE_ENTRY_ON_CALL_DEFAULTS, sai_api_name, __VA_ARGS__) \ - } \ - FOR_EACH(DEFINE_MOCK_ENTRY_METHODS, sai_api_name, __VA_ARGS__) \ + } FOR_EACH(DEFINE_ENTRY_MOCK_METHODS, sai_api_name, __VA_ARGS__) \ }; \ static mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ - FOR_EACH(DEFINE_ENTRY_WRAPPER_FUNCTIONS, sai_api_name, __VA_ARGS__) \ + FOR_EACH(DEFINE_WRAPPER_ENTRY_FUNCTIONS, sai_api_name, __VA_ARGS__) \ inline void apply_sai_##sai_api_name##_api_mock() { \ mock_sai_##sai_api_name##_api = new NiceMock(); \ old_sai_##sai_api_name##_api = sai_##sai_api_name##_api; \ diff --git a/tests/mock_tests/ut_saihelper.cpp b/tests/mock_tests/ut_saihelper.cpp index def88394..083b1711 100644 --- a/tests/mock_tests/ut_saihelper.cpp +++ b/tests/mock_tests/ut_saihelper.cpp @@ -102,6 +102,7 @@ namespace ut_helper sai_api_query((sai_api_t)SAI_API_DASH_OUTBOUND_ROUTING, (void**)&sai_dash_outbound_routing_api); sai_api_query((sai_api_t)SAI_API_DASH_INBOUND_ROUTING, (void**)&sai_dash_inbound_routing_api); sai_api_query((sai_api_t)SAI_API_DASH_TUNNEL, (void**)&sai_dash_tunnel_api); + sai_api_query((sai_api_t)SAI_API_DASH_OUTBOUND_PORT_MAP, (void**)&sai_dash_outbound_port_map_api); sai_api_query((sai_api_t)SAI_API_DASH_TRUSTED_VNI, (void**)&sai_dash_trusted_vni_api); sai_api_query(SAI_API_STP, (void**)&sai_stp_api); return SAI_STATUS_SUCCESS;