From 2940340654a944aca107564ca3070b46d741386a Mon Sep 17 00:00:00 2001 From: Yakiv Huryk Date: Tue, 29 Oct 2024 12:53:49 +0200 Subject: [PATCH 1/2] [DASH] fix VNET Map logic This fixes a bug when adding a VNET map doesn't go to the addVnetMapPost() flow. The reason is that the addVnetMap() returns 'true' for normal flow, while it should return 'false' to keep the task is in the m_toSync for a post processing flow. Signed-off-by: Yakiv Huryk --- orchagent/dash/dashvnetorch.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/orchagent/dash/dashvnetorch.cpp b/orchagent/dash/dashvnetorch.cpp index a143c31484..dd01d1137d 100644 --- a/orchagent/dash/dashvnetorch.cpp +++ b/orchagent/dash/dashvnetorch.cpp @@ -310,7 +310,7 @@ bool DashVnetOrch::addOutboundCaToPa(const string& key, VnetMapBulkContext& ctxt else { SWSS_LOG_ERROR("Invalid encap type %d for %s", action.encap_type(), key.c_str()); - return false; + return true; } outbound_ca_to_pa_attrs.push_back(outbound_ca_to_pa_attr); @@ -360,7 +360,7 @@ bool DashVnetOrch::addOutboundCaToPa(const string& key, VnetMapBulkContext& ctxt object_statuses.emplace_back(); outbound_ca_to_pa_bulker_.create_entry(&object_statuses.back(), &outbound_ca_to_pa_entry, (uint32_t)outbound_ca_to_pa_attrs.size(), outbound_ca_to_pa_attrs.data()); - return true; + return false; } bool DashVnetOrch::addPaValidation(const string& key, VnetMapBulkContext& ctxt) @@ -401,7 +401,7 @@ bool DashVnetOrch::addPaValidation(const string& key, VnetMapBulkContext& ctxt) pa_refcount_table_[pa_ref_key] = 1; SWSS_LOG_INFO("Initialize PA refcount to 1 for PA IP %s", underlay_ip_str.c_str()); - return true; + return false; } bool DashVnetOrch::addVnetMap(const string& key, VnetMapBulkContext& ctxt) @@ -418,7 +418,7 @@ bool DashVnetOrch::addVnetMap(const string& key, VnetMapBulkContext& ctxt) SWSS_LOG_INFO("Not creating VNET map for %s since VNET %s doesn't exist", key.c_str(), ctxt.vnet_name.c_str()); return false; } - return addOutboundCaToPa(key, ctxt) && addPaValidation(key, ctxt); + return addOutboundCaToPa(key, ctxt) || addPaValidation(key, ctxt); } /* * If the VNET map is already added, don't add it to the bulker and From 9b7a7f61ec2a6bac8c4ff76e2d4996f7104045cc Mon Sep 17 00:00:00 2001 From: Yakiv Huryk Date: Wed, 25 Sep 2024 20:00:10 +0300 Subject: [PATCH 2/2] [DASH] add PA Validation orch * new DASH PA Validation orch is responsible for PA Validation table processing * VnetOrch now uses the new orch to create/remove PA Validation entries * update sai mock infrastructure to support dash api * add mock tests for DASH PA Validation orch --- orchagent/Makefile.am | 1 + orchagent/dash/dashpavalidationorch.cpp | 364 +++++++++++++++++ orchagent/dash/dashpavalidationorch.h | 52 +++ orchagent/dash/dashvnetorch.cpp | 163 +++----- orchagent/dash/dashvnetorch.h | 13 +- orchagent/orchdaemon.cpp | 8 +- orchagent/orchdaemon.h | 1 + tests/mock_tests/Makefile.am | 2 + tests/mock_tests/dash/dashpavalidation_ut.cpp | 371 ++++++++++++++++++ tests/mock_tests/mock_orchagent_main.h | 3 + tests/mock_tests/mock_sai_api.h | 58 +-- tests/mock_tests/ut_saihelper.cpp | 6 + 12 files changed, 890 insertions(+), 152 deletions(-) create mode 100644 orchagent/dash/dashpavalidationorch.cpp create mode 100644 orchagent/dash/dashpavalidationorch.h create mode 100644 tests/mock_tests/dash/dashpavalidation_ut.cpp diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index ae83c16d8f..0a20f8e2b7 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -113,6 +113,7 @@ orchagent_SOURCES = \ dash/dashaclorch.cpp \ dash/dashaclgroupmgr.cpp \ dash/dashtagmgr.cpp \ + dash/dashpavalidationorch.cpp \ dash/pbutils.cpp \ twamporch.cpp diff --git a/orchagent/dash/dashpavalidationorch.cpp b/orchagent/dash/dashpavalidationorch.cpp new file mode 100644 index 0000000000..e03399a321 --- /dev/null +++ b/orchagent/dash/dashpavalidationorch.cpp @@ -0,0 +1,364 @@ +#include "dashpavalidationorch.h" + +#include "dashvnetorch.h" +#include "crmorch.h" + +#include "taskworker.h" +#include "pbutils.h" +#include "saihelper.h" +#include "swssnet.h" + +#include +#include + +extern sai_dash_pa_validation_api_t* sai_dash_pa_validation_api; +extern size_t gMaxBulkSize; +extern CrmOrch *gCrmOrch; +extern sai_object_id_t gSwitchId; + +using namespace std; +using namespace swss; + +size_t IpAddressHash::operator()(swss::IpAddress addr) const +{ + size_t seed = 0; + const auto &inner = addr.getIp(); + boost::hash_combine(seed, inner.family); + if (inner.family == AF_INET) + { + boost::hash_combine(seed, inner.ip_addr.ipv4_addr); + } + else if (inner.family == AF_INET6) + { + boost::hash_combine(seed, inner.ip_addr.ipv6_addr); + } + return seed; +} + +DashPaValidationOrch::DashPaValidationOrch(swss::DBConnector *db, swss::ZmqServer *zmqServer): + vnetOrch_(nullptr), + ZmqOrch(db, {APP_DASH_PA_VALIDATION_TABLE_NAME}, zmqServer) +{ + SWSS_LOG_ENTER(); +} + +void DashPaValidationOrch::setVnetOrch(const DashVnetOrch *vnetOrch) +{ + vnetOrch_ = vnetOrch; +} + +bool DashPaValidationOrch::parseVni(const std::string& key, uint32_t& vni) { + std::istringstream iss(key); + + iss >> vni; + + return !iss.fail() && iss.eof(); +} + +bool DashPaValidationOrch::entryRefCountInc(const PaValidationEntry& entry) +{ + SWSS_LOG_ENTER(); + + bool new_entry = false; + + auto vni = vni_map_.find(entry.vni); + if (vni == vni_map_.end()) + { + new_entry = true; + vni_map_[entry.vni] = {{entry.address, 1}}; + } else { + auto& vni_addresses = vni->second; + auto address = vni_addresses.find(entry.address); + if (address == vni_addresses.end()) + { + new_entry = true; + vni_addresses[entry.address] = 1; + } else { + vni_addresses[entry.address]++; + } + + SWSS_LOG_DEBUG("PA Validation entry (%u, %s) refcount is increased to %" PRIu64, entry.vni, entry.address.to_string().c_str(), vni_addresses[entry.address]); + } + + return new_entry; +} + +bool DashPaValidationOrch::entryRefCountDec(const PaValidationEntry& entry) +{ + SWSS_LOG_ENTER(); + + auto vni = vni_map_.find(entry.vni); + if (vni == vni_map_.end()) + { + SWSS_LOG_WARN("PA Validaion entry VNI %u IP %s is removed or not created yet", entry.vni, entry.address.to_string().c_str()); + return false; + } + + auto& vni_addresses = vni->second; + auto address = vni_addresses.find(entry.address); + if (address == vni_addresses.end()) + { + SWSS_LOG_WARN("PA Validaion entry VNI %u IP %s is removed or not created yet", entry.vni, entry.address.to_string().c_str()); + return false; + } + + SWSS_LOG_DEBUG("PA Validation entry (%u, %s) refcount is decreased to %" PRIu64, entry.vni, entry.address.to_string().c_str(), address->second - 1); + + if (address->second == 1) + { + vni_addresses.erase(address); + if (vni_addresses.empty()) + { + vni_map_.erase(entry.vni); + } + + return true; + } else { + address->second--; + return false; + } +} + +bool DashPaValidationOrch::fetchVniAddresses(uint32_t vni, PaValidationEntryAddresses& addresses) const +{ + SWSS_LOG_ENTER(); + + auto it = vni_map_.find(vni); + if (it == vni_map_.end()) + { + return true; + } + + const auto& vni_addresses = it->second; + + addresses.reserve(vni_addresses.size()); + transform(vni_addresses.begin(), vni_addresses.end(), std::back_inserter(addresses), + [](const auto& kv) { return kv.first; }); + + return false; +} + +void DashPaValidationOrch::paValidationConfigProcess(const PaValidationEntryList& toCreate, const PaValidationEntryList& toRemove) +{ + SWSS_LOG_ENTER(); + + auto task_status = paValidationConfigApply(toCreate, toRemove); + if (task_status != task_success) + { + parseHandleSaiStatusFailure(task_status); + } +} + +task_process_status DashPaValidationOrch::paValidationConfigApply(const PaValidationEntryList& toCreate, const PaValidationEntryList& toRemove) +{ + EntityBulker bulker(sai_dash_pa_validation_api, gMaxBulkSize); + std::vector statuses; + + SWSS_LOG_ENTER(); + + statuses.reserve(toCreate.size() + toRemove.size()); + + for (const auto& entry: toCreate) + { + bool new_entry = entryRefCountInc(entry); + if (!new_entry) + { + // mark entry as created to omit error handling + statuses.emplace_back(SAI_STATUS_SUCCESS); + } else { + sai_attribute_t attr; + uint32_t attr_count = 1; + + sai_pa_validation_entry_t sai_entry; + sai_entry.vnet_id = entry.vnet_oid; + sai_entry.switch_id = gSwitchId; + swss::copy(sai_entry.sip, entry.address); + + attr.id = SAI_PA_VALIDATION_ENTRY_ATTR_ACTION; + attr.value.u32 = SAI_PA_VALIDATION_ENTRY_ACTION_PERMIT; + + statuses.emplace_back(); + bulker.create_entry(&statuses.back(), &sai_entry, attr_count, &attr); + + gCrmOrch->incCrmResUsedCounter(entry.address.isV4() ? CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION : CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION); + } + } + + for (const auto& entry: toRemove) + { + bool last_ref = entryRefCountDec(entry); + if (!last_ref) + { + // mark entry as removed to omit error handling + statuses.emplace_back(SAI_STATUS_SUCCESS); + } else { + sai_pa_validation_entry_t sai_entry; + sai_entry.vnet_id = entry.vnet_oid; + sai_entry.switch_id = gSwitchId; + swss::copy(sai_entry.sip, entry.address); + + statuses.emplace_back(); + bulker.remove_entry(&statuses.back(), &sai_entry); + + gCrmOrch->decCrmResUsedCounter(entry.address.isV4() ? CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION : CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION); + } + } + + bulker.flush(); + + size_t i = 0; + for (; i < toCreate.size(); i++) + { + const auto& entry = toCreate[i]; + const auto& status = statuses[i]; + + if (status == SAI_STATUS_SUCCESS) + { + continue; + } + + SWSS_LOG_ERROR("Failed to create PA validation entry for VNI %u IP %s", entry.vni, entry.address.to_string().c_str()); + task_process_status handle_status = handleSaiCreateStatus((sai_api_t) SAI_API_DASH_PA_VALIDATION, status); + if (handle_status != task_success) + { + return handle_status; + } + } + + for (;i < toRemove.size(); i++) + { + const auto& entry = toRemove[i]; + const auto& status = statuses[i]; + + if (status == SAI_STATUS_SUCCESS) + { + continue; + } + + SWSS_LOG_ERROR("Failed to remvoe PA validation entry for VNI %u IP %s", entry.vni, entry.address.to_string().c_str()); + task_process_status handle_status = handleSaiCreateStatus((sai_api_t) SAI_API_DASH_PA_VALIDATION, status); + if (handle_status != task_success) + { + return handle_status; + } + } + + return task_success; +} + +task_process_status DashPaValidationOrch::addPaValidationEntries(const PaValidationEntryList& entries) +{ + return paValidationConfigApply(entries, {}); +} + +task_process_status DashPaValidationOrch::removePaValidationEntries(const PaValidationEntryList& entries) +{ + return paValidationConfigApply({}, entries); +} + +void DashPaValidationOrch::doTask(ConsumerBase &consumer) +{ + SWSS_LOG_ENTER(); + + const auto& tn = consumer.getTableName(); + + SWSS_LOG_INFO("Table name: %s", tn.c_str()); + + if (tn == APP_DASH_PA_VALIDATION_TABLE_NAME) + { + doTaskPaValidation(consumer); + } + else + { + SWSS_LOG_ERROR("Unknown table: %s", tn.c_str()); + } +} + +void DashPaValidationOrch::doTaskPaValidation(ConsumerBase &consumer) +{ + SWSS_LOG_ENTER(); + + PaValidationEntryList toCreate, toRemove; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto tuple = it->second; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + + uint32_t vni; + if (!parseVni(key, vni)) + { + SWSS_LOG_WARN("Failed to parse VNI from PA Validation key %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + sai_object_id_t vnet_oid; + if (!vnetOrch_->getVnetByVni(vni, vnet_oid)) + { + SWSS_LOG_INFO("VNET for VNI %u is not created yet or removed", vni); + it++; + continue; + } + + if (op == SET_COMMAND) + { + dash::pa_validation::PaValidation pbEntry; + if (!parsePbMessage(kfvFieldsValues(tuple), pbEntry)) + { + SWSS_LOG_WARN("Failed to parse protobuff messaage for PA Validation (vni: %u)", vni); + it = consumer.m_toSync.erase(it); + continue; + } + + PaValidationEntryAddresses addresses; + if (!toAddresses(pbEntry, addresses)) + { + SWSS_LOG_WARN("Failed to parse PA Validation (vni: %u) addresses", vni); + it = consumer.m_toSync.erase(it); + continue; + } + + for (const auto addr : addresses) + { + toCreate.push_back(PaValidationEntry{vnet_oid, vni, addr}); + } + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + PaValidationEntryAddresses addresses; + bool empty = fetchVniAddresses(vni, addresses); + if (empty) + { + SWSS_LOG_WARN("PA validation entries for VNI %u are already removed or not created yet", vni); + it = consumer.m_toSync.erase(it); + continue; + } + + for (const auto addr : addresses) + { + toRemove.push_back(PaValidationEntry{vnet_oid, vni, addr}); + } + + it = consumer.m_toSync.erase(it); + } + } + + paValidationConfigProcess(toCreate, toRemove); +} + +bool DashPaValidationOrch::toAddresses(const dash::pa_validation::PaValidation& entry, PaValidationEntryAddresses &addresses) +{ + addresses.reserve(entry.addresses().size()); + + for (const auto& addr : entry.addresses()) + { + addresses.push_back(IpAddress(to_swss(addr))); + } + + return true; +} diff --git a/orchagent/dash/dashpavalidationorch.h b/orchagent/dash/dashpavalidationorch.h new file mode 100644 index 0000000000..9d5dd14015 --- /dev/null +++ b/orchagent/dash/dashpavalidationorch.h @@ -0,0 +1,52 @@ +#pragma once + +#include "zmqorch.h" +#include "bulker.h" +#include "ipaddress.h" + +#include "dash_api/pa_validation.pb.h" + +class DashVnetOrch; + +using PaValidationEntryAddresses = std::vector; + +struct PaValidationEntry +{ + sai_object_id_t vnet_oid; + uint32_t vni; + swss::IpAddress address; +}; + +using PaValidationEntryList = std::vector; + +struct IpAddressHash { + std::size_t operator()(swss::IpAddress value) const; +}; + +class DashPaValidationOrch : public ZmqOrch +{ +public: + DashPaValidationOrch(swss::DBConnector *db, swss::ZmqServer *zmqServer); + void setVnetOrch(const DashVnetOrch *vnetOrch); + task_process_status addPaValidationEntries(const PaValidationEntryList& entries); + task_process_status removePaValidationEntries(const PaValidationEntryList& entries); + +private: + using AddressRefcountMap = std::unordered_map; + using VniAddressMap = std::unordered_map; + + const DashVnetOrch *vnetOrch_; + VniAddressMap vni_map_; + + void doTask(ConsumerBase& consumer); + void doTaskPaValidation(ConsumerBase& consumer); + + bool entryRefCountInc(const PaValidationEntry& entry); + bool entryRefCountDec(const PaValidationEntry& entry); + bool fetchVniAddresses(uint32_t vni, PaValidationEntryAddresses& addresses) const; + void paValidationConfigProcess(const PaValidationEntryList& toCreate, const PaValidationEntryList& toRemove); + task_process_status paValidationConfigApply(const PaValidationEntryList& toCreate, const PaValidationEntryList& toRemove); + + bool parseVni(const std::string& key, uint32_t& vni); + bool toAddresses(const dash::pa_validation::PaValidation& entry, PaValidationEntryAddresses &addresses); +}; diff --git a/orchagent/dash/dashvnetorch.cpp b/orchagent/dash/dashvnetorch.cpp index dd01d1137d..e6c471697f 100644 --- a/orchagent/dash/dashvnetorch.cpp +++ b/orchagent/dash/dashvnetorch.cpp @@ -37,15 +37,27 @@ extern size_t gMaxBulkSize; extern CrmOrch *gCrmOrch; extern Directory gDirectory; -DashVnetOrch::DashVnetOrch(DBConnector *db, vector &tables, ZmqServer *zmqServer) : +DashVnetOrch::DashVnetOrch(DBConnector *db, const vector &tables, ZmqServer *zmqServer, DashPaValidationOrch *pa_validation_orch) : vnet_bulker_(sai_dash_vnet_api, gSwitchId, gMaxBulkSize), outbound_ca_to_pa_bulker_(sai_dash_outbound_ca_to_pa_api, gMaxBulkSize), - pa_validation_bulker_(sai_dash_pa_validation_api, gMaxBulkSize), - ZmqOrch(db, tables, zmqServer) + ZmqOrch(db, tables, zmqServer), + pa_validation_orch_(pa_validation_orch) { SWSS_LOG_ENTER(); } +bool DashVnetOrch::getVnetByVni(uint32_t vni, sai_object_id_t& vnet_oid) const +{ + auto vnet = vni_vnet_oid_table_.find(vni); + if (vnet == vni_vnet_oid_table_.end()) + { + return false; + } + + vnet_oid = vnet->second; + return true; +} + bool DashVnetOrch::addVnet(const string& vnet_name, DashVnetBulkContext& ctxt) { SWSS_LOG_ENTER(); @@ -89,6 +101,7 @@ bool DashVnetOrch::addVnetPost(const string& vnet_name, const DashVnetBulkContex VnetEntry entry = { id, ctxt.metadata }; vnet_table_[vnet_name] = entry; gVnetNameToId[vnet_name] = id; + vni_vnet_oid_table_[ctxt.metadata.vni()] = id; gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_DASH_VNET); @@ -112,6 +125,7 @@ bool DashVnetOrch::removeVnet(const string& vnet_name, DashVnetBulkContext& ctxt sai_object_id_t vni; VnetEntry entry = vnet_table_[vnet_name]; vni = entry.vni; + ctxt.metadata = entry.metadata; object_statuses.emplace_back(); vnet_bulker_.remove_entry(&object_statuses.back(), vni); @@ -150,6 +164,7 @@ bool DashVnetOrch::removeVnetPost(const string& vnet_name, const DashVnetBulkCon vnet_table_.erase(vnet_name); gVnetNameToId.erase(vnet_name); + vni_vnet_oid_table_.erase(ctxt.metadata.vni()); SWSS_LOG_INFO("Vnet entry removed for %s", vnet_name.c_str()); return true; @@ -367,40 +382,19 @@ bool DashVnetOrch::addPaValidation(const string& key, VnetMapBulkContext& ctxt) { SWSS_LOG_ENTER(); - auto& object_statuses = ctxt.pa_validation_object_statuses; - string underlay_ip_str = to_string(ctxt.metadata.underlay_ip()); - string pa_ref_key = ctxt.vnet_name + ":" + underlay_ip_str; - auto it = pa_refcount_table_.find(pa_ref_key); - if (it != pa_refcount_table_.end()) + auto vnet = vnet_table_.find(ctxt.vnet_name); + if (vnet == vnet_table_.end()) { - /* - * PA validation entry already exisits. Just increment refcount and add - * a dummy success status to satisfy postop - */ - object_statuses.emplace_back(SAI_STATUS_SUCCESS); - pa_refcount_table_[pa_ref_key]++; - SWSS_LOG_INFO("Increment PA refcount to %u for PA IP %s", - pa_refcount_table_[pa_ref_key], - underlay_ip_str.c_str()); - return true; + SWSS_LOG_ERROR("VNET %s is removed or not created yet", ctxt.vnet_name.c_str()); + return false; } - uint32_t attr_count = 1; - sai_pa_validation_entry_t pa_validation_entry; - pa_validation_entry.vnet_id = gVnetNameToId[ctxt.vnet_name]; - pa_validation_entry.switch_id = gSwitchId; - to_sai(ctxt.metadata.underlay_ip(), pa_validation_entry.sip); - sai_attribute_t pa_validation_attr; - - pa_validation_attr.id = SAI_PA_VALIDATION_ENTRY_ATTR_ACTION; - pa_validation_attr.value.u32 = SAI_PA_VALIDATION_ENTRY_ACTION_PERMIT; + PaValidationEntry entry; + entry.vnet_oid = vnet->second.vni; + entry.vni = vnet->second.metadata.vni(); + entry.address = IpAddress(to_swss(ctxt.metadata.underlay_ip())); + ctxt.pa_validation_entries.push_back(entry); - object_statuses.emplace_back(); - pa_validation_bulker_.create_entry(&object_statuses.back(), &pa_validation_entry, - attr_count, &pa_validation_attr); - pa_refcount_table_[pa_ref_key] = 1; - SWSS_LOG_INFO("Initialize PA refcount to 1 for PA IP %s", - underlay_ip_str.c_str()); return false; } @@ -466,36 +460,13 @@ bool DashVnetOrch::addPaValidationPost(const string& key, const VnetMapBulkConte { SWSS_LOG_ENTER(); - const auto& object_statuses = ctxt.pa_validation_object_statuses; - if (object_statuses.empty()) - { - return false; - } - - auto it_status = object_statuses.begin(); - string underlay_ip_str = to_string(ctxt.metadata.underlay_ip()); - string pa_ref_key = ctxt.vnet_name + ":" + underlay_ip_str; - sai_status_t status = *it_status++; - if (status != SAI_STATUS_SUCCESS) + auto task_status = pa_validation_orch_->addPaValidationEntries(ctxt.pa_validation_entries); + if (task_status != task_success) { - /* PA validation entry add failed. Remove PA refcount entry */ - pa_refcount_table_.erase(pa_ref_key); - if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) - { - // Retry if item exists in the bulker - return false; - } - SWSS_LOG_ERROR("Failed to create PA validation entry for %s", key.c_str()); - task_process_status handle_status = handleSaiCreateStatus((sai_api_t) SAI_API_DASH_PA_VALIDATION, status); - if (handle_status != task_success) - { - return parseHandleSaiStatusFailure(handle_status); - } + return parseHandleSaiStatusFailure(task_status); } - gCrmOrch->incCrmResUsedCounter(ctxt.metadata.underlay_ip().has_ipv4() ? CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION : CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION); - SWSS_LOG_INFO("PA validation entry for %s added", key.c_str()); return true; @@ -538,42 +509,19 @@ void DashVnetOrch::removePaValidation(const string& key, VnetMapBulkContext& ctx { SWSS_LOG_ENTER(); - auto& object_statuses = ctxt.pa_validation_object_statuses; - string underlay_ip = to_string(vnet_map_table_[key].metadata.underlay_ip()); - string pa_ref_key = ctxt.vnet_name + ":" + underlay_ip; - auto it = pa_refcount_table_.find(pa_ref_key); - if (it == pa_refcount_table_.end()) + auto vnet = vnet_table_.find(ctxt.vnet_name); + if (vnet == vnet_table_.end()) { + SWSS_LOG_ERROR("VNET %s is removed or not created yet", ctxt.vnet_name.c_str()); return; } - else - { - if (--pa_refcount_table_[pa_ref_key] > 0) - { - /* - * PA validation entry already exisits. Just decrement refcount and add - * a dummy success status to satisfy postop - */ - object_statuses.emplace_back(SAI_STATUS_SUCCESS); - SWSS_LOG_INFO("Decrement PA refcount to %u for PA IP %s", - pa_refcount_table_[pa_ref_key], - underlay_ip.c_str()); - return; - } - else - { - sai_pa_validation_entry_t pa_validation_entry; - pa_validation_entry.vnet_id = vnet_map_table_[key].dst_vnet_id; - pa_validation_entry.switch_id = gSwitchId; - to_sai(vnet_map_table_[key].metadata.underlay_ip(), pa_validation_entry.sip); - - object_statuses.emplace_back(); - pa_validation_bulker_.remove_entry(&object_statuses.back(), &pa_validation_entry); - SWSS_LOG_INFO("PA refcount refcount is zero for PA IP %s, removing refcount table entry", - underlay_ip.c_str()); - pa_refcount_table_.erase(pa_ref_key); - } - } + + PaValidationEntry entry; + entry.vnet_oid = vnet->second.vni; + entry.vni = vnet->second.metadata.vni(); + entry.address = IpAddress(to_swss(vnet_map_table_[key].metadata.underlay_ip())); + + ctxt.pa_validation_entries.push_back(entry); } bool DashVnetOrch::removeVnetMap(const string& key, VnetMapBulkContext& ctxt) @@ -632,34 +580,13 @@ bool DashVnetOrch::removePaValidationPost(const string& key, const VnetMapBulkCo { SWSS_LOG_ENTER(); - string underlay_ip = to_string(vnet_map_table_[key].metadata.underlay_ip()); - string pa_ref_key = ctxt.vnet_name + ":" + underlay_ip; - const auto& object_statuses = ctxt.pa_validation_object_statuses; - if (object_statuses.empty()) - { - return false; - } - - auto it_status = object_statuses.begin(); - sai_status_t status = *it_status++; - if (status != SAI_STATUS_SUCCESS) + auto task_status = pa_validation_orch_->removePaValidationEntries(ctxt.pa_validation_entries); + if (task_status != task_success) { - // Retry later if object has non-zero reference to it - if (status == SAI_STATUS_NOT_EXECUTED) - { - return false; - } - SWSS_LOG_ERROR("Failed to remove PA validation entry for %s", key.c_str()); - task_process_status handle_status = handleSaiRemoveStatus((sai_api_t) SAI_API_DASH_PA_VALIDATION, status); - if (handle_status != task_success) - { - return parseHandleSaiStatusFailure(handle_status); - } + return parseHandleSaiStatusFailure(task_status); } - gCrmOrch->decCrmResUsedCounter(vnet_map_table_[key].metadata.underlay_ip().has_ipv4() ? CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION : CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION); - SWSS_LOG_INFO("PA validation entry for %s removed", key.c_str()); return true; @@ -762,7 +689,6 @@ void DashVnetOrch::doTaskVnetMapTable(ConsumerBase& consumer) } outbound_ca_to_pa_bulker_.flush(); - pa_validation_bulker_.flush(); auto it_prev = consumer.m_toSync.begin(); while (it_prev != it) @@ -779,8 +705,7 @@ void DashVnetOrch::doTaskVnetMapTable(ConsumerBase& consumer) const auto& ctxt = found->second; const auto& outbound_ca_to_pa_object_statuses = ctxt.outbound_ca_to_pa_object_statuses; - const auto& pa_validation_object_statuses = ctxt.pa_validation_object_statuses; - if (outbound_ca_to_pa_object_statuses.empty() || pa_validation_object_statuses.empty()) + if (outbound_ca_to_pa_object_statuses.empty()) { it_prev++; continue; diff --git a/orchagent/dash/dashvnetorch.h b/orchagent/dash/dashvnetorch.h index 4a3acc5845..087eeb2ed8 100644 --- a/orchagent/dash/dashvnetorch.h +++ b/orchagent/dash/dashvnetorch.h @@ -13,6 +13,7 @@ #include "timer.h" #include "zmqorch.h" #include "zmqserver.h" +#include "dashpavalidationorch.h" #include "dash_api/vnet.pb.h" #include "dash_api/vnet_mapping.pb.h" @@ -32,7 +33,7 @@ struct VnetMapEntry typedef std::unordered_map DashVnetTable; typedef std::unordered_map DashVnetMapTable; -typedef std::unordered_map PaRefCountTable; +typedef std::unordered_map VniVnetOidTable; struct DashVnetBulkContext { @@ -57,8 +58,8 @@ struct VnetMapBulkContext std::string vnet_name; swss::IpAddress dip; dash::vnet_mapping::VnetMapping metadata; + PaValidationEntryList pa_validation_entries; std::deque outbound_ca_to_pa_object_statuses; - std::deque pa_validation_object_statuses; VnetMapBulkContext() {} VnetMapBulkContext(const VnetMapBulkContext&) = delete; @@ -67,22 +68,22 @@ struct VnetMapBulkContext void clear() { outbound_ca_to_pa_object_statuses.clear(); - pa_validation_object_statuses.clear(); } }; class DashVnetOrch : public ZmqOrch { public: - DashVnetOrch(swss::DBConnector *db, std::vector &tables, swss::ZmqServer *zmqServer); + DashVnetOrch(swss::DBConnector *db, const std::vector &tables, swss::ZmqServer *zmqServer, DashPaValidationOrch *pa_validation_orch); + bool getVnetByVni(uint32_t vni, sai_object_id_t& vnet_oid) const; private: + DashPaValidationOrch *pa_validation_orch_; DashVnetTable vnet_table_; DashVnetMapTable vnet_map_table_; - PaRefCountTable pa_refcount_table_; + VniVnetOidTable vni_vnet_oid_table_; ObjectBulker vnet_bulker_; EntityBulker outbound_ca_to_pa_bulker_; - EntityBulker pa_validation_bulker_; void doTask(ConsumerBase &consumer); void doTaskVnetTable(ConsumerBase &consumer); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index f6e9428ab6..161d24c5c9 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -250,13 +250,18 @@ bool OrchDaemon::init() NvgreTunnelMapOrch *nvgre_tunnel_map_orch = new NvgreTunnelMapOrch(m_configDb, CFG_NVGRE_TUNNEL_MAP_TABLE_NAME); gDirectory.set(nvgre_tunnel_map_orch); + DashPaValidationOrch *dash_pa_validation_orch = new DashPaValidationOrch(m_applDb, m_zmqServer); + gDirectory.set(dash_pa_validation_orch); + vector dash_vnet_tables = { APP_DASH_VNET_TABLE_NAME, APP_DASH_VNET_MAPPING_TABLE_NAME }; - DashVnetOrch *dash_vnet_orch = new DashVnetOrch(m_applDb, dash_vnet_tables, m_zmqServer); + DashVnetOrch *dash_vnet_orch = new DashVnetOrch(m_applDb, dash_vnet_tables, m_zmqServer, dash_pa_validation_orch); gDirectory.set(dash_vnet_orch); + dash_pa_validation_orch->setVnetOrch(dash_vnet_orch); + vector dash_tables = { APP_DASH_APPLIANCE_TABLE_NAME, APP_DASH_ROUTING_TYPE_TABLE_NAME, @@ -524,6 +529,7 @@ bool OrchDaemon::init() m_orchList.push_back(dash_vnet_orch); m_orchList.push_back(dash_route_orch); m_orchList.push_back(dash_orch); + m_orchList.push_back(dash_pa_validation_orch); if (m_fabricEnabled) { diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 2473848bf5..8af100afcb 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -51,6 +51,7 @@ #include "dash/dashorch.h" #include "dash/dashrouteorch.h" #include "dash/dashvnetorch.h" +#include "dash/dashpavalidationorch.h" #include using namespace swss; diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 126f4e88c5..f8cd4624e0 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -65,6 +65,7 @@ tests_SOURCES = aclorch_ut.cpp \ twamporch_ut.cpp \ flexcounter_ut.cpp \ mock_orch_test.cpp \ + dash/dashpavalidation_ut.cpp \ $(top_srcdir)/warmrestart/warmRestartHelper.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ $(top_srcdir)/lib/subintf.cpp \ @@ -135,6 +136,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/dash/dashtagmgr.cpp \ $(top_srcdir)/orchagent/dash/dashrouteorch.cpp \ $(top_srcdir)/orchagent/dash/dashvnetorch.cpp \ + $(top_srcdir)/orchagent/dash/dashpavalidationorch.cpp \ $(top_srcdir)/cfgmgr/buffermgrdyn.cpp \ $(top_srcdir)/warmrestart/warmRestartAssist.cpp \ $(top_srcdir)/orchagent/dash/pbutils.cpp \ diff --git a/tests/mock_tests/dash/dashpavalidation_ut.cpp b/tests/mock_tests/dash/dashpavalidation_ut.cpp new file mode 100644 index 0000000000..9b7adda117 --- /dev/null +++ b/tests/mock_tests/dash/dashpavalidation_ut.cpp @@ -0,0 +1,371 @@ +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_sai_api.h" +#include "mock_orch_test.h" +#define private public +#include "dashpavalidationorch.h" +#include "dashvnetorch.h" +#undef private + +#include "dash_api/pa_validation.pb.h" + +#include +#include + +using namespace swss; +using namespace std; + +namespace dash_test { + DEFINE_SAI_API_MOCK_DASH(pa_validation); + DEFINE_SAI_GENERIC_API_MOCK(dash_vnet, vnet); + DEFINE_SAI_API_MOCK_DASH(outbound_ca_to_pa); + + struct PaValidaionTestEntry + { + string vni; + IpAddress address; + + inline bool operator==(const PaValidaionTestEntry &o) const + { + return vni == o.vni && address == o.address; + } + }; + + using PaValidaionDb = vector; + + struct PaValidaionConfig + { + string vni; + vector addresses; + }; + + struct DashPaValidationTest : public mock_orch_test::MockOrchTest + { + PaValidaionDb db; + unique_ptr vnetorch; + const vector vnis = { 100, 200, 300 }; + + bool addEntry(const PaValidaionTestEntry& entry) + { + auto exists = checkExists(entry); + if (exists) { + return false; + } + + db.push_back(entry); + return true; + } + + bool removeEntry(const PaValidaionTestEntry& entry) + { + auto exists = find(begin(db), end(db), entry); + if (exists == end(db)) { + return false; + } + + db.erase(exists); + return true; + } + + bool checkExists(const PaValidaionTestEntry& entry) + { + return find(begin(db), end(db), entry) != db.end(); + } + + PaValidaionTestEntry fromSai(const sai_pa_validation_entry_t& entry) + { + ip_addr_t addr; + if (entry.sip.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + addr.family = AF_INET; + addr.ip_addr.ipv4_addr = entry.sip.addr.ip4; + } else { + addr.family = AF_INET6; + memcpy(addr.ip_addr.ipv6_addr, entry.sip.addr.ip6, sizeof(entry.sip.addr.ip6)); + } + + return PaValidaionTestEntry{to_string(entry.vnet_id), IpAddress(addr)}; + } + + void initVnetOrch() + { + vnetorch = make_unique(m_app_db.get(), vector(), nullptr, nullptr); + for (auto vni : vnis) + { + vnetorch->vni_vnet_oid_table_[vni] = vni; + } + } + + sai_status_t handleBulkCreate(const sai_pa_validation_entry_t *entries, uint32_t count, sai_status_t *object_statuses) + { + sai_status_t status = SAI_STATUS_SUCCESS; + + for (uint32_t i = 0; i < count; i++) + { + bool unique = addEntry(fromSai(entries[i])); + object_statuses[i] = unique ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; + + if (!unique) + { + status = SAI_STATUS_FAILURE; + } + } + + return status; + } + + sai_status_t handleBulkRemove(const sai_pa_validation_entry_t *entries, uint32_t count, sai_status_t *object_statuses) + { + sai_status_t status = SAI_STATUS_SUCCESS; + + for (uint32_t i = 0; i < count; i++) + { + bool removed = removeEntry(fromSai(entries[i])); + object_statuses[i] = removed ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; + + if (!removed) + { + status = SAI_STATUS_FAILURE; + } + } + + return status; + } + + void PostSetUp() override + { + INIT_SAI_API_MOCK(dash_pa_validation); + MockSaiApis(); + + db.clear(); + initVnetOrch(); + + EXPECT_CALL(*mock_sai_dash_pa_validation_api, create_pa_validation_entries).WillRepeatedly(testing::Invoke( + [this](uint32_t object_count, const sai_pa_validation_entry_t *pa_validation_entry, const uint32_t *attr_count, + const sai_attribute_t **attr_list, sai_bulk_op_error_mode_t mode, sai_status_t *object_statuses) { + return handleBulkCreate(pa_validation_entry, object_count, object_statuses); + })); + + EXPECT_CALL(*mock_sai_dash_pa_validation_api, remove_pa_validation_entries).WillRepeatedly(testing::Invoke( + [this](uint32_t object_count, const sai_pa_validation_entry_t *pa_validation_entry, + sai_bulk_op_error_mode_t mode, sai_status_t *object_statuses) { + return handleBulkRemove(pa_validation_entry, object_count, object_statuses); + })); + } + + void PreTearDown() override + { + RestoreSaiApis(); + } + + dash::pa_validation::PaValidation toPb(const PaValidaionConfig& config) + { + dash::pa_validation::PaValidation pb; + for (const auto& addr : config.addresses) + { + dash::types::IpAddress pbaddr; + if (addr.isV4()) + { + pbaddr.set_ipv4(addr.getV4Addr()); + } else { + pbaddr.set_ipv6(string((const char*)addr.getV6Addr(), 16)); + } + *pb.add_addresses() = pbaddr; + } + + return pb; + } + + unique_ptr makeConsumerWithConfig(const std::vector& toCreate, const std::vector& toRemove) + { + deque tasks; + + for (const auto& entry : toCreate) + { + auto pb = toPb(entry); + tasks.push_back({ entry.vni, SET_COMMAND, { {"pb", pb.SerializeAsString()} }}); + } + + for (const auto& entry : toRemove) + { + tasks.push_back({ entry.vni, DEL_COMMAND, { }}); + } + + auto consumer = unique_ptr(new Consumer( + new ConsumerStateTable(m_app_db.get(), APP_DASH_PA_VALIDATION_TABLE_NAME, 1, 1), + nullptr, APP_DASH_PA_VALIDATION_TABLE_NAME)); + consumer->addToSync(tasks); + + return consumer; + } + + void doConfig(DashPaValidationOrch &pa, const std::vector& toCreate, const std::vector& toRemove) + { + auto consumer = makeConsumerWithConfig(toCreate, toRemove); + + pa.doTask(*consumer); + }; + + void validateDb(const vector &expected) + { + for (const auto& entry : expected) + { + ASSERT_TRUE(checkExists(entry)) << "Entry not found in SAI db"; + } + + ASSERT_EQ(expected.size(), db.size()) << "DB size is not expected"; + } + + uint64_t getCrmCounterIpV4() const + { + auto const &resourceMap = Portal::CrmOrchInternal::getResourceMap(gCrmOrch); + return resourceMap.at(CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION).countersMap.at("STATS").usedCounter; + } + + uint64_t getCrmCounterIpV6() const + { + auto const &resourceMap = Portal::CrmOrchInternal::getResourceMap(gCrmOrch); + return resourceMap.at(CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION).countersMap.at("STATS").usedCounter; + } + }; + + TEST_F(DashPaValidationTest, CreateRemove) + { + DashPaValidationOrch pa(m_app_db.get(), nullptr); + pa.setVnetOrch(vnetorch.get()); + + auto config = PaValidaionConfig {"100", {IpAddress("1.1.1.1"), IpAddress("1.1.1.2")}}; + doConfig(pa, { config }, {}); + + validateDb({ + PaValidaionTestEntry { "100", IpAddress("1.1.1.1") }, + PaValidaionTestEntry { "100", IpAddress("1.1.1.2") } + }); + + config = PaValidaionConfig {"100", {}}; + + doConfig(pa, { }, { config }); + validateDb({}); + } + + TEST_F(DashPaValidationTest, CreateAppend) + { + DashPaValidationOrch pa(m_app_db.get(), nullptr); + pa.setVnetOrch(vnetorch.get()); + + auto config = PaValidaionConfig {"100", {IpAddress("1.1.1.1"), IpAddress("1.1.1.2")}}; + doConfig(pa, { config }, {}); + + config = PaValidaionConfig {"100", {IpAddress("1.1.1.3"), IpAddress("1.1.1.2")}}; + doConfig(pa, { config }, {}); + + validateDb({ + PaValidaionTestEntry { "100", IpAddress("1.1.1.1") }, + PaValidaionTestEntry { "100", IpAddress("1.1.1.2") }, + PaValidaionTestEntry { "100", IpAddress("1.1.1.3") } + }); + + config = PaValidaionConfig {"100", {}}; + doConfig(pa, { }, { config }); + + validateDb({ + PaValidaionTestEntry { "100", IpAddress("1.1.1.2") } // this entry is created twice + }); + + config = PaValidaionConfig {"100", {}}; + doConfig(pa, { }, { config }); + validateDb({}); + } + + TEST_F(DashPaValidationTest, CreateFromVnetOrch) + { + DashPaValidationOrch pa(m_app_db.get(), nullptr); + pa.setVnetOrch(vnetorch.get()); + + // Create from ZMQ + auto config = PaValidaionConfig {"100", {IpAddress("1.1.1.1"), IpAddress("1.1.1.2")}}; + doConfig(pa, { config }, {}); + + validateDb({ + PaValidaionTestEntry { "100", IpAddress("1.1.1.1") }, + PaValidaionTestEntry { "100", IpAddress("1.1.1.2") } + }); + + // Create the same entry from VnetOrch via PA validation Orch API + auto vnet_orch_entries = PaValidationEntryList { + PaValidationEntry { + vnetorch->vni_vnet_oid_table_[100], + 100, + IpAddress("1.1.1.1") + } + }; + pa.addPaValidationEntries(vnet_orch_entries); + + validateDb({ + PaValidaionTestEntry { "100", IpAddress("1.1.1.1") }, + PaValidaionTestEntry { "100", IpAddress("1.1.1.2") } + }); + + // Remove all the entries for 100, the one created by VnetOrch should still be active + config = PaValidaionConfig {"100", {}}; + doConfig(pa, { }, { config }); + + validateDb({ + PaValidaionTestEntry { "100", IpAddress("1.1.1.1") } + }); + + pa.removePaValidationEntries(vnet_orch_entries); + validateDb({}); + } + + TEST_F(DashPaValidationTest, CRM) + { + DashPaValidationOrch pa(m_app_db.get(), nullptr); + pa.setVnetOrch(vnetorch.get()); + + auto config = PaValidaionConfig {"100", {IpAddress("1.1.1.1"), IpAddress("::1")}}; + doConfig(pa, { config }, {}); + + auto crm_v4 = getCrmCounterIpV4(); + auto crm_v6 = getCrmCounterIpV6(); + ASSERT_EQ(crm_v4, 1); + ASSERT_EQ(crm_v6, 1); + + // Apply the same entry again, the CRM should not change + doConfig(pa, { config }, {}); + + crm_v4 = getCrmCounterIpV4(); + crm_v6 = getCrmCounterIpV6(); + ASSERT_EQ(crm_v4, 1); + ASSERT_EQ(crm_v6, 1); + + // add new addresses + config = PaValidaionConfig {"100", {IpAddress("1.1.1.2"), IpAddress("::2")}}; + doConfig(pa, { config }, {}); + + crm_v4 = getCrmCounterIpV4(); + crm_v6 = getCrmCounterIpV6(); + ASSERT_EQ(crm_v4, 2); + ASSERT_EQ(crm_v6, 2); + + config = PaValidaionConfig {"100", {}}; + doConfig(pa, { }, { config }); + + // there are still entries that are created twice + crm_v4 = getCrmCounterIpV4(); + crm_v6 = getCrmCounterIpV6(); + ASSERT_EQ(crm_v4, 1); + ASSERT_EQ(crm_v6, 1); + + config = PaValidaionConfig {"100", {}}; + doConfig(pa, { }, { config }); + + // all removed now + crm_v4 = getCrmCounterIpV4(); + crm_v6 = getCrmCounterIpV6(); + ASSERT_EQ(crm_v4, 0); + ASSERT_EQ(crm_v6, 0); + + validateDb({}); + } +} diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index f2469a09ef..eca290af90 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -94,3 +94,6 @@ extern sai_tam_api_t* sai_tam_api; extern sai_dash_vip_api_t* sai_dash_vip_api; extern sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; extern sai_dash_eni_api_t* sai_dash_eni_api; +extern sai_dash_pa_validation_api_t* sai_dash_pa_validation_api; +extern sai_dash_vnet_api_t* sai_dash_vnet_api; +extern sai_dash_outbound_ca_to_pa_api_t* sai_dash_outbound_ca_to_pa_api; diff --git a/tests/mock_tests/mock_sai_api.h b/tests/mock_tests/mock_sai_api.h index 58e3c9da23..3cf4d31a80 100644 --- a/tests/mock_tests/mock_sai_api.h +++ b/tests/mock_tests/mock_sai_api.h @@ -45,33 +45,39 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the 6. Define a method to apply the mock 7. Define a method to remove the mock */ -#define DEFINE_SAI_API_MOCK(sai_object_type) \ - static sai_##sai_object_type##_api_t *old_sai_##sai_object_type##_api; \ - static sai_##sai_object_type##_api_t ut_sai_##sai_object_type##_api; \ - class mock_sai_##sai_object_type##_api_t \ + +// The DASH API has a special macro since for DASH the API != entry type (e.g. sai_*dash*_pa_validation_api_t for pa_validation_entry) +#define DEFINE_SAI_API_MOCK_DASH(sai_object_type) DEFINE_SAI_API_MOCK_IMPL(sai_object_type, dash_##sai_object_type) + +#define DEFINE_SAI_API_MOCK(sai_object_type) DEFINE_SAI_API_MOCK_IMPL(sai_object_type, sai_object_type) + +#define DEFINE_SAI_API_MOCK_IMPL(sai_object_type, api) \ + static sai_##api##_api_t *old_sai_##api##_api; \ + static sai_##api##_api_t ut_sai_##api##_api; \ + class mock_sai_##api##_api_t \ { \ public: \ - mock_sai_##sai_object_type##_api_t() \ + mock_sai_##api##_api_t() \ { \ ON_CALL(*this, create_##sai_object_type##_entry) \ .WillByDefault( \ [this](CREATE_PARAMS(sai_object_type)) { \ - return old_sai_##sai_object_type##_api->create_##sai_object_type##_entry(CREATE_ARGS(sai_object_type)); \ + return old_sai_##api##_api->create_##sai_object_type##_entry(CREATE_ARGS(sai_object_type)); \ }); \ ON_CALL(*this, remove_##sai_object_type##_entry) \ .WillByDefault( \ [this](REMOVE_PARAMS(sai_object_type)) { \ - return old_sai_##sai_object_type##_api->remove_##sai_object_type##_entry(REMOVE_ARGS(sai_object_type)); \ + return old_sai_##api##_api->remove_##sai_object_type##_entry(REMOVE_ARGS(sai_object_type)); \ }); \ ON_CALL(*this, create_##sai_object_type##_entries) \ .WillByDefault( \ [this](CREATE_BULK_PARAMS(sai_object_type)) { \ - return old_sai_##sai_object_type##_api->create_##sai_object_type##_entries(CREATE_BULK_ARGS(sai_object_type)); \ + return old_sai_##api##_api->create_##sai_object_type##_entries(CREATE_BULK_ARGS(sai_object_type)); \ }); \ ON_CALL(*this, remove_##sai_object_type##_entries) \ .WillByDefault( \ [this](REMOVE_BULK_PARAMS(sai_object_type)) { \ - return old_sai_##sai_object_type##_api->remove_##sai_object_type##_entries(REMOVE_BULK_ARGS(sai_object_type)); \ + return old_sai_##api##_api->remove_##sai_object_type##_entries(REMOVE_BULK_ARGS(sai_object_type)); \ }); \ } \ MOCK_METHOD3(create_##sai_object_type##_entry, sai_status_t(CREATE_PARAMS(sai_object_type))); \ @@ -79,40 +85,40 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the MOCK_METHOD6(create_##sai_object_type##_entries, sai_status_t(CREATE_BULK_PARAMS(sai_object_type))); \ MOCK_METHOD4(remove_##sai_object_type##_entries, sai_status_t(REMOVE_BULK_PARAMS(sai_object_type))); \ }; \ - static mock_sai_##sai_object_type##_api_t *mock_sai_##sai_object_type##_api; \ + static mock_sai_##api##_api_t *mock_sai_##api##_api; \ inline sai_status_t mock_create_##sai_object_type##_entry(CREATE_PARAMS(sai_object_type)) \ { \ - return mock_sai_##sai_object_type##_api->create_##sai_object_type##_entry(CREATE_ARGS(sai_object_type)); \ + return mock_sai_##api##_api->create_##sai_object_type##_entry(CREATE_ARGS(sai_object_type)); \ } \ inline sai_status_t mock_remove_##sai_object_type##_entry(REMOVE_PARAMS(sai_object_type)) \ { \ - return mock_sai_##sai_object_type##_api->remove_##sai_object_type##_entry(REMOVE_ARGS(sai_object_type)); \ + return mock_sai_##api##_api->remove_##sai_object_type##_entry(REMOVE_ARGS(sai_object_type)); \ } \ inline sai_status_t mock_create_##sai_object_type##_entries(CREATE_BULK_PARAMS(sai_object_type)) \ { \ - return mock_sai_##sai_object_type##_api->create_##sai_object_type##_entries(CREATE_BULK_ARGS(sai_object_type)); \ + return mock_sai_##api##_api->create_##sai_object_type##_entries(CREATE_BULK_ARGS(sai_object_type)); \ } \ inline sai_status_t mock_remove_##sai_object_type##_entries(REMOVE_BULK_PARAMS(sai_object_type)) \ { \ - return mock_sai_##sai_object_type##_api->remove_##sai_object_type##_entries(REMOVE_BULK_ARGS(sai_object_type)); \ + return mock_sai_##api##_api->remove_##sai_object_type##_entries(REMOVE_BULK_ARGS(sai_object_type)); \ } \ - inline void apply_sai_##sai_object_type##_api_mock() \ + inline void apply_sai_##api##_api_mock() \ { \ - mock_sai_##sai_object_type##_api = new NiceMock(); \ + mock_sai_##api##_api = new NiceMock(); \ \ - old_sai_##sai_object_type##_api = sai_##sai_object_type##_api; \ - ut_sai_##sai_object_type##_api = *sai_##sai_object_type##_api; \ - sai_##sai_object_type##_api = &ut_sai_##sai_object_type##_api; \ + old_sai_##api##_api = sai_##api##_api; \ + ut_sai_##api##_api = *sai_##api##_api; \ + sai_##api##_api = &ut_sai_##api##_api; \ \ - sai_##sai_object_type##_api->create_##sai_object_type##_entry = mock_create_##sai_object_type##_entry; \ - sai_##sai_object_type##_api->remove_##sai_object_type##_entry = mock_remove_##sai_object_type##_entry; \ - sai_##sai_object_type##_api->create_##sai_object_type##_entries = mock_create_##sai_object_type##_entries; \ - sai_##sai_object_type##_api->remove_##sai_object_type##_entries = mock_remove_##sai_object_type##_entries; \ + sai_##api##_api->create_##sai_object_type##_entry = mock_create_##sai_object_type##_entry; \ + sai_##api##_api->remove_##sai_object_type##_entry = mock_remove_##sai_object_type##_entry; \ + sai_##api##_api->create_##sai_object_type##_entries = mock_create_##sai_object_type##_entries; \ + sai_##api##_api->remove_##sai_object_type##_entries = mock_remove_##sai_object_type##_entries; \ } \ - inline void remove_sai_##sai_object_type##_api_mock() \ + inline void remove_sai_##api##_api_mock() \ { \ - sai_##sai_object_type##_api = old_sai_##sai_object_type##_api; \ - delete mock_sai_##sai_object_type##_api; \ + sai_##api##_api = old_sai_##api##_api; \ + delete mock_sai_##api##_api; \ } #define DEFINE_SAI_GENERIC_API_MOCK(sai_api_name, sai_object_type) \ diff --git a/tests/mock_tests/ut_saihelper.cpp b/tests/mock_tests/ut_saihelper.cpp index 269f54f06b..077910ce04 100644 --- a/tests/mock_tests/ut_saihelper.cpp +++ b/tests/mock_tests/ut_saihelper.cpp @@ -94,6 +94,9 @@ namespace ut_helper sai_api_query((sai_api_t)SAI_API_DASH_VIP, (void**)&sai_dash_vip_api); 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_ENI, (void**)&sai_dash_eni_api); + sai_api_query((sai_api_t)SAI_API_DASH_PA_VALIDATION, (void**)&sai_dash_pa_validation_api); + sai_api_query((sai_api_t)SAI_API_DASH_VNET, (void**)&sai_dash_vnet_api); + sai_api_query((sai_api_t)SAI_API_DASH_OUTBOUND_CA_TO_PA, (void**)&sai_dash_outbound_ca_to_pa_api); return SAI_STATUS_SUCCESS; } @@ -128,6 +131,9 @@ namespace ut_helper sai_dash_vip_api = nullptr; sai_dash_direction_lookup_api = nullptr; sai_dash_eni_api = nullptr; + sai_dash_pa_validation_api = nullptr; + sai_dash_vnet_api = nullptr; + sai_dash_outbound_ca_to_pa_api = nullptr; return SAI_STATUS_SUCCESS; }