/*
 * SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
 * Copyright (c) 2012-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef AGG_FABRIC_H_
#define AGG_FABRIC_H_

#include "common_types.h"

#include "agg_node.h"
#include "agg_types.h"
#include "am_common.h"
#include "am_log.h"
#include "dump_file.h"
#include "fabric_dump_parser.h"
#include "fabric_provider.h"
#include "port_data.h"
#include "restart_info.h"
#include "smx/smx_types.h"
#include "telemetry/telemetry.h"
#include "thread_pool.h"
#include "tree_manager.h"
class SharableMlid;
using SharableMlidPtr = std::shared_ptr<SharableMlid>;
using MapAggNodeKeyToMlidList = std::map<uint64_t, ListLids>;

class AggTree;
class AggTreeDeleter;

using AggTreeUniquePtr = std::unique_ptr<AggTree, AggTreeDeleter>;
using MapTreeIdToAggTree = std::map<sharp_trees_t, AggTreeUniquePtr>;
using MapExtJobIdToMapTreeIdToTree = std::unordered_map<SharpExtJobId, MapTreeIdToAggTree>;

enum class DynamicTreeAlgoEnum
{
    SUPER_POD_ORIENTED_ALGORITHM = 0,
    QUASI_FAT_TREE_ORIENTED_ALGORITHM = 1
};

class AggTreeDeleter
{
   public:
    void operator()(AggTree* ptr) const;
};

class HostInfo
{
    string m_host_name_;
    sharp_sd_info m_sharpd_info_;
    MapHcaPortDataPtr m_ports_data_;

    static sharpd_id_t m_next_id_;

   public:
    explicit HostInfo(const string& host_name);

    void AddPortData(PortData* p_port_data);
    void ChangePortDataState(const hca_port_key_t port_key, const bool should_enable);
    void EnablePortData(const hca_port_key_t port_key);
    void DisablePortData(const hca_port_key_t port_key);
    void DeletePortFromPortsData(const hca_port_key_t port_key);

    inline const string& GetHostName() const { return m_host_name_; }
    inline const sharp_sd_info& GetSharpdInfo() const { return m_sharpd_info_; }
    inline sharpd_id_t GetSharpdId() const { return m_sharpd_info_.sharpd_id; }
    inline const MapHcaPortDataPtr& GetPortsData() const { return m_ports_data_; }
};

struct SpanningInfo
{
    AggNodeFabricInfo* m_agg_node;
    uint16_t m_trees_load;
    uint32_t m_leafs_spanning_load;
    SetAnFabricInfoPtr m_spanning_set;
    uint16_t m_tree_turns;
    uint16_t m_tree_child_idx;   // Number of children

    SpanningInfo() : m_agg_node(NULL), m_trees_load(0), m_leafs_spanning_load(0), m_tree_turns(0), m_tree_child_idx(0) {}

    // Prefer build tree using node that spans over maximal leafs, with minimal
    // load and less used turns (pair of two consecutive paths)
    int32_t GetScore() const { return (int32_t)m_spanning_set.size() * 5 - m_trees_load - m_tree_turns; }

    bool operator<(SpanningInfo const& rhs) const;
    bool operator>(SpanningInfo const& rhs) const { return (rhs < *this); }
};

// the same mlid is shared between all planes
class SharableMlid
{
    lid_t m_mlid_;   // mlid of all-gather multicast tree

   public:
    SharableMlid(lid_t mlid) : m_mlid_(mlid) {}
    ~SharableMlid();
    lid_t GetMlid() { return m_mlid_; }
};

class Fabric
{
    MapHCAPortDataUniquePtr m_hca_port_data_by_key_;
    MapPortDataUniquePtr m_an_port_data_by_key_;
    VecPortDataPtr m_old_ports_;
    AggregatedLogMessage m_disabled_ports_msg_;
    MapStrListAggNodeFabricPtr m_agg_node_by_desc_;         // Nodes list ptr by node desc
    MapPortKeyToAggNodeFabricPtr m_agg_node_by_port_key_;   // AggNode ptr node by node key

    FabricTopologyData m_topology_data_;

    MapStrToHostInfo m_hosts_;
    VecAggTreePtr m_agg_trees_;   // Vector of all the trees, computed at start time
    // Maps between job and id a map of tree ids to agg trees. computed at each begin job. discarded at job end
    MapExtJobIdToMapTreeIdToTree m_job_agg_trees_;
    VecAggNodeFabricPtr m_agg_nodes_;
    VecAggPathPtr m_agg_paths_;

    ////Optimization
    MapHcaPortKeyToPortKey m_compute_to_sw_port_;
    // set of closest AggNode to compute port
    // we might have several AggNodes to compute ports if not directly connected
    MapHcaPortKeyToAggNodeFabrics m_compute_port_to_agg_nodes_;
    MapPortKeyToAggNodeFabricPtr m_sw_to_agg_node_;

    // set of all computes PortData that are connected to ANs not spans by trees
    SetPortDataConstPtr m_compute_not_on_tree_;
    MapPortKeyToHCAPortKeys m_sw_with_no_an_to_compute_ports_;

    ////collection for the current epoch
    MapPortKeyToHCAPortKeys m_sw_to_update_compute_ports_;

    //  accumulates updates until the next build_trees is executed
    MapHcaPortKeyToAggNodeFabrics m_update_compute_to_agg_nodes_;

    uint64_t m_epoch_;   // Current update index
    bool m_trees_built_;
    uint16_t m_llt_trees_to_build_;
    uint16_t m_max_tree_id_;
    uint16_t m_max_llt_tree_id_;
    sharp_trees_t m_min_mc_tree_id_;   // first multicast tree id
    sharp_trees_t m_max_mc_tree_id_;   // last multicast tree id
    DynamicTreeAlgoEnum m_dynamic_tree_algo;
    port_key_t m_sm_port_guid_;
    uint64_t m_sa_key_;

    FabricProvider* m_fabric_provider_;
    TreeManager m_tree_manager_;
    CommandManager* m_command_manager_;

    AggTreesIter m_llt_end_itr_;
    uint8_t m_control_path_version_;
    bool m_agg_nodes_file_update_req_;
    bool m_is_seamless_restart_in_progress_;
    MapPortKeyToAggNodeInfoRecord m_agg_node_info_records_by_port_key_;
    VecAggPathRecord m_agg_path_records_with_action_;
    MapFileNameToCRC m_file_crc_by_filename_;
    bool m_rebuild_trees_required_;
    MapTreeHashToDumpMessage m_trees_history_by_hash_;
    std::unique_ptr<file_utils::DumpFile> m_trees_history_file_ptr_;
    bool m_initialized_;
    Metrics m_metrics_{Metrics::Config{}, ""};   // will be initialise in Init()

    // Information used during restart, for seamless restart
    RestartInfo m_restart_info_;

    // All gather multicat tree mlid managment
    lid_t m_min_mlid_;            // minimal multicast lid
    lid_t m_max_mlid_;            // maximal multicast lid
    SetLids m_available_mlids_;   // Set of available mlids

    MapAggNodeKeyToMlidList m_required_mc_query_;   // multicast table information required by seamless restart

    void CheckUpdateTreesPerPortSet(const SetPortDataConstPtr& compute_ports);
    int CreateDumpDirectory(const string& dir_name) const;

   public:
    explicit Fabric()
        : m_disabled_ports_msg_(4 * 1024),
          m_topology_data_(TOPOLOGY_TYPE_NONE),
          // m_topology_type_(TOPOLOGY_TYPE_NONE),
          // m_max_sw_rank_(0),
          // m_dimensions_(0),
          m_epoch_(0),
          m_trees_built_(false),
          m_llt_trees_to_build_(0),
          m_max_tree_id_(0),
          m_max_llt_tree_id_(0),
          m_min_mc_tree_id_(0),
          m_max_mc_tree_id_(0),
          m_dynamic_tree_algo(DynamicTreeAlgoEnum::SUPER_POD_ORIENTED_ALGORITHM),
          m_sm_port_guid_(),
          m_sa_key_(0x1),
          m_fabric_provider_(NULL),
          m_tree_manager_(),
          m_command_manager_(NULL),
          m_control_path_version_(0),
          m_agg_nodes_file_update_req_(false),
          m_is_seamless_restart_in_progress_(false),
          m_rebuild_trees_required_(false),
          m_initialized_(false),
          m_min_mlid_(MIN_SHARP_MLID_START),
          m_max_mlid_(MAX_SHARP_MLID_END)
    {}

    ~Fabric();

    int Init(SeamlessRestartStatus& seamless_restart, TelemetryConfig cfg, std::string host_name);

    void ConfigureLocalPort(port_key_t local_port);
    void CleanFabricProvider();
    void Finish();   // explicit destruction

    uint16_t GetMaxTreeId() { return m_max_tree_id_; }
    uint16_t GetMaxLltTreeId() { return m_max_llt_tree_id_; }
    sharp_trees_t GetMinMulticastTreeId() const { return m_min_mc_tree_id_; }
    sharp_trees_t GetMaxMulticastTreeId() const { return m_max_mc_tree_id_; }

    lid_t GetMinMlid() const { return m_min_mlid_; }
    lid_t GetMaxMlid() const { return m_max_mlid_; }

    DynamicTreeAlgoEnum GetDynamicTreeAlgo() const { return m_dynamic_tree_algo; }
    void SetDynamicTreeAlgo(DynamicTreeAlgoEnum algo) { m_dynamic_tree_algo = algo; }

    void FabricDumpTables();
    void SetCommandManager(CommandManager* p_command_manager);

    const MapStrListAggNodeFabricPtr& GetAggNodesByDesc() const { return m_agg_node_by_desc_; }
    const VecAggPathPtr& GetAggPaths() const { return m_agg_paths_; }

    int BuildTreesSeamlessRestart();
    int RestoreFromNetworkExistingTreeNodes(const SetTreeIds& tree_ids);
    int RestoreFromNetworkAllQpsOnAllAggNodes();
    int RestoreFromNetworkTreeNodesConfigurations(const SetTreeIds& tree_ids);
    int AssignRestoredQPs();
    int RestoreAggPaths();
    bool IsSeamlessRestartInProgress() { return m_is_seamless_restart_in_progress_; }
    void BuildTrees();
    void RebuildSkeletonTrees();
    uint16_t GetLltTreesCount() { return m_llt_trees_to_build_; }

    uint16_t GetTotalTreesNum() const { return m_agg_trees_.size(); }

    uint16_t GetAggNodesNum() const { return m_agg_node_by_port_key_.size(); }
    const MapPortKeyToAggNodeFabricPtr& GetAggNodes() const { return m_agg_node_by_port_key_; }

    sharp_trees_t GetSatTreeId(sharp_trees_t llt_tree_id);
    sharp_trees_t GetLltTreeId(sharp_trees_t sat_tree_id);

    bool IsSatTreeId(sharp_trees_t tree_id) const;
    bool IsMulticastTreeId(sharp_trees_t tree_id) const;

    FabricProvider* GetFabricProvider() const { return m_fabric_provider_; }

    TopologyType GetTopologyType() const { return m_topology_data_.GetTopologyInfo().m_topology_type; }

    FabricTopologyData& GetTopologyData() { return m_topology_data_; }

    VecListAggNodePtr& GetAggNodeBySwRank() { return m_topology_data_.m_agg_node_by_sw_rank_; }

    const MapPortKeyToAggNodeFabricPtr& GetPeerKeyToAggNodePtr() const { return m_sw_to_agg_node_; }

    inline uint8_t GetMaxSwRank() const { return m_topology_data_.GetTopologyInfo().m_info.m_max_sw_rank; }

    inline uint8_t GetMaxSwRankIfAvailable() const
    {
        if (GetTopologyType() != TOPOLOGY_TYPE_TREE) {
            return INVALID_TREE_RANK;
        }
        return m_topology_data_.GetTopologyInfo().m_info.m_max_sw_rank;
    }

    // Return number of dimension on Hyper-Cube
    uint8_t GetNumDimensions() const { return m_topology_data_.GetTopologyInfo().m_info.m_dimensions; }

    // Trees
    int GetAvailableLltTreeId(int start_index = 0) const { return GetAvailableTreeId(start_index, m_llt_trees_to_build_ - 1); }

    AggTree* CreateTree(sharp_trees_t tree_id);
    AggTree* GetTree(sharp_trees_t tree_id) const;
    AggTreesIter GetTreesIter() { return m_agg_trees_.begin(); }
    bool IsTreesEnd(AggTreesIter& iter) { return (iter == m_agg_trees_.end()); }
    bool IsLltTreesEnd(AggTreesIter& iter) { return (iter == m_llt_end_itr_); }
    AggTreesConstIter GetTreesIter() const { return m_agg_trees_.begin(); }
    bool IsTreesEnd(AggTreesConstIter& iter) const { return (iter == m_agg_trees_.end()); }
    bool IsLltTreesEnd(AggTreesConstIter& iter) const { return (iter == m_llt_end_itr_); }

    void DeleteTree(sharp_trees_t tree_id);
    void DeleteAllTrees();

    AggTree& CreateJobTree(const SharpExtJobId& external_job_id, sharp_trees_t tree_id, uint8_t tree_plane);
    AggTree& CreateJobMulticastTree(const SharpExtJobId& external_job_id, uint8_t tree_plane, SharableMlidPtr& sharable_mlid);

    bool DeleteJobTrees(const SharpExtJobId& external_job_id, bool force = false, bool clean_flow = false);
    // delete all job trees in a force=true manner
    void DeleteAllJobTrees();
    const MapTreeIdToAggTree* GetJobTrees(const SharpExtJobId& external_job_id);
    AggTree* GetJobTree(const SharpExtJobId& external_job_id, sharp_trees_t tree_id);
    void GetJobTreeIds(const SharpExtJobId& external_job_id, SetTreeIds& set_tree_id);

    void GetAggNodesResourceData(sharp_resource_agg_node*& agg_node_recources, uint32_t& agg_node_num);
    void FillANSwitchPortsResourceData(sharp_resource_agg_node*& agg_node_recources, uint32_t& agg_node_num);
    void FillANStatusDetails(sharp_resource_agg_node& smx_agg_node, const AggNodeFabricInfo& fabric_agg_node);
    void GetAggTreesResourceData(sharp_resource_agg_tree*& tree_recources, uint32_t& agg_trees_num);
    void GetAggPathResourceData(sharp_resource_link*& link_recources,
                                uint32_t& link_recources_links_num,
                                sharp_resource_link* links = NULL,
                                uint32_t links_num = 0);
    uint16_t GetAggPathNum() const;

    int SetTreePriority(sharp_resource_priority priority, sharp_trees_t tree_id);

    // UpdateFabric
    void DeleteOldPorts();
    void HandlePortsUpdates(const FabricTopologyInfo& topology_info, ListPortUpdateInfo& updates, uint64_t epoch);
    void LogPortsUpdates(uint32_t new_cn_ports_counter,
                         uint32_t new_an_ports_counter,
                         uint32_t new_vports_counter,
                         uint32_t active_vports_counter);
    void UpdatesPathsState(ListPathUpdate& updates, uint64_t epoch);
    void HandlePathsUpdates(ListPathUpdate& updates, uint64_t epoch);
    void HandleAggNodePortUpdateFabricDB(ListPortUpdateInfo& updates);

    void CheckUpdateTrees(const SharpJob* p_job);

    int FabricUpdateEnd();
    void StartFabric(uint32_t agg_nodes_number,
                     uint32_t paths_number,
                     sharp_job_id_t max_jobs_number,
                     uint8_t control_path_version,
                     uint16_t tree_table_size,
                     lid_t sharp_mlid_start);

    void SetSaKey(uint64_t sa_key) { m_sa_key_ = sa_key; };

    // get node by desc or node key
    AggNodeFabricInfo* GetAggNode(const string& node_desc, port_key_t port_key);
    AggNodeFabricInfo* GetAggNode(port_key_t port_key);
    AggNodeFabricInfo* GetAggNode(const string& node_desc);
    AggNodeFabricInfo* GetAggNode(lid_t lid);

    AggNodeFabricInfo* GetAggNodeById(sharp_an_id_t an_id) const;
    void InsertAggNodeById(AggNodeFabricInfo* p_agg_node);

    AggPath* GetAggPathById(sharp_path_id_t path_id) const;
    void InsertAggPathById(AggPath* p_agg_path);

    // Aggregation nodes iterator
    AggNodeFabricIterator GetAggNodeIteratorBegin() { return AggNodeFabricIterator(m_agg_node_by_port_key_.begin()); }

    AggNodeFabricIterator GetAggNodeIteratorEnd() { return AggNodeFabricIterator(m_agg_node_by_port_key_.end()); }

    const HostInfo* GetHostInfo(const string& host_name) const;
    uint8_t GetNumHops(AggNodeFabricInfo* p_agg_node1, AggNodeFabricInfo* p_agg_node2);

    const PortData* FindConstHcaPortData(hca_port_key_t port_key) const;
    const PortData* FindConstAnPortData(port_key_t port_key) const;

    uint8_t GetControlPathVersion() const { return m_control_path_version_; }

    int GetMinimalShortestPathGraph(uint64_t* guids,
                                    uint32_t num_guids,
                                    std::map<uint64_t, std::set<uint64_t>>& sub_graph,
                                    std::map<uint64_t, std::list<uint64_t>>& leaf_to_hosts,
                                    uint8_t plane);

    void SubDivide(uint64_t root,
                   uint64_t src,
                   std::list<uint64_t>& destinations,
                   std::map<uint64_t, std::set<uint64_t>>& known_dst_map,
                   std::map<uint64_t, std::set<uint64_t>>& sub_graph,
                   uint8_t plane);

    void UpdateAggNodeInfo(AggNodeFabricInfo* p_agg_node, const AggNodeInfo& agg_node_info, const PortInfo& port_info);

    SeamlessRestartStatus ValidateAggNodesForSeamlessRestart();
    SeamlessRestartStatus IsSeamlessRestartCompatibleWithPreviousRun();

    void TryRecoverTrees();
    void UpdateAggNodeStateFromDump();
    int ValidateRequiredFWCapabilitiesForSeamlessRestart();
    int UpdateAggPathActionFromDump();
    void ReleaseQpsLeftFromSeamlessRestart();
    void CleanDataRecoveredDuringSeamlessRestart();
    void MarkInvalidSwitchesForRediscovery();

    // This method returns the leaf switch that is directly connected to 'port_key, NULL if not found
    AggNodeFabricInfo* GetSwitchThatIsConnectedToHost(hca_port_key_t port_guid);

    bool IsReconstructMulticastTrees() { return !m_required_mc_query_.empty(); }
    void SetRequiredMulticastTableQuery(persistent_job_info* job_info);
    int ReadRequiredMulticastTables();
    bool AllocateMlid(lid_t mlid);   // Allocate specific mlid, used by seamless restart
    lid_t GetNextMlid();             // Get next available Mlid without allocating it

    AMGeneralInfoRecord& GetAMGeneralInfoRecord() { return m_restart_info_.m_general_info_record_; }
    uint64_t GetEpoch() { return m_epoch_; }
    void AddFabricGraphUpdate(FabricGraphPortDataUpdate& port_update);

    Metrics& GetMetrics() { return m_metrics_; }
    const Metrics& GetMetrics() const { return m_metrics_; }

   private:
    // find existing PortData
    // Returns NULL if not found
    PortData* FindHcaPortData(hca_port_key_t port_key, const bool should_ignore_invalid_ports = true);
    PortData* FindAnPortData(port_key_t port_key, const bool should_ignore_invalid_ports = true);
    PortData* CreateHcaPortData(const PortInfo& port_info);
    PortData* CreateAnPortData(const PortInfo& port_info);
    void CreateVportData(const PortInfo& vport_info, const std::string& host_name, const PortInfo& phys_port_info);

    void CreatePath(AggNodeFabricInfo* p_down_node,
                    AggNodeFabricInfo* p_up_node,
                    sharp_path_id_t path_id,
                    uint8_t plane_number,
                    uint8_t sw_hops,
                    uint64_t epoch,
                    uint8_t num_semaphores,
                    uint16_t agg_rate,
                    uint32_t sat_qps,
                    uint32_t llt_qps,
                    const AnToAnInfo& p_an_to_an_info);

    // Allocate and configure multicast QPs for AggNodes
    void ConfigureMulticast();

    int DiscoverAggNode(PortData* p_port_data);
    int AddComputePort(PortData* p_port_data, const string& host_name);
    void AddAggNode(PortData* p_port_data,
                    const AggNodeInfo& agg_node_info,
                    sharp_an_id_t an_id,
                    const AnMinHopsTableByPlane& an_min_hops_table,
                    uint64_t am_key,
                    const string& switch_desc,
                    uint16_t mlid_start,
                    uint16_t mlid_end);
    int AddHostInfoToVport(PortData* p_vport_data, const string& host_name);

    int UpdateCaPort(PortInfo& port_info);

    void DeleteAllPortData();
    void DeleteAllHosts();

    void HandlemComutesOnSwitchWithNewAggNode(port_key_t sw_key);
    void EraseSwMultimapElement(MapPortKeyToHCAPortKeys& sw_multimap, port_key_t sw_key, hca_port_key_t element);

    // Associate compute ports to aggregation nodes
    // This method needs to be invoked before invoking UpdateComputeToTreeNode
    int UpdateComputeToAggNode();
    void UpdateComputeSwDb(const PortData* p_port_data, bool is_new);

    // Associate compute ports to tree nodes
    // UpdateComputeToAggNode needs to be invoked before this method
    int UpdateComputeToTreeNode(bool use_all_computes = false);
    // clear mapping between compute to tree nodes on all valid trees
    void ClearComputeToTreesNode(hca_port_key_t compute_port_key);

    // old virtual port is a port that was deleted from m_port_data_by_key_ because fabric received INACTIVE update for it
    void ActivateVport(const PortUpdateInfo& port_update_info);
    void HandleVportUpdate(const PortUpdateInfo& port_update_info, PortData* p_port_data);

    void HandlePortUpdate(const PortUpdateInfo& port_update_info);

    bool IsTreeUpdateSupported()
    {
        if (GetTopologyType() == TOPOLOGY_TYPE_TREE || GetTopologyType() == TOPOLOGY_TYPE_DFP) {
            return true;
        }
        return false;
    }
    int ConfigureTreesOnFabric();
    int GetAvailableTreeId(int start_index, int max_tree_id) const;

    void DumpTrees() const;
    void DumpTreesState() const;
    void CreateTreesHistoricData();
    void ExcludeExistingTreesFromHistoricData();
    void DumpTreesStructureHistory();   // dumps historic trees structure that are no longer exist
    int DumpFabric() const;
    void DumpFabric(FILE* f) const;
    void SetHostInfo(PortData* p_port_data, const string& host_name);

    template <typename PortType, typename... AdditionalPortDataParameters>
    PortData* CreatePortDataImpl(char const* const port_type_str,
                                 const PortInfo& port_info,
                                 const AdditionalPortDataParameters&... port_data_parameters)
    {
        try {
            std::unique_ptr<PortData> p_port_data = std::unique_ptr<PortData>(new PortType(port_info, port_data_parameters...));
            auto insert_pair = m_hca_port_data_by_key_.insert(
                make_pair<hca_port_key_t, std::unique_ptr<PortData>>(hca_port_key_t{port_info.m_port_key, port_info.m_first_plane},
                                                                     nullptr));
            if (!insert_pair.second) {   // the key already exists
                INFO("Replacing existing %s in m_port_data_by_key_ with key: %s", port_type_str, port_info.ToString().c_str());
                m_old_ports_.push_back(std::move(insert_pair.first->second));
                m_old_ports_.back()->Disable();
            }
            // replace nullptr or the old port in the map to the new port
            insert_pair.first->second = std::move(p_port_data);
            return insert_pair.first->second.get();
        } catch (const std::bad_alloc&) {
            ERROR("Failed to create %s for %s\n", port_type_str, port_info.GetName().c_str());
            throw;
        }
    }

    lid_t AllocateMlid();
    void FreeMlid(lid_t mlid);

    friend SharableMlid;   // This enables SharableMlid to call private FreeMlid method
};

// Recognize the global variables
extern Fabric g_fabric;

bool HostInfoSort::operator()(const HostInfo* p_lhs, const HostInfo* p_rhs) const
{
    return (p_lhs->GetSharpdId() < p_rhs->GetSharpdId());
}

#endif   // AGG_FABRIC_H_
