From 4a61327e14502f6b50819b00557281ed8aba00ac Mon Sep 17 00:00:00 2001 From: Yakiv Huryk Date: Wed, 25 Sep 2024 20:00:10 +0300 Subject: [PATCH] [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 | 308 +++++++++++++++ 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, 827 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 5224751f73..53b75d9580 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..0af48f3bf1 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(ctxt.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 0d2ab1c200..56a04b3822 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 0f5afa4486..413c2e8c76 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..5e9e2bf3e5 --- /dev/null +++ b/tests/mock_tests/dash/dashpavalidation_ut.cpp @@ -0,0 +1,308 @@ +#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())); + } + *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"; + } + }; + + 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({}); + } +} 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; }