diff --git a/lib/inc/ClientSai.h b/lib/inc/ClientSai.h new file mode 100644 index 000000000000..5628093b2b98 --- /dev/null +++ b/lib/inc/ClientSai.h @@ -0,0 +1,394 @@ +#pragma once + +#include "SaiInterface.h" +#include "Channel.h" +#include "SwitchContainer.h" +#include "Notification.h" + +#include "swss/table.h" + +#include +#include +#include +#include + +#define SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(ot) \ + virtual sai_status_t remove( \ + _In_ const sai_ ## ot ## _t* ot) override; + +#define SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(ot) \ + virtual sai_status_t create( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _In_ const sai_attribute_t *attr_list) override; + +#define SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(ot) \ + virtual sai_status_t set( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ const sai_attribute_t *attr) override; + +#define SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(ot) \ + virtual sai_status_t get( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _Out_ sai_attribute_t *attr_list) override; + +#define SAIREDIS_CLIENTSAI_DECLARE_BULK_CREATE_ENTRY(ot) \ + virtual sai_status_t bulkCreate( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ const uint32_t *attr_count, \ + _In_ const sai_attribute_t **attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +#define SAIREDIS_CLIENTSAI_DECLARE_BULK_REMOVE_ENTRY(ot) \ + virtual sai_status_t bulkRemove( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +#define SAIREDIS_CLIENTSAI_DECLARE_BULK_SET_ENTRY(ot) \ + virtual sai_status_t bulkSet( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ const sai_attribute_t *attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +namespace sairedis +{ + class ClientSai: + public sairedis::SaiInterface + { + public: + + ClientSai(); + + virtual ~ClientSai(); + + public: + + sai_status_t initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) override; + + sai_status_t uninitialize(void) override; + + public: // SAI interface overrides + + virtual sai_status_t create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) override; + + virtual sai_status_t set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) override; + + virtual sai_status_t get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) override; + + public: // create ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(route_entry); + SAIREDIS_CLIENTSAI_DECLARE_CREATE_ENTRY(nat_entry); + + public: // remove ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(route_entry); + SAIREDIS_CLIENTSAI_DECLARE_REMOVE_ENTRY(nat_entry); + + public: // set ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(route_entry); + SAIREDIS_CLIENTSAI_DECLARE_SET_ENTRY(nat_entry); + + public: // get ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(route_entry); + SAIREDIS_CLIENTSAI_DECLARE_GET_ENTRY(nat_entry); + + public: // bulk QUAD oid + + virtual sai_status_t bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + public: // bulk create ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_BULK_CREATE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_CREATE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_CREATE_ENTRY(nat_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_CREATE_ENTRY(route_entry); + + public: // bulk remove ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_BULK_REMOVE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_REMOVE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_REMOVE_ENTRY(nat_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_REMOVE_ENTRY(route_entry); + + public: // bulk set ENTRY + + SAIREDIS_CLIENTSAI_DECLARE_BULK_SET_ENTRY(fdb_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_SET_ENTRY(inseg_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_SET_ENTRY(nat_entry); + SAIREDIS_CLIENTSAI_DECLARE_BULK_SET_ENTRY(route_entry); + + public: // stats API + + virtual sai_status_t getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) override; + + virtual sai_status_t getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) override; + + virtual sai_status_t clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) override; + + public: // non QUAD API + + virtual sai_status_t flushFdbEntries( + _In_ sai_object_id_t switchId, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList) override; + + public: // SAI API + + virtual sai_status_t objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) override; + + virtual sai_status_t queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) override; + + virtual sai_status_t queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) override; + + virtual sai_object_type_t objectTypeQuery( + _In_ sai_object_id_t objectId) override; + + virtual sai_object_id_t switchIdQuery( + _In_ sai_object_id_t objectId) override; + + virtual sai_status_t logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) override; + + private: // QUAD API helpers + + sai_status_t create( + _In_ sai_object_type_t objectType, + _In_ const std::string& serializedObjectId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t remove( + _In_ sai_object_type_t objectType, + _In_ const std::string& serializedObjectId); + + sai_status_t set( + _In_ sai_object_type_t objectType, + _In_ const std::string& serializedObjectId, + _In_ const sai_attribute_t *attr); + + sai_status_t get( + _In_ sai_object_type_t objectType, + _In_ const std::string& serializedObjectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + + private: // bulk QUAD API helpers + + sai_status_t bulkCreate( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Inout_ sai_status_t *object_statuses); + + sai_status_t bulkRemove( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses); + + sai_status_t bulkSet( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses); + + private: // QUAD API response + + /** + * @brief Wait for response. + * + * Will wait for response from syncd. Method used only for single + * object create/remove/set since they have common output which is + * sai_status_t. + */ + sai_status_t waitForResponse( + _In_ sai_common_api_t api); + + /** + * @brief Wait for GET response. + * + * Will wait for response from syncd. Method only used for single + * GET object. If status is SUCCESS all values will be deserialized + * and transferred to user buffers. If status is BUFFER_OVERFLOW + * then all non list values will be transferred, but LIST objects + * will only transfer COUNT item of list, without touching user + * list at all. + */ + sai_status_t waitForGetResponse( + _In_ sai_object_type_t objectType, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + + private: // bulk QUAD API response + + /** + * @brief Wait for bulk response. + * + * Will wait for response from syncd. Method used only for bulk + * object create/remove/set since they have common output which is + * sai_status_t and object_statuses. + */ + sai_status_t waitForBulkResponse( + _In_ sai_common_api_t api, + _In_ uint32_t object_count, + _Out_ sai_status_t *object_statuses); + + private: // stats API response + + sai_status_t waitForGetStatsResponse( + _In_ uint32_t number_of_counters, + _Out_ uint64_t *counters); + + sai_status_t waitForClearStatsResponse(); + + private: // non QUAD API response + + sai_status_t waitForFlushFdbEntriesResponse(); + + private: // SAI API response + + sai_status_t waitForQueryAttributeCapabilityResponse( + _Out_ sai_attr_capability_t* capability); + + sai_status_t waitForQueryAattributeEnumValuesCapabilityResponse( + _Inout_ sai_s32_list_t* enumValuesCapability); + + sai_status_t waitForObjectTypeGetAvailabilityResponse( + _In_ uint64_t *count); + + private: + + void handleNotification( + _In_ const std::string &name, + _In_ const std::string &serializedNotification, + _In_ const std::vector &values); + + sai_switch_notifications_t syncProcessNotification( + _In_ std::shared_ptr notification); + + private: + + bool m_apiInitialized; + + std::recursive_mutex m_apimutex; + + sai_service_method_table_t m_service_method_table; + + std::shared_ptr m_communicationChannel; + + std::shared_ptr m_switchContainer; + + std::shared_ptr m_sai; + + std::function)> m_notificationCallback; + + std::vector m_lastCreateOids; + }; +} diff --git a/lib/inc/ClientServerSai.h b/lib/inc/ClientServerSai.h new file mode 100644 index 000000000000..99a00a175dbe --- /dev/null +++ b/lib/inc/ClientServerSai.h @@ -0,0 +1,259 @@ +#pragma once + +#include "SaiInterface.h" + +#include +#include + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(ot) \ + virtual sai_status_t remove( \ + _In_ const sai_ ## ot ## _t* ot) override; + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(ot) \ + virtual sai_status_t create( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _In_ const sai_attribute_t *attr_list) override; + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(ot) \ + virtual sai_status_t set( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ const sai_attribute_t *attr) override; + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(ot) \ + virtual sai_status_t get( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _Out_ sai_attribute_t *attr_list) override; + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_CREATE_ENTRY(ot) \ + virtual sai_status_t bulkCreate( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ const uint32_t *attr_count, \ + _In_ const sai_attribute_t **attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_REMOVE_ENTRY(ot) \ + virtual sai_status_t bulkRemove( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +#define SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_SET_ENTRY(ot) \ + virtual sai_status_t bulkSet( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ const sai_attribute_t *attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +namespace sairedis +{ + class ClientServerSai: + public sairedis::SaiInterface + { + public: + + ClientServerSai(); + + virtual ~ClientServerSai(); + + public: + + sai_status_t initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) override; + + sai_status_t uninitialize(void) override; + + public: // SAI interface overrides + + virtual sai_status_t create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) override; + + virtual sai_status_t set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) override; + + virtual sai_status_t get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) override; + + public: // create ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(route_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_CREATE_ENTRY(nat_entry); + + public: // remove ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(route_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_REMOVE_ENTRY(nat_entry); + + public: // set ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(route_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_SET_ENTRY(nat_entry); + + public: // get ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(ipmc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(l2mc_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(mcast_fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(neighbor_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(route_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_GET_ENTRY(nat_entry); + + public: // bulk QUAD oid + + virtual sai_status_t bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + public: // bulk create ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_CREATE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_CREATE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_CREATE_ENTRY(nat_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_CREATE_ENTRY(route_entry); + + public: // bulk remove ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_REMOVE_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_REMOVE_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_REMOVE_ENTRY(nat_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_REMOVE_ENTRY(route_entry); + + public: // bulk set ENTRY + + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_SET_ENTRY(fdb_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_SET_ENTRY(inseg_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_SET_ENTRY(nat_entry); + SAIREDIS_CLIENTSERVERSAI_DECLARE_BULK_SET_ENTRY(route_entry); + + public: // stats API + + virtual sai_status_t getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) override; + + virtual sai_status_t getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) override; + + virtual sai_status_t clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) override; + + public: // non QUAD API + + virtual sai_status_t flushFdbEntries( + _In_ sai_object_id_t switchId, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList) override; + + public: // SAI API + + virtual sai_status_t objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) override; + + virtual sai_status_t queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) override; + + virtual sai_status_t queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) override; + + virtual sai_object_type_t objectTypeQuery( + _In_ sai_object_id_t objectId) override; + + virtual sai_object_id_t switchIdQuery( + _In_ sai_object_id_t objectId) override; + + virtual sai_status_t logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) override; + + private: + + bool m_apiInitialized; + + std::recursive_mutex m_apimutex; + + sai_service_method_table_t m_service_method_table; + + std::shared_ptr m_sai; + }; +} diff --git a/lib/inc/RedisRemoteSaiInterface.h b/lib/inc/RedisRemoteSaiInterface.h index fbf198932fc6..200619867556 100644 --- a/lib/inc/RedisRemoteSaiInterface.h +++ b/lib/inc/RedisRemoteSaiInterface.h @@ -432,12 +432,14 @@ namespace sairedis sai_switch_notifications_t processNotification( _In_ std::shared_ptr notification); - std::string getHardwareInfo( - _In_ uint32_t attrCount, - _In_ const sai_attribute_t *attrList) const; - void refreshTableDump(); + public: + + static std::string getHardwareInfo( + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList); + private: std::shared_ptr m_contextConfig; diff --git a/lib/inc/Sai.h b/lib/inc/Sai.h index 4dd3944eb4e6..d71e5e7b70d8 100644 --- a/lib/inc/Sai.h +++ b/lib/inc/Sai.h @@ -1,6 +1,5 @@ #pragma once -#include "RedisRemoteSaiInterface.h" #include "Notification.h" #include "Recorder.h" #include "Context.h" diff --git a/lib/inc/ServerSai.h b/lib/inc/ServerSai.h new file mode 100644 index 000000000000..77ab66348ca7 --- /dev/null +++ b/lib/inc/ServerSai.h @@ -0,0 +1,312 @@ +#pragma once + +#include "SaiInterface.h" + +#include "syncd/SelectableChannel.h" + +#include "swss/selectableevent.h" + +#include +#include +#include + +#define SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(ot) \ + virtual sai_status_t remove( \ + _In_ const sai_ ## ot ## _t* ot) override; + +#define SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(ot) \ + virtual sai_status_t create( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _In_ const sai_attribute_t *attr_list) override; + +#define SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(ot) \ + virtual sai_status_t set( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ const sai_attribute_t *attr) override; + +#define SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(ot) \ + virtual sai_status_t get( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _Out_ sai_attribute_t *attr_list) override; + +#define SAIREDIS_SERVERSAI_DECLARE_BULK_CREATE_ENTRY(ot) \ + virtual sai_status_t bulkCreate( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ const uint32_t *attr_count, \ + _In_ const sai_attribute_t **attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +#define SAIREDIS_SERVERSAI_DECLARE_BULK_REMOVE_ENTRY(ot) \ + virtual sai_status_t bulkRemove( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +#define SAIREDIS_SERVERSAI_DECLARE_BULK_SET_ENTRY(ot) \ + virtual sai_status_t bulkSet( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *ot, \ + _In_ const sai_attribute_t *attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) override; + +namespace sairedis +{ + class ServerSai: + public sairedis::SaiInterface + { + public: + + ServerSai(); + + virtual ~ServerSai(); + + public: + + sai_status_t initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) override; + + sai_status_t uninitialize(void) override; + + public: // SAI interface overrides + + virtual sai_status_t create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) override; + + virtual sai_status_t set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) override; + + virtual sai_status_t get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) override; + + public: // create ENTRY + + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(ipmc_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(l2mc_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(mcast_fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(neighbor_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(route_entry); + SAIREDIS_SERVERSAI_DECLARE_CREATE_ENTRY(nat_entry); + + public: // remove ENTRY + + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(ipmc_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(l2mc_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(mcast_fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(neighbor_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(route_entry); + SAIREDIS_SERVERSAI_DECLARE_REMOVE_ENTRY(nat_entry); + + public: // set ENTRY + + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(ipmc_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(l2mc_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(mcast_fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(neighbor_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(route_entry); + SAIREDIS_SERVERSAI_DECLARE_SET_ENTRY(nat_entry); + + public: // get ENTRY + + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(ipmc_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(l2mc_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(mcast_fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(neighbor_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(route_entry); + SAIREDIS_SERVERSAI_DECLARE_GET_ENTRY(nat_entry); + + public: // bulk QUAD oid + + virtual sai_status_t bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + public: // bulk create ENTRY + + SAIREDIS_SERVERSAI_DECLARE_BULK_CREATE_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_CREATE_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_CREATE_ENTRY(nat_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_CREATE_ENTRY(route_entry); + + public: // bulk remove ENTRY + + SAIREDIS_SERVERSAI_DECLARE_BULK_REMOVE_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_REMOVE_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_REMOVE_ENTRY(nat_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_REMOVE_ENTRY(route_entry); + + public: // bulk set ENTRY + + SAIREDIS_SERVERSAI_DECLARE_BULK_SET_ENTRY(fdb_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_SET_ENTRY(inseg_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_SET_ENTRY(nat_entry); + SAIREDIS_SERVERSAI_DECLARE_BULK_SET_ENTRY(route_entry); + + public: // stats API + + virtual sai_status_t getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) override; + + virtual sai_status_t getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) override; + + virtual sai_status_t clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) override; + + public: // non QUAD API + + virtual sai_status_t flushFdbEntries( + _In_ sai_object_id_t switchId, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList) override; + + public: // SAI API + + virtual sai_status_t objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) override; + + virtual sai_status_t queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) override; + + virtual sai_status_t queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) override; + + virtual sai_object_type_t objectTypeQuery( + _In_ sai_object_id_t objectId) override; + + virtual sai_object_id_t switchIdQuery( + _In_ sai_object_id_t objectId) override; + + virtual sai_status_t logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) override; + + private: + + void serverThreadFunction(); + + void processEvent( + _In_ syncd::SelectableChannel& consumer); + + sai_status_t processSingleEvent( + _In_ const swss::KeyOpFieldsValuesTuple &kco); + + sai_status_t processQuadEvent( + _In_ sai_common_api_t api, + _In_ const swss::KeyOpFieldsValuesTuple &kco); + + sai_status_t processEntry( + _In_ sai_object_meta_key_t metaKey, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list); + + sai_status_t processOid( + _In_ sai_object_type_t objectType, + _Inout_ sai_object_id_t& oid, + _In_ sai_object_id_t switchId, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list); + + void sendApiResponse( + _In_ sai_common_api_t api, + _In_ sai_status_t status, + _In_ sai_object_id_t oid); + + void sendGetResponse( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ sai_status_t status, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list); + + private: + + bool m_apiInitialized; + + bool m_runServerThread; + + std::recursive_mutex m_apimutex; + + sai_service_method_table_t m_service_method_table; + + std::shared_ptr m_sai; + + std::shared_ptr m_serverThread; + + std::shared_ptr m_selectableChannel; + + swss::SelectableEvent m_serverThreadThreadShouldEndEvent; + }; +} diff --git a/lib/inc/sai_redis.h b/lib/inc/sai_redis.h index abeb9c6aeaa8..e7bc91b457fc 100644 --- a/lib/inc/sai_redis.h +++ b/lib/inc/sai_redis.h @@ -5,7 +5,9 @@ extern "C" { #include "saiextensions.h" } -#include "Sai.h" +#include "SaiInterface.h" + +#include "swss/logger.h" #include @@ -57,7 +59,7 @@ PRIVATE extern const sai_virtual_router_api_t redis_virtual_router_api; PRIVATE extern const sai_vlan_api_t redis_vlan_api; PRIVATE extern const sai_wred_api_t redis_wred_api; -PRIVATE extern std::shared_ptr redis_sai; +PRIVATE extern std::shared_ptr redis_sai; // QUAD OID diff --git a/lib/inc/sairedis.h b/lib/inc/sairedis.h index 24029fa7e4ae..036bded2c6d8 100644 --- a/lib/inc/sairedis.h +++ b/lib/inc/sairedis.h @@ -13,6 +13,17 @@ extern "C" { */ #define SAI_REDIS_KEY_CONTEXT_CONFIG "SAI_REDIS_CONTEXT_CONFIG" +/** + * @brief Redis enable client. + * + * Optional. By default sairedis act as sairedis server, which means it + * directly talks to syncd. If this key is set to "true", then this instance of + * sairedis will act as a client, and it will connect and talk to sairedis + * server instance only. There can be multiple clients active. Default value + * is "false". + */ +#define SAI_REDIS_KEY_ENABLE_CLIENT "SAI_REDIS_ENABLE_CLIENT" + /** * @brief Default synchronous operation response timeout in milliseconds. */ diff --git a/lib/src/ClientSai.cpp b/lib/src/ClientSai.cpp new file mode 100644 index 000000000000..02ca184e513b --- /dev/null +++ b/lib/src/ClientSai.cpp @@ -0,0 +1,1706 @@ +#include "ClientSai.h" +#include "SaiInternal.h" +#include "RedisRemoteSaiInterface.h" +#include "ZeroMQChannel.h" +#include "Utils.h" +#include "sairediscommon.h" +#include "PerformanceIntervalTimer.h" +#include "NotificationFactory.h" + +#include "swss/logger.h" + +#include "meta/sai_serialize.h" +#include "meta/SaiAttributeList.h" + +#include + +#define REDIS_CHECK_API_INITIALIZED() \ + if (!m_apiInitialized) { \ + SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__); \ + return SAI_STATUS_FAILURE; } + +using namespace sairedis; +using namespace sairediscommon; +using namespace saimeta; +using namespace std::placeholders; + +// TODO how to tell if current SAI is in init view or apply view ? + +std::string joinFieldValues( + _In_ const std::vector &values); + +std::vector serialize_counter_id_list( + _In_ const sai_enum_metadata_t *stats_enum, + _In_ uint32_t count, + _In_ const sai_stat_id_t *counter_id_list); + +ClientSai::ClientSai() +{ + SWSS_LOG_ENTER(); + + m_apiInitialized = false; +} + +ClientSai::~ClientSai() +{ + SWSS_LOG_ENTER(); + + if (m_apiInitialized) + { + uninitialize(); + } +} + +sai_status_t ClientSai::initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) +{ + MUTEX(); + SWSS_LOG_ENTER(); + + if (m_apiInitialized) + { + SWSS_LOG_ERROR("already initialized"); + + return SAI_STATUS_FAILURE; + } + + // TODO support context config + + m_switchContainer = std::make_shared(); + + // TODO from config/method table + m_communicationChannel = std::make_shared( + "ipc:///tmp/saiServer", + "ipc:///tmp/saiServerNtf", + std::bind(&ClientSai::handleNotification, this, _1, _2, _3)); + + m_apiInitialized = true; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t ClientSai::uninitialize(void) +{ + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + SWSS_LOG_NOTICE("begin"); + + m_communicationChannel = nullptr; + + m_apiInitialized = false; + + SWSS_LOG_NOTICE("end"); + + return SAI_STATUS_SUCCESS; +} + +// QUAD API + +sai_status_t ClientSai::create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // NOTE: in client mode, each create oid must be retrieved from server since + // server is actually creating new oid, and it's value must be transferred + // over communication channel + + *objectId = SAI_NULL_OBJECT_ID; + + if (objectType == SAI_OBJECT_TYPE_SWITCH && attr_count > 0 && attr_list) + { + auto initSwitchAttr = sai_metadata_get_attr_by_id(SAI_SWITCH_ATTR_INIT_SWITCH, attr_count, attr_list); + + if (initSwitchAttr && initSwitchAttr->value.booldata == false) + { + // TODO for connect, we may not need to actually call server, if we + // have hwinfo and context and context container information, we + // could allocate switch object ID locally + + auto hwinfo = RedisRemoteSaiInterface::getHardwareInfo(attr_count, attr_list); + + // TODO support context + SWSS_LOG_NOTICE("request to connect switch (context: 0) and hwinfo='%s'", hwinfo.c_str()); + + for (uint32_t i = 0; i < attr_count; ++i) + { + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_SWITCH, attr_list[i].id); + + if (meta == NULL) + { + SWSS_LOG_THROW("failed to find metadata for switch attribute %d", attr_list[i].id); + } + + if (meta->attrid == SAI_SWITCH_ATTR_INIT_SWITCH) + continue; + + if (meta->attrid == SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO) + continue; + + if (meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER) + { + SWSS_LOG_ERROR("notifications not supported yet, FIXME"); + + return SAI_STATUS_FAILURE; + } + + SWSS_LOG_ERROR("attribute %s not supported during INIT_SWITCH=false, expected HARDWARE_INFO or notification pointer", meta->attridname); + + return SAI_STATUS_FAILURE; + } + } + else + { + SWSS_LOG_ERROR("creating new switch not supported yet, use SAI_SWITCH_ATTR_INIT_SWITCH=false"); + + return SAI_STATUS_FAILURE; + } + } + + auto status = create( + objectType, + sai_serialize_object_id(switchId), // using switch ID instead of oid to transfer to server + attr_count, + attr_list); + + if (status == SAI_STATUS_SUCCESS) + { + // since user requested create, OID value was created remotely and it + // was returned in m_lastCreateOids + + *objectId = m_lastCreateOids.at(0); + } + + if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS) + { + /* + * When doing CREATE operation user may want to update notification + * pointers, since notifications can be defined per switch we need to + * update them. + */ + + SWSS_LOG_NOTICE("create switch OID = %s", sai_serialize_object_id(*objectId).c_str()); + + auto sw = std::make_shared(*objectId, attr_count, attr_list); + + m_switchContainer->insert(sw); + } + + return status; +} + +sai_status_t ClientSai::remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + auto status = remove( + objectType, + sai_serialize_object_id(objectId)); + + if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("removing switch id %s", sai_serialize_object_id(objectId).c_str()); + + // remove switch from container + m_switchContainer->removeSwitch(objectId); + } + + return status; +} + +sai_status_t ClientSai::set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr)) + { + SWSS_LOG_ERROR("sairedis extension attributes are not supported in CLIENT mode"); + + return SAI_STATUS_FAILURE; + } + + auto status = set( + objectType, + sai_serialize_object_id(objectId), + attr); + + if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS) + { + auto sw = m_switchContainer->getSwitch(objectId); + + if (!sw) + { + SWSS_LOG_THROW("failed to find switch %s in container", + sai_serialize_object_id(objectId).c_str()); + } + + /* + * When doing SET operation user may want to update notification + * pointers. + */ + + sw->updateNotifications(1, attr); + } + + return status; +} + +sai_status_t ClientSai::get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return get( + objectType, + sai_serialize_object_id(objectId), + attr_count, + attr_list); +} + +// QUAD ENTRY API + +#define DECLARE_CREATE_ENTRY(OT,ot) \ +sai_status_t ClientSai::create( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _In_ const sai_attribute_t *attr_list) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return create( \ + SAI_OBJECT_TYPE_ ## OT, \ + sai_serialize_ ## ot(*ot), \ + attr_count, \ + attr_list); \ +} + +DECLARE_CREATE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_CREATE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_CREATE_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_CREATE_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_CREATE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_CREATE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_CREATE_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_CREATE_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_REMOVE_ENTRY(OT,ot) \ +sai_status_t ClientSai::remove( \ + _In_ const sai_ ## ot ## _t* ot) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return remove( \ + SAI_OBJECT_TYPE_ ## OT, \ + sai_serialize_ ## ot(*ot)); \ +} + +DECLARE_REMOVE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_REMOVE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_REMOVE_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_REMOVE_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_REMOVE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_REMOVE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_REMOVE_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_REMOVE_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_SET_ENTRY(OT,ot) \ +sai_status_t ClientSai::set( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ const sai_attribute_t *attr) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return set( \ + SAI_OBJECT_TYPE_ ## OT, \ + sai_serialize_ ## ot(*ot), \ + attr); \ +} + +DECLARE_SET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_SET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_SET_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_SET_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_SET_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_SET_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_SET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_SET_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_GET_ENTRY(OT,ot) \ +sai_status_t ClientSai::get( \ + _In_ const sai_ ## ot ## _t* ot, \ + _In_ uint32_t attr_count, \ + _Inout_ sai_attribute_t *attr_list) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return get( \ + SAI_OBJECT_TYPE_ ## OT, \ + sai_serialize_ ## ot(*ot), \ + attr_count, \ + attr_list); \ +} + +DECLARE_GET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_GET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_GET_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_GET_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_GET_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_GET_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_GET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_GET_ENTRY(NAT_ENTRY,nat_entry); + +// QUAD API HELPERS + +sai_status_t ClientSai::create( + _In_ sai_object_type_t object_type, + _In_ const std::string& serializedObjectId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto entry = SaiAttributeList::serialize_attr_list( + object_type, + attr_count, + attr_list, + false); + + if (entry.empty()) + { + // make sure that we put object into db + // even if there are no attributes set + swss::FieldValueTuple null("NULL", "NULL"); + + entry.push_back(null); + } + + auto serializedObjectType = sai_serialize_object_type(object_type); + + const std::string key = serializedObjectType + ":" + serializedObjectId; + + SWSS_LOG_DEBUG("generic create key: %s, fields: %" PRIu64, key.c_str(), entry.size()); + + m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_CREATE); + + auto status = waitForResponse(SAI_COMMON_API_CREATE); + + return status; +} + +sai_status_t ClientSai::remove( + _In_ sai_object_type_t objectType, + _In_ const std::string& serializedObjectId) +{ + SWSS_LOG_ENTER(); + + auto serializedObjectType = sai_serialize_object_type(objectType); + + const std::string key = serializedObjectType + ":" + serializedObjectId; + + SWSS_LOG_DEBUG("generic remove key: %s", key.c_str()); + + m_communicationChannel->set(key, {}, REDIS_ASIC_STATE_COMMAND_REMOVE); + + auto status = waitForResponse(SAI_COMMON_API_REMOVE); + + return status; +} + +sai_status_t ClientSai::set( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + + auto entry = SaiAttributeList::serialize_attr_list( + objectType, + 1, + attr, + false); + + auto serializedObjectType = sai_serialize_object_type(objectType); + + std::string key = serializedObjectType + ":" + serializedObjectId; + + SWSS_LOG_DEBUG("generic set key: %s, fields: %lu", key.c_str(), entry.size()); + + m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_SET); + + auto status = waitForResponse(SAI_COMMON_API_SET); + + return status; +} + +sai_status_t ClientSai::get( + _In_ sai_object_type_t objectType, + _In_ const std::string& serializedObjectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + /* + * Since user may reuse buffers, then oid list buffers maybe not cleared + * and contain some garbage, let's clean them so we send all oids as null to + * syncd. + */ + + Utils::clearOidValues(objectType, attr_count, attr_list); + + auto entry = SaiAttributeList::serialize_attr_list(objectType, attr_count, attr_list, false); + + std::string serializedObjectType = sai_serialize_object_type(objectType); + + std::string key = serializedObjectType + ":" + serializedObjectId; + + SWSS_LOG_DEBUG("generic get key: %s, fields: %lu", key.c_str(), entry.size()); + + // get is special, it will not put data + // into asic view, only to message queue + m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_GET); + + auto status = waitForGetResponse(objectType, attr_count, attr_list); + + return status; +} + +// QUAD API RESPONSE HELPERS + +sai_status_t ClientSai::waitForResponse( + _In_ sai_common_api_t api) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco); + + if (api == SAI_COMMON_API_CREATE && status == SAI_STATUS_SUCCESS) + { + m_lastCreateOids.clear(); + + // since last api was create, we need to extract OID that was created in that api + // if create was for entry, oid is NULL + + const auto& entry = kfvFieldsValues(kco); + + const auto& field = fvField(entry.at(0)); + const auto& value = fvValue(entry.at(0)); + + SWSS_LOG_INFO("parsing response: %s:%s", field.c_str(), value.c_str()); + + if (field == "oid") + { + sai_object_id_t oid; + sai_deserialize_object_id(value, oid); + + m_lastCreateOids.push_back(oid); + } + } + + return status; +} + +sai_status_t ClientSai::waitForGetResponse( + _In_ sai_object_type_t objectType, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco); + + auto &values = kfvFieldsValues(kco); + + if (status == SAI_STATUS_SUCCESS) + { + if (values.size() == 0) + { + SWSS_LOG_THROW("logic error, get response returned 0 values!, send api response or sync/async issue?"); + } + + SaiAttributeList list(objectType, values, false); + + transfer_attributes(objectType, attr_count, list.get_attr_list(), attr_list, false); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + if (values.size() == 0) + { + SWSS_LOG_THROW("logic error, get response returned 0 values!, send api response or sync/async issue?"); + } + + SaiAttributeList list(objectType, values, true); + + // no need for id fix since this is overflow + transfer_attributes(objectType, attr_count, list.get_attr_list(), attr_list, true); + } + + return status; +} + +// FLUSH FDB ENTRIES + +sai_status_t ClientSai::flushFdbEntries( + _In_ sai_object_id_t switchId, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + auto entry = SaiAttributeList::serialize_attr_list( + SAI_OBJECT_TYPE_FDB_FLUSH, + attrCount, + attrList, + false); + + std::string serializedObjectId = sai_serialize_object_type(SAI_OBJECT_TYPE_FDB_FLUSH); + + // NOTE ! we actually give switch ID since FLUSH is not real object + std::string key = serializedObjectId + ":" + sai_serialize_object_id(switchId); + + SWSS_LOG_NOTICE("flush key: %s, fields: %lu", key.c_str(), entry.size()); + + m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_FLUSH); + + auto status = waitForFlushFdbEntriesResponse(); + + return status; +} + +sai_status_t ClientSai::waitForFlushFdbEntriesResponse() +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_FLUSHRESPONSE, kco); + + return status; +} + +// OBJECT TYPE GET AVAILABILITY + +sai_status_t ClientSai::objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + auto strSwitchId = sai_serialize_object_id(switchId); + + auto entry = SaiAttributeList::serialize_attr_list(objectType, attrCount, attrList, false); + + entry.push_back(swss::FieldValueTuple("OBJECT_TYPE", sai_serialize_object_type(objectType))); + + SWSS_LOG_DEBUG( + "Query arguments: switch: %s, attributes: %s", + strSwitchId.c_str(), + joinFieldValues(entry).c_str()); + + // Syncd will pop this argument off before trying to deserialize the attribute list + + // This query will not put any data into the ASIC view, just into the + // message queue + m_communicationChannel->set(strSwitchId, entry, REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY); + + auto status = waitForObjectTypeGetAvailabilityResponse(count); + + return status; +} + +sai_status_t ClientSai::waitForObjectTypeGetAvailabilityResponse( + _Inout_ uint64_t *count) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE, kco); + + if (status == SAI_STATUS_SUCCESS) + { + auto &values = kfvFieldsValues(kco); + + if (values.size() != 1) + { + SWSS_LOG_THROW("Invalid response from syncd: expected 1 value, received %zu", values.size()); + } + + const std::string &availability_str = fvValue(values[0]); + + *count = std::stol(availability_str); + + SWSS_LOG_DEBUG("Received payload: count = %lu", *count); + } + + return status; +} + +// QUERY ATTRIBUTE CAPABILITY + +sai_status_t ClientSai::queryAttributeCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ sai_attr_id_t attrId, + _Out_ sai_attr_capability_t *capability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + auto switchIdStr = sai_serialize_object_id(switchId); + auto objectTypeStr = sai_serialize_object_type(objectType); + + auto meta = sai_metadata_get_attr_metadata(objectType, attrId); + + if (meta == NULL) + { + SWSS_LOG_ERROR("Failed to find attribute metadata: object type %s, attr id %d", objectTypeStr.c_str(), attrId); + return SAI_STATUS_INVALID_PARAMETER; + } + + const std::string attrIdStr = meta->attridname; + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr), + swss::FieldValueTuple("ATTR_ID", attrIdStr) + }; + + SWSS_LOG_DEBUG( + "Query arguments: switch %s, object type: %s, attribute: %s", + switchIdStr.c_str(), + objectTypeStr.c_str(), + attrIdStr.c_str() + ); + + // This query will not put any data into the ASIC view, just into the + // message queue + + m_communicationChannel->set(switchIdStr, entry, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_QUERY); + + auto status = waitForQueryAttributeCapabilityResponse(capability); + + return status; +} + +sai_status_t ClientSai::waitForQueryAttributeCapabilityResponse( + _Out_ sai_attr_capability_t* capability) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_RESPONSE, kco); + + if (status == SAI_STATUS_SUCCESS) + { + const std::vector &values = kfvFieldsValues(kco); + + if (values.size() != 3) + { + SWSS_LOG_ERROR("Invalid response from syncd: expected 3 values, received %zu", values.size()); + + return SAI_STATUS_FAILURE; + } + + capability->create_implemented = (fvValue(values[0]) == "true" ? true : false); + capability->set_implemented = (fvValue(values[1]) == "true" ? true : false); + capability->get_implemented = (fvValue(values[2]) == "true" ? true : false); + + SWSS_LOG_DEBUG("Received payload: create_implemented:%s, set_implemented:%s, get_implemented:%s", + (capability->create_implemented ? "true" : "false"), + (capability->set_implemented ? "true" : "false"), + (capability->get_implemented ? "true" : "false")); + } + + return status; +} + +// QUERY ATTRIBUTE ENUM CAPABILITY + +sai_status_t ClientSai::queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ sai_attr_id_t attrId, + _Inout_ sai_s32_list_t *enumValuesCapability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + if (enumValuesCapability && enumValuesCapability->list) + { + // clear input list, since we use serialize to transfer values + for (uint32_t idx = 0; idx < enumValuesCapability->count; idx++) + enumValuesCapability->list[idx] = 0; + } + + auto switch_id_str = sai_serialize_object_id(switchId); + auto object_type_str = sai_serialize_object_type(objectType); + + auto meta = sai_metadata_get_attr_metadata(objectType, attrId); + + if (meta == NULL) + { + SWSS_LOG_ERROR("Failed to find attribute metadata: object type %s, attr id %d", object_type_str.c_str(), attrId); + return SAI_STATUS_INVALID_PARAMETER; + } + + const std::string attr_id_str = meta->attridname; + const std::string list_size = std::to_string(enumValuesCapability->count); + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", object_type_str), + swss::FieldValueTuple("ATTR_ID", attr_id_str), + swss::FieldValueTuple("LIST_SIZE", list_size) + }; + + SWSS_LOG_DEBUG( + "Query arguments: switch %s, object type: %s, attribute: %s, count: %s", + switch_id_str.c_str(), + object_type_str.c_str(), + attr_id_str.c_str(), + list_size.c_str() + ); + + // This query will not put any data into the ASIC view, just into the + // message queue + + m_communicationChannel->set(switch_id_str, entry, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_QUERY); + + auto status = waitForQueryAattributeEnumValuesCapabilityResponse(enumValuesCapability); + + return status; +} + +sai_status_t ClientSai::waitForQueryAattributeEnumValuesCapabilityResponse( + _Inout_ sai_s32_list_t* enumValuesCapability) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_RESPONSE, kco); + + if (status == SAI_STATUS_SUCCESS) + { + const std::vector &values = kfvFieldsValues(kco); + + if (values.size() != 2) + { + SWSS_LOG_ERROR("Invalid response from syncd: expected 2 values, received %zu", values.size()); + + return SAI_STATUS_FAILURE; + } + + const std::string &capability_str = fvValue(values[0]); + const uint32_t num_capabilities = std::stoi(fvValue(values[1])); + + SWSS_LOG_DEBUG("Received payload: capabilities = '%s', count = %d", capability_str.c_str(), num_capabilities); + + enumValuesCapability->count = num_capabilities; + + size_t position = 0; + for (uint32_t i = 0; i < num_capabilities; i++) + { + size_t old_position = position; + position = capability_str.find(",", old_position); + std::string capability = capability_str.substr(old_position, position - old_position); + enumValuesCapability->list[i] = std::stoi(capability); + + // We have run out of values to add to our list + if (position == std::string::npos) + { + if (num_capabilities != i + 1) + { + SWSS_LOG_WARN("Query returned less attributes than expected: expected %d, received %d", num_capabilities, i+1); + } + + break; + } + + // Skip the commas + position++; + } + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + // TODO on sai status overflow we should populate correct count on the list + + SWSS_LOG_ERROR("TODO need to handle SAI_STATUS_BUFFER_OVERFLOW, FIXME"); + } + + return status; +} + + +// STATS API + +sai_status_t ClientSai::getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + auto stats_enum = sai_metadata_get_object_type_info(object_type)->statenum; + + auto entry = serialize_counter_id_list(stats_enum, number_of_counters, counter_ids); + + std::string str_object_type = sai_serialize_object_type(object_type); + + std::string key = str_object_type + ":" + sai_serialize_object_id(object_id); + + SWSS_LOG_DEBUG("generic get stats key: %s, fields: %lu", key.c_str(), entry.size()); + + // get_stats will not put data to asic view, only to message queue + + m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_GET_STATS); + + return waitForGetStatsResponse(number_of_counters, counters); +} + +sai_status_t ClientSai::waitForGetStatsResponse( + _In_ uint32_t number_of_counters, + _Out_ uint64_t *counters) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco); + + if (status == SAI_STATUS_SUCCESS) + { + auto &values = kfvFieldsValues(kco); + + if (values.size () != number_of_counters) + { + SWSS_LOG_THROW("wrong number of counters, got %zu, expected %u", values.size(), number_of_counters); + } + + for (uint32_t idx = 0; idx < number_of_counters; idx++) + { + counters[idx] = stoull(fvValue(values[idx])); + } + } + + return status; +} + +sai_status_t ClientSai::getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + SWSS_LOG_ERROR("not implemented"); + + // TODO could be the same as getStats but put mode at first argument + + return SAI_STATUS_NOT_IMPLEMENTED; +} + +sai_status_t ClientSai::clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + auto stats_enum = sai_metadata_get_object_type_info(object_type)->statenum; + + auto values = serialize_counter_id_list(stats_enum, number_of_counters, counter_ids); + + auto str_object_type = sai_serialize_object_type(object_type); + + auto key = str_object_type + ":" + sai_serialize_object_id(object_id); + + SWSS_LOG_DEBUG("generic clear stats key: %s, fields: %lu", key.c_str(), values.size()); + + // clear_stats will not put data into asic view, only to message queue + + m_communicationChannel->set(key, values, REDIS_ASIC_STATE_COMMAND_CLEAR_STATS); + + auto status = waitForClearStatsResponse(); + + return status; +} + +sai_status_t ClientSai::waitForClearStatsResponse() +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco); + + return status; +} + +// BULK CREATE + +sai_status_t ClientSai::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // NOTE: in client mode, each create oid must be retrieved from server since + // server is actually creating new oid, and it's value must be transferred + // over communication channel + + // TODO support mode + + SWSS_LOG_THROW("TODO, not implemented, FIXME"); + + //for (uint32_t idx = 0; idx < object_count; idx++) + //{ + // object_id[idx] = m_virtualObjectIdManager->allocateNewObjectId(object_type, switch_id); + + // if (object_id[idx] == SAI_NULL_OBJECT_ID) + // { + // SWSS_LOG_ERROR("failed to create %s, with switch id: %s", + // sai_serialize_object_type(object_type).c_str(), + // sai_serialize_object_id(switch_id).c_str()); + + // return SAI_STATUS_INSUFFICIENT_RESOURCES; + // } + //} + + //std::vector serialized_object_ids; + + //// on create vid is put in db by syncd + //for (uint32_t idx = 0; idx < object_count; idx++) + //{ + // std::string str_object_id = sai_serialize_object_id(object_id[idx]); + // serialized_object_ids.push_back(str_object_id); + //} + + //auto status = bulkCreate( + // object_type, + // serialized_object_ids, + // attr_count, + // attr_list, + // mode, + // object_statuses); + + // TODO m_lastCreateOids +} + +sai_status_t ClientSai::bulkCreate( + _In_ uint32_t object_count, + _In_ const sai_route_entry_t* route_entry, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // TODO support mode + + static PerformanceIntervalTimer timer("ClientSai::bulkCreate(route_entry)"); + + timer.start(); + + std::vector serialized_object_ids; + + // on create vid is put in db by syncd + for (uint32_t idx = 0; idx < object_count; idx++) + { + std::string str_object_id = sai_serialize_route_entry(route_entry[idx]); + serialized_object_ids.push_back(str_object_id); + } + + auto status = bulkCreate( + SAI_OBJECT_TYPE_ROUTE_ENTRY, + serialized_object_ids, + attr_count, + attr_list, + mode, + object_statuses); + + timer.stop(); + + timer.inc(object_count); + + return status; +} + +sai_status_t ClientSai::bulkCreate( + _In_ uint32_t object_count, + _In_ const sai_fdb_entry_t* fdb_entry, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // TODO support mode + + std::vector serialized_object_ids; + + // on create vid is put in db by syncd + for (uint32_t idx = 0; idx < object_count; idx++) + { + std::string str_object_id = sai_serialize_fdb_entry(fdb_entry[idx]); + serialized_object_ids.push_back(str_object_id); + } + + return bulkCreate( + SAI_OBJECT_TYPE_FDB_ENTRY, + serialized_object_ids, + attr_count, + attr_list, + mode, + object_statuses); +} + +sai_status_t ClientSai::bulkCreate( + _In_ uint32_t object_count, + _In_ const sai_inseg_entry_t* inseg_entry, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // TODO support mode + + static PerformanceIntervalTimer timer("ClientSai::bulkCreate(inseg_entry)"); + + timer.start(); + + std::vector serialized_object_ids; + + // on create vid is put in db by syncd + for (uint32_t idx = 0; idx < object_count; idx++) + { + std::string str_object_id = sai_serialize_inseg_entry(inseg_entry[idx]); + serialized_object_ids.push_back(str_object_id); + } + + auto status = bulkCreate( + SAI_OBJECT_TYPE_INSEG_ENTRY, + serialized_object_ids, + attr_count, + attr_list, + mode, + object_statuses); + + timer.stop(); + + timer.inc(object_count); + + return status; +} + +sai_status_t ClientSai::bulkCreate( + _In_ uint32_t object_count, + _In_ const sai_nat_entry_t* nat_entry, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // TODO support mode + + std::vector serialized_object_ids; + + // on create vid is put in db by syncd + for (uint32_t idx = 0; idx < object_count; idx++) + { + std::string str_object_id = sai_serialize_nat_entry(nat_entry[idx]); + serialized_object_ids.push_back(str_object_id); + } + + return bulkCreate( + SAI_OBJECT_TYPE_NAT_ENTRY, + serialized_object_ids, + attr_count, + attr_list, + mode, + object_statuses); +} + +// BULK CREATE HELPERS + +sai_status_t ClientSai::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Inout_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + + // TODO support mode + + std::string str_object_type = sai_serialize_object_type(object_type); + + std::vector entries; + + for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) + { + auto entry = SaiAttributeList::serialize_attr_list(object_type, attr_count[idx], attr_list[idx], false); + + if (entry.empty()) + { + // make sure that we put object into db + // even if there are no attributes set + swss::FieldValueTuple null("NULL", "NULL"); + + entry.push_back(null); + } + + std::string str_attr = joinFieldValues(entry); + + swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , str_attr); + + entries.push_back(fvtNoStatus); + } + + /* + * We are adding number of entries to actually add ':' to be compatible + * with previous + */ + + // key: object_type:count + // field: object_id + // value: object_attrs + std::string key = str_object_type + ":" + std::to_string(entries.size()); + + m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_CREATE); + + return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, (uint32_t)serialized_object_ids.size(), object_statuses); +} + +// BULK REMOVE + +sai_status_t ClientSai::bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_object_id(object_id[idx])); + } + + return bulkRemove(object_type, serializedObjectIds, mode, object_statuses); +} + +sai_status_t ClientSai::bulkRemove( + _In_ uint32_t object_count, + _In_ const sai_route_entry_t *route_entry, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_route_entry(route_entry[idx])); + } + + return bulkRemove(SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectIds, mode, object_statuses); +} + +sai_status_t ClientSai::bulkRemove( + _In_ uint32_t object_count, + _In_ const sai_nat_entry_t *nat_entry, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_nat_entry(nat_entry[idx])); + } + + return bulkRemove(SAI_OBJECT_TYPE_NAT_ENTRY, serializedObjectIds, mode, object_statuses); +} + +sai_status_t ClientSai::bulkRemove( + _In_ uint32_t object_count, + _In_ const sai_inseg_entry_t *inseg_entry, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_inseg_entry(inseg_entry[idx])); + } + + return bulkRemove(SAI_OBJECT_TYPE_INSEG_ENTRY, serializedObjectIds, mode, object_statuses); +} + +sai_status_t ClientSai::bulkRemove( + _In_ uint32_t object_count, + _In_ const sai_fdb_entry_t *fdb_entry, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_fdb_entry(fdb_entry[idx])); + } + + return bulkRemove(SAI_OBJECT_TYPE_FDB_ENTRY, serializedObjectIds, mode, object_statuses); +} + +// BULK REMOVE HELPERS + +sai_status_t ClientSai::bulkRemove( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + + // TODO support mode, this will need to go as extra parameter and needs to + // be supported by LUA script passed as first or last entry in values, + // currently mode is ignored + + std::string serializedObjectType = sai_serialize_object_type(object_type); + + std::vector entries; + + for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) + { + std::string str_attr = ""; + + swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx], str_attr); + + entries.push_back(fvtNoStatus); + } + + /* + * We are adding number of entries to actually add ':' to be compatible + * with previous + */ + + // key: object_type:count + // field: object_id + // value: object_attrs + std::string key = serializedObjectType + ":" + std::to_string(entries.size()); + + m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_REMOVE); + + return waitForBulkResponse(SAI_COMMON_API_BULK_REMOVE, (uint32_t)serialized_object_ids.size(), object_statuses); +} + +// BULK SET + +sai_status_t ClientSai::bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_object_id(object_id[idx])); + } + + return bulkSet(object_type, serializedObjectIds, attr_list, mode, object_statuses); +} + +sai_status_t ClientSai::bulkSet( + _In_ uint32_t object_count, + _In_ const sai_route_entry_t *route_entry, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_route_entry(route_entry[idx])); + } + + return bulkSet(SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectIds, attr_list, mode, object_statuses); +} + +sai_status_t ClientSai::bulkSet( + _In_ uint32_t object_count, + _In_ const sai_nat_entry_t *nat_entry, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_nat_entry(nat_entry[idx])); + } + + return bulkSet(SAI_OBJECT_TYPE_NAT_ENTRY, serializedObjectIds, attr_list, mode, object_statuses); +} + +sai_status_t ClientSai::bulkSet( + _In_ uint32_t object_count, + _In_ const sai_inseg_entry_t *inseg_entry, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_inseg_entry(inseg_entry[idx])); + } + + return bulkSet(SAI_OBJECT_TYPE_INSEG_ENTRY, serializedObjectIds, attr_list, mode, object_statuses); +} + +sai_status_t ClientSai::bulkSet( + _In_ uint32_t object_count, + _In_ const sai_fdb_entry_t *fdb_entry, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serializedObjectIds; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + serializedObjectIds.emplace_back(sai_serialize_fdb_entry(fdb_entry[idx])); + } + + return bulkSet(SAI_OBJECT_TYPE_FDB_ENTRY, serializedObjectIds, attr_list, mode, object_statuses); +} + +// BULK SET HELPERS + +sai_status_t ClientSai::bulkSet( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + // TODO support mode + + std::vector entries; + + for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) + { + auto entry = SaiAttributeList::serialize_attr_list(object_type, 1, &attr_list[idx], false); + + std::string str_attr = joinFieldValues(entry); + + swss::FieldValueTuple value(serialized_object_ids[idx], str_attr); + + entries.push_back(value); + } + + /* + * We are adding number of entries to actually add ':' to be compatible + * with previous + */ + + auto serializedObjectType = sai_serialize_object_type(object_type); + + std::string key = serializedObjectType + ":" + std::to_string(entries.size()); + + m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_SET); + + return waitForBulkResponse(SAI_COMMON_API_BULK_SET, (uint32_t)serialized_object_ids.size(), object_statuses); +} + +// BULK RESPONSE HELPERS + +sai_status_t ClientSai::waitForBulkResponse( + _In_ sai_common_api_t api, + _In_ uint32_t object_count, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco); + + auto &values = kfvFieldsValues(kco); + + // double object count may show up when bulk create api is executed + + if ((values.size() != 2 * object_count) && (values.size() != object_count)) + { + SWSS_LOG_THROW("wrong number of counters, got %zu, expected %u", values.size(), object_count); + } + + // deserialize statuses for all objects + + for (uint32_t idx = 0; idx < object_count; idx++) + { + sai_deserialize_status(fvField(values[idx]), object_statuses[idx]); + } + + if (api == SAI_COMMON_API_BULK_CREATE) + { + m_lastCreateOids.clear(); + + // since last api was create, we need to extract OID that was created in that api + // if create was for entry, oid is NULL + + for (uint32_t idx = object_count; idx < 2 * object_count; idx++) + { + const auto& field = fvField(values.at(idx)); + const auto& value = fvValue(values.at(idx)); + + if (field == "oid") + { + sai_object_id_t oid; + sai_deserialize_object_id(value, oid); + + m_lastCreateOids.push_back(oid); + } + } + } + + return status; +} + +void ClientSai::handleNotification( + _In_ const std::string &name, + _In_ const std::string &serializedNotification, + _In_ const std::vector &values) +{ + SWSS_LOG_ENTER(); + + // TODO to pass switch_id for every notification we could add it to values + // at syncd side + // + // Each global context (syncd) will have it's own notification thread + // handler, so we will know at which context notification arrived, but we + // also need to know at which switch id generated this notification. For + // that we will assign separate notification handlers in syncd itself, and + // each of those notifications will know to which switch id it belongs. + // Then later we could also check whether oids in notification actually + // belongs to given switch id. This way we could find vendor bugs like + // sending notifications from one switch to another switch handler. + // + // But before that we will extract switch id from notification itself. + + auto notification = NotificationFactory::deserialize(name, serializedNotification); + + if (notification) + { + MUTEX(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__); + + return; + } + + auto sn = syncProcessNotification(notification); + + // execute callback from notification thread + + notification->executeCallback(sn); + } +} + +sai_switch_notifications_t ClientSai::syncProcessNotification( + _In_ std::shared_ptr notification) +{ + SWSS_LOG_ENTER(); + + // NOTE: process metadata must be executed under sairedis API mutex since + // it will access meta database and notification comes from different + // thread, and this method is executed from notifications thread + + auto objectId = notification->getAnyObjectId(); + + auto switchId = switchIdQuery(objectId); + + auto sw = m_switchContainer->getSwitch(switchId); + + if (sw) + { + return sw->getSwitchNotifications(); // explicit copy + } + + SWSS_LOG_WARN("switch %s not present in container, returning empty switch notifications", + sai_serialize_object_id(switchId).c_str()); + + return { }; +} + +sai_object_type_t ClientSai::objectTypeQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return SAI_OBJECT_TYPE_NULL; + } + + return VirtualObjectIdManager::objectTypeQuery(objectId); +} + +sai_object_id_t ClientSai::switchIdQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return SAI_OBJECT_TYPE_NULL; + } + + return VirtualObjectIdManager::switchIdQuery(objectId); +} + +sai_status_t ClientSai::logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) +{ + SWSS_LOG_ENTER(); + + return SAI_STATUS_SUCCESS; +} diff --git a/lib/src/ClientServerSai.cpp b/lib/src/ClientServerSai.cpp new file mode 100644 index 000000000000..ebe2faaa5f41 --- /dev/null +++ b/lib/src/ClientServerSai.cpp @@ -0,0 +1,571 @@ +#include "ClientServerSai.h" +#include "ClientSai.h" +#include "ServerSai.h" +#include "SaiInternal.h" +#include "Sai.h" + +#include "swss/logger.h" + +#include "meta/sai_serialize.h" + +using namespace sairedis; +using namespace std::placeholders; + +#define REDIS_CHECK_API_INITIALIZED() \ + if (!m_apiInitialized) { \ + SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__); \ + return SAI_STATUS_FAILURE; } + +ClientServerSai::ClientServerSai() +{ + SWSS_LOG_ENTER(); + + m_apiInitialized = false; +} + +ClientServerSai::~ClientServerSai() +{ + SWSS_LOG_ENTER(); + + if (m_apiInitialized) + { + uninitialize(); + } +} + +// INITIALIZE UNINITIALIZE + +sai_status_t ClientServerSai::initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) +{ + MUTEX(); + SWSS_LOG_ENTER(); + + if (m_apiInitialized) + { + SWSS_LOG_ERROR("%s: api already initialized", __PRETTY_FUNCTION__); + + return SAI_STATUS_FAILURE; + } + + if (flags != 0) + { + SWSS_LOG_ERROR("invalid flags passed to SAI API initialize"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + if ((service_method_table == NULL) || + (service_method_table->profile_get_next_value == NULL) || + (service_method_table->profile_get_value == NULL)) + { + SWSS_LOG_ERROR("invalid service_method_table handle passed to SAI API initialize"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + memcpy(&m_service_method_table, service_method_table, sizeof(m_service_method_table)); + + const char* enableClient = service_method_table->profile_get_value(0, SAI_REDIS_KEY_ENABLE_CLIENT); + + if (enableClient && strcmp(enableClient, "true") == 0) + { + SWSS_LOG_NOTICE("acting as a sairedis CLIENT"); + + m_sai = std::make_shared(); + } + else + { + SWSS_LOG_NOTICE("acting as a sairedis SERVER"); + + m_sai = std::make_shared(); + } + + auto status = m_sai->initialize(flags, service_method_table); + + SWSS_LOG_NOTICE("init client/server sai: %s", sai_serialize_status(status).c_str()); + + if (status == SAI_STATUS_SUCCESS) + { + m_apiInitialized = true; + } + + return status; +} + +sai_status_t ClientServerSai::uninitialize(void) +{ + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + SWSS_LOG_NOTICE("begin"); + + m_apiInitialized = false; + + m_sai = nullptr; + + SWSS_LOG_NOTICE("end"); + + return SAI_STATUS_SUCCESS; +} + +// QUAD OID + +sai_status_t ClientServerSai::create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->create( + objectType, + objectId, + switchId, + attr_count, + attr_list); +} + +sai_status_t ClientServerSai::remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->remove(objectType, objectId); +} + +sai_status_t ClientServerSai::set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->set(objectType, objectId, attr); +} + +sai_status_t ClientServerSai::get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->get( + objectType, + objectId, + attr_count, + attr_list); +} + +// QUAD ENTRY + +#define DECLARE_CREATE_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::create( \ + _In_ const sai_ ## ot ## _t* entry, \ + _In_ uint32_t attr_count, \ + _In_ const sai_attribute_t *attr_list) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->create(entry, attr_count, attr_list); \ +} + +DECLARE_CREATE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_CREATE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_CREATE_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_CREATE_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_CREATE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_CREATE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_CREATE_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_CREATE_ENTRY(NAT_ENTRY,nat_entry); + + +#define DECLARE_REMOVE_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::remove( \ + _In_ const sai_ ## ot ## _t* entry) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->remove(entry); \ +} + +DECLARE_REMOVE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_REMOVE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_REMOVE_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_REMOVE_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_REMOVE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_REMOVE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_REMOVE_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_REMOVE_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_SET_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::set( \ + _In_ const sai_ ## ot ## _t* entry, \ + _In_ const sai_attribute_t *attr) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->set(entry, attr); \ +} + +DECLARE_SET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_SET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_SET_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_SET_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_SET_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_SET_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_SET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_SET_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_GET_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::get( \ + _In_ const sai_ ## ot ## _t* entry, \ + _In_ uint32_t attr_count, \ + _Inout_ sai_attribute_t *attr_list) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->get(entry, attr_count, attr_list); \ +} + +DECLARE_GET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_GET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_GET_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_GET_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_GET_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_GET_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_GET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_GET_ENTRY(NAT_ENTRY,nat_entry); + +// STATS + +sai_status_t ClientServerSai::getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->getStats( + object_type, + object_id, + number_of_counters, + counter_ids, + counters); +} + +sai_status_t ClientServerSai::getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->getStatsExt( + object_type, + object_id, + number_of_counters, + counter_ids, + mode, + counters); +} + +sai_status_t ClientServerSai::clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->clearStats( + object_type, + object_id, + number_of_counters, + counter_ids); +} + +// BULK QUAD OID + +sai_status_t ClientServerSai::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->bulkCreate( + object_type, + switch_id, + object_count, + attr_count, + attr_list, + mode, + object_id, + object_statuses); +} + +sai_status_t ClientServerSai::bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->bulkRemove( + object_type, + object_count, + object_id, + mode, + object_statuses); +} + +sai_status_t ClientServerSai::bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->bulkSet( + object_type, + object_count, + object_id, + attr_list, + mode, + object_statuses); +} + +// BULK QUAD ENTRY + +#define DECLARE_BULK_CREATE_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::bulkCreate( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t* entries, \ + _In_ const uint32_t *attr_count, \ + _In_ const sai_attribute_t **attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->bulkCreate( \ + object_count, \ + entries, \ + attr_count, \ + attr_list, \ + mode, \ + object_statuses); \ +} + +DECLARE_BULK_CREATE_ENTRY(ROUTE_ENTRY,route_entry) +DECLARE_BULK_CREATE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_BULK_CREATE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_BULK_CREATE_ENTRY(NAT_ENTRY,nat_entry) + + +// BULK REMOVE + +#define DECLARE_BULK_REMOVE_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::bulkRemove( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *entries, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->bulkRemove( \ + object_count, \ + entries, \ + mode, \ + object_statuses); \ +} + +DECLARE_BULK_REMOVE_ENTRY(ROUTE_ENTRY,route_entry) +DECLARE_BULK_REMOVE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_BULK_REMOVE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_BULK_REMOVE_ENTRY(NAT_ENTRY,nat_entry) + +// BULK SET + +#define DECLARE_BULK_SET_ENTRY(OT,ot) \ +sai_status_t ClientServerSai::bulkSet( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *entries, \ + _In_ const sai_attribute_t *attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->bulkSet( \ + object_count, \ + entries, \ + attr_list, \ + mode, \ + object_statuses); \ +} + +DECLARE_BULK_SET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_BULK_SET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_BULK_SET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_BULK_SET_ENTRY(NAT_ENTRY,nat_entry); + +// NON QUAD API + +sai_status_t ClientServerSai::flushFdbEntries( + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->flushFdbEntries(switch_id, attr_count, attr_list); +} + +// SAI API + +sai_status_t ClientServerSai::objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->objectTypeGetAvailability( + switchId, + objectType, + attrCount, + attrList, + count); +} + +sai_status_t ClientServerSai::queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->queryAttributeCapability( + switch_id, + object_type, + attr_id, + capability); +} + +sai_status_t ClientServerSai::queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->queryAattributeEnumValuesCapability( + switch_id, + object_type, + attr_id, + enum_values_capability); +} + +sai_object_type_t ClientServerSai::objectTypeQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return SAI_OBJECT_TYPE_NULL; + } + + return m_sai->objectTypeQuery(objectId); +} + +sai_object_id_t ClientServerSai::switchIdQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return SAI_NULL_OBJECT_ID; + } + + return m_sai->switchIdQuery(objectId); +} + +sai_status_t ClientServerSai::logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->logSet(api, log_level); +} diff --git a/lib/src/Context.cpp b/lib/src/Context.cpp index 19f1843ab065..a5cecf528fac 100644 --- a/lib/src/Context.cpp +++ b/lib/src/Context.cpp @@ -2,6 +2,8 @@ #include "swss/logger.h" +#include "meta/sai_serialize.h" + using namespace sairedis; using namespace std::placeholders; @@ -52,5 +54,14 @@ void Context::populateMetadata( auto& dump = m_redisSai->getTableDump(); + SWSS_LOG_NOTICE("dump size: %zu", dump.size()); + + if (dump.size() == 0) + { + SWSS_LOG_NOTICE("skipping populate metadata for switch %s, (probably connecting to already existing switch)", + sai_serialize_object_id(switchId).c_str()); + return; + } + m_meta->populate(dump.at(switchId)); } diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 71910678a05a..bb4ced5468e9 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -10,6 +10,11 @@ lib_LTLIBRARIES = libsairedis.la noinst_LIBRARIES = libSaiRedis.a libSaiRedis_a_SOURCES = \ + ../../syncd/ZeroMQSelectableChannel.cpp \ + ../../syncd/SelectableChannel.cpp \ + ClientServerSai.cpp \ + ClientSai.cpp \ + ServerSai.cpp \ PerformanceIntervalTimer.cpp \ ZeroMQChannel.cpp \ Channel.cpp \ diff --git a/lib/src/RedisRemoteSaiInterface.cpp b/lib/src/RedisRemoteSaiInterface.cpp index 48301e1c3bc5..e1f2e8db97a1 100644 --- a/lib/src/RedisRemoteSaiInterface.cpp +++ b/lib/src/RedisRemoteSaiInterface.cpp @@ -133,9 +133,10 @@ sai_status_t RedisRemoteSaiInterface::uninitialize(void) return SAI_STATUS_SUCCESS; } +// TODO move to common namespace utils class std::string RedisRemoteSaiInterface::getHardwareInfo( _In_ uint32_t attrCount, - _In_ const sai_attribute_t *attrList) const + _In_ const sai_attribute_t *attrList) { SWSS_LOG_ENTER(); @@ -175,7 +176,6 @@ std::string RedisRemoteSaiInterface::getHardwareInfo( return std::string((const char*)s8list.list, actualLength); } - sai_status_t RedisRemoteSaiInterface::create( _In_ sai_object_type_t objectType, _Out_ sai_object_id_t* objectId, @@ -212,6 +212,14 @@ sai_status_t RedisRemoteSaiInterface::create( if (attr && attr->value.booldata == false) { + if (m_switchContainer->contains(*objectId)) + { + SWSS_LOG_NOTICE("switch container already contains switch %s", + sai_serialize_object_id(*objectId).c_str()); + + return SAI_STATUS_SUCCESS; + } + refreshTableDump(); if (m_tableDump.find(switchId) == m_tableDump.end()) diff --git a/lib/src/Sai.cpp b/lib/src/Sai.cpp index 411ad37a8d38..b5f2c60a1e6e 100644 --- a/lib/src/Sai.cpp +++ b/lib/src/Sai.cpp @@ -166,6 +166,8 @@ sai_status_t Sai::create( { // request to connect to existing switch + SWSS_LOG_NOTICE("Sai::create call context populateMetadata"); + context->populateMetadata(*objectId); } } diff --git a/lib/src/ServerSai.cpp b/lib/src/ServerSai.cpp new file mode 100644 index 000000000000..821d2f3f2868 --- /dev/null +++ b/lib/src/ServerSai.cpp @@ -0,0 +1,988 @@ +#include "ServerSai.h" +#include "SaiInternal.h" +#include "Sai.h" +#include "sairediscommon.h" + +#include "syncd/ZeroMQSelectableChannel.h" + +#include "meta/sai_serialize.h" +#include "meta/SaiAttributeList.h" + +#include "swss/logger.h" +#include "swss/select.h" + +using namespace sairedis; +using namespace saimeta; +using namespace std::placeholders; + +#define REDIS_CHECK_API_INITIALIZED() \ + if (!m_apiInitialized) { \ + SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__); \ + return SAI_STATUS_FAILURE; } + +ServerSai::ServerSai() +{ + SWSS_LOG_ENTER(); + + m_apiInitialized = false; + + m_runServerThread = false; +} + +ServerSai::~ServerSai() +{ + SWSS_LOG_ENTER(); + + if (m_apiInitialized) + { + uninitialize(); + } +} + +// INITIALIZE UNINITIALIZE + +sai_status_t ServerSai::initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) +{ + MUTEX(); + SWSS_LOG_ENTER(); + + if (m_apiInitialized) + { + SWSS_LOG_ERROR("%s: api already initialized", __PRETTY_FUNCTION__); + + return SAI_STATUS_FAILURE; + } + + if (flags != 0) + { + SWSS_LOG_ERROR("invalid flags passed to SAI API initialize"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + if ((service_method_table == NULL) || + (service_method_table->profile_get_next_value == NULL) || + (service_method_table->profile_get_value == NULL)) + { + SWSS_LOG_ERROR("invalid service_method_table handle passed to SAI API initialize"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + memcpy(&m_service_method_table, service_method_table, sizeof(m_service_method_table)); + + m_sai = std::make_shared(); // actual SAI to talk to syncd + + auto status = m_sai->initialize(flags, service_method_table); + + SWSS_LOG_NOTICE("init client/server sai: %s", sai_serialize_status(status).c_str()); + + if (status == SAI_STATUS_SUCCESS) + { + // TODO from config + m_selectableChannel = std::make_shared("ipc:///tmp/saiServer"); + + SWSS_LOG_NOTICE("starting server thread"); + + m_runServerThread = true; + + m_serverThread = std::make_shared(&ServerSai::serverThreadFunction, this); + + m_apiInitialized = true; + } + + return status; +} + +sai_status_t ServerSai::uninitialize(void) +{ + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + SWSS_LOG_NOTICE("begin"); + + m_apiInitialized = false; + + if (m_serverThread) + { + SWSS_LOG_NOTICE("end server thread begin"); + + m_runServerThread = true; + + m_serverThreadThreadShouldEndEvent.notify(); + + m_serverThread->join(); + + SWSS_LOG_NOTICE("end server thread end"); + } + + m_sai = nullptr; + + SWSS_LOG_NOTICE("end"); + + return SAI_STATUS_SUCCESS; +} + +// QUAD OID + +sai_status_t ServerSai::create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->create( + objectType, + objectId, + switchId, + attr_count, + attr_list); +} + +sai_status_t ServerSai::remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->remove(objectType, objectId); +} + +sai_status_t ServerSai::set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->set(objectType, objectId, attr); +} + +sai_status_t ServerSai::get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->get( + objectType, + objectId, + attr_count, + attr_list); +} + +// QUAD ENTRY + +#define DECLARE_CREATE_ENTRY(OT,ot) \ +sai_status_t ServerSai::create( \ + _In_ const sai_ ## ot ## _t* entry, \ + _In_ uint32_t attr_count, \ + _In_ const sai_attribute_t *attr_list) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->create(entry, attr_count, attr_list); \ +} + +DECLARE_CREATE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_CREATE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_CREATE_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_CREATE_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_CREATE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_CREATE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_CREATE_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_CREATE_ENTRY(NAT_ENTRY,nat_entry); + + +#define DECLARE_REMOVE_ENTRY(OT,ot) \ +sai_status_t ServerSai::remove( \ + _In_ const sai_ ## ot ## _t* entry) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->remove(entry); \ +} + +DECLARE_REMOVE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_REMOVE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_REMOVE_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_REMOVE_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_REMOVE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_REMOVE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_REMOVE_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_REMOVE_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_SET_ENTRY(OT,ot) \ +sai_status_t ServerSai::set( \ + _In_ const sai_ ## ot ## _t* entry, \ + _In_ const sai_attribute_t *attr) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->set(entry, attr); \ +} + +DECLARE_SET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_SET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_SET_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_SET_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_SET_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_SET_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_SET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_SET_ENTRY(NAT_ENTRY,nat_entry); + +#define DECLARE_GET_ENTRY(OT,ot) \ +sai_status_t ServerSai::get( \ + _In_ const sai_ ## ot ## _t* entry, \ + _In_ uint32_t attr_count, \ + _Inout_ sai_attribute_t *attr_list) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->get(entry, attr_count, attr_list); \ +} + +DECLARE_GET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_GET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_GET_ENTRY(IPMC_ENTRY,ipmc_entry); +DECLARE_GET_ENTRY(L2MC_ENTRY,l2mc_entry); +DECLARE_GET_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); +DECLARE_GET_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); +DECLARE_GET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_GET_ENTRY(NAT_ENTRY,nat_entry); + +// STATS + +sai_status_t ServerSai::getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->getStats( + object_type, + object_id, + number_of_counters, + counter_ids, + counters); +} + +sai_status_t ServerSai::getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->getStatsExt( + object_type, + object_id, + number_of_counters, + counter_ids, + mode, + counters); +} + +sai_status_t ServerSai::clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->clearStats( + object_type, + object_id, + number_of_counters, + counter_ids); +} + +// BULK QUAD OID + +sai_status_t ServerSai::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->bulkCreate( + object_type, + switch_id, + object_count, + attr_count, + attr_list, + mode, + object_id, + object_statuses); +} + +sai_status_t ServerSai::bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->bulkRemove( + object_type, + object_count, + object_id, + mode, + object_statuses); +} + +sai_status_t ServerSai::bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->bulkSet( + object_type, + object_count, + object_id, + attr_list, + mode, + object_statuses); +} + +// BULK QUAD ENTRY + +#define DECLARE_BULK_CREATE_ENTRY(OT,ot) \ +sai_status_t ServerSai::bulkCreate( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t* entries, \ + _In_ const uint32_t *attr_count, \ + _In_ const sai_attribute_t **attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->bulkCreate( \ + object_count, \ + entries, \ + attr_count, \ + attr_list, \ + mode, \ + object_statuses); \ +} + +DECLARE_BULK_CREATE_ENTRY(ROUTE_ENTRY,route_entry) +DECLARE_BULK_CREATE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_BULK_CREATE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_BULK_CREATE_ENTRY(NAT_ENTRY,nat_entry) + + +// BULK REMOVE + +#define DECLARE_BULK_REMOVE_ENTRY(OT,ot) \ +sai_status_t ServerSai::bulkRemove( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *entries, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->bulkRemove( \ + object_count, \ + entries, \ + mode, \ + object_statuses); \ +} + +DECLARE_BULK_REMOVE_ENTRY(ROUTE_ENTRY,route_entry) +DECLARE_BULK_REMOVE_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_BULK_REMOVE_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_BULK_REMOVE_ENTRY(NAT_ENTRY,nat_entry) + +// BULK SET + +#define DECLARE_BULK_SET_ENTRY(OT,ot) \ +sai_status_t ServerSai::bulkSet( \ + _In_ uint32_t object_count, \ + _In_ const sai_ ## ot ## _t *entries, \ + _In_ const sai_attribute_t *attr_list, \ + _In_ sai_bulk_op_error_mode_t mode, \ + _Out_ sai_status_t *object_statuses) \ +{ \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + REDIS_CHECK_API_INITIALIZED(); \ + return m_sai->bulkSet( \ + object_count, \ + entries, \ + attr_list, \ + mode, \ + object_statuses); \ +} + +DECLARE_BULK_SET_ENTRY(ROUTE_ENTRY,route_entry); +DECLARE_BULK_SET_ENTRY(FDB_ENTRY,fdb_entry); +DECLARE_BULK_SET_ENTRY(INSEG_ENTRY,inseg_entry); +DECLARE_BULK_SET_ENTRY(NAT_ENTRY,nat_entry); + +// NON QUAD API + +sai_status_t ServerSai::flushFdbEntries( + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->flushFdbEntries(switch_id, attr_count, attr_list); +} + +// SAI API + +sai_status_t ServerSai::objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->objectTypeGetAvailability( + switchId, + objectType, + attrCount, + attrList, + count); +} + +sai_status_t ServerSai::queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->queryAttributeCapability( + switch_id, + object_type, + attr_id, + capability); +} + +sai_status_t ServerSai::queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->queryAattributeEnumValuesCapability( + switch_id, + object_type, + attr_id, + enum_values_capability); +} + +sai_object_type_t ServerSai::objectTypeQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return SAI_OBJECT_TYPE_NULL; + } + + return m_sai->objectTypeQuery(objectId); +} + +sai_object_id_t ServerSai::switchIdQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return SAI_NULL_OBJECT_ID; + } + + return m_sai->switchIdQuery(objectId); +} + +sai_status_t ServerSai::logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + return m_sai->logSet(api, log_level); +} + +void ServerSai::serverThreadFunction() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("begin"); + + swss::Select s; + + s.addSelectable(m_selectableChannel.get()); + s.addSelectable(&m_serverThreadThreadShouldEndEvent); + + while (m_runServerThread) + { + swss::Selectable *sel; + + int result = s.select(&sel); + + if (sel == &m_serverThreadThreadShouldEndEvent) + { + // user requested end server thread + break; + } + + if (result == swss::Select::OBJECT) + { + processEvent(*m_selectableChannel.get()); + } + else + { + SWSS_LOG_ERROR("select failed: %s", swss::Select::resultToString(result).c_str()); + } + } + + SWSS_LOG_NOTICE("end"); +} + +void ServerSai::processEvent( + _In_ syncd::SelectableChannel& consumer) +{ + MUTEX(); + SWSS_LOG_ENTER(); + + if (!m_apiInitialized) + { + SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__); + + return; + } + + do + { + swss::KeyOpFieldsValuesTuple kco; + + consumer.pop(kco, false); + + processSingleEvent(kco); + } + while (!consumer.empty()); +} + +sai_status_t ServerSai::processSingleEvent( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& key = kfvKey(kco); + auto& op = kfvOp(kco); + + SWSS_LOG_INFO("key: %s op: %s", key.c_str(), op.c_str()); + + if (key.length() == 0) + { + SWSS_LOG_DEBUG("no elements in m_buffer"); + + return SAI_STATUS_SUCCESS; + } + + if (op == REDIS_ASIC_STATE_COMMAND_CREATE) + return processQuadEvent(SAI_COMMON_API_CREATE, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_REMOVE) + return processQuadEvent(SAI_COMMON_API_REMOVE, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_SET) + return processQuadEvent(SAI_COMMON_API_SET, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_GET) + return processQuadEvent(SAI_COMMON_API_GET, kco); + +// TODO implement +// if (op == REDIS_ASIC_STATE_COMMAND_BULK_CREATE) +// return processBulkQuadEvent(SAI_COMMON_API_BULK_CREATE, kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_BULK_REMOVE) +// return processBulkQuadEvent(SAI_COMMON_API_BULK_REMOVE, kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_BULK_SET) +// return processBulkQuadEvent(SAI_COMMON_API_BULK_SET, kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_NOTIFY) +// return processNotifySyncd(kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_GET_STATS) +// return processGetStatsEvent(kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_CLEAR_STATS) +// return processClearStatsEvent(kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_FLUSH) +// return processFdbFlush(kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_QUERY) +// return processAttrCapabilityQuery(kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_QUERY) +// return processAttrEnumValuesCapabilityQuery(kco); +// +// if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) +// return processObjectTypeGetAvailabilityQuery(kco); + + SWSS_LOG_THROW("event op '%s' is not implemented, FIXME", op.c_str()); +} + +sai_status_t ServerSai::processQuadEvent( + _In_ sai_common_api_t api, + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string& key = kfvKey(kco); + const std::string& op = kfvOp(kco); + + const std::string& strObjectId = key.substr(key.find(":") + 1); + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(key, metaKey); + + if (!sai_metadata_is_object_type_valid(metaKey.objecttype)) + { + SWSS_LOG_THROW("invalid object type %s", key.c_str()); + } + + SWSS_LOG_NOTICE("got server request: %s : %s", key.c_str(), op.c_str()); + + auto& values = kfvFieldsValues(kco); + + for (auto& v: values) + { + SWSS_LOG_NOTICE(" - attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); + } + + SaiAttributeList list(metaKey.objecttype, values, false); + + sai_attribute_t *attr_list = list.get_attr_list(); + uint32_t attr_count = list.get_attr_count(); + + /* + * NOTE: Need to translate client notification pointers to sairedis + * pointers since they reside in different memory space. + */ + + if (metaKey.objecttype == SAI_OBJECT_TYPE_SWITCH && (api == SAI_COMMON_API_CREATE || api == SAI_COMMON_API_SET)) + { + /* + * Since we will be operating on existing switch, it may happen that + * client will set notification pointer, which is not actually set in + * sairedis server. Then we would need to take extra steps in server + * to forward those notifications. We could also drop notification + * request on clients side, since it would be possible only to set only + * existing pointers. + * + * TODO: must be done per switch, and switch may not exists yet + */ + + // TODO m_handler->updateNotificationsPointers(attr_count, attr_list); + SWSS_LOG_ERROR("TODO, update notification pointers, FIXME!"); + } + + auto info = sai_metadata_get_object_type_info(metaKey.objecttype); + + sai_status_t status; + + // NOTE: for create api (even switch) passed oid will be SAI_NULL_OBJECT_ID + + sai_object_id_t oid = SAI_NULL_OBJECT_ID; + + if (info->isnonobjectid) + { + status = processEntry(metaKey, api, attr_count, attr_list); + } + else + { + sai_deserialize_object_id(strObjectId, oid); + + // In case of create oid object, client don't know what will be created + // object id before it receive it, so instead of that it will transfer + // switch id in that place since switch id is needed to create other + // object in the first place. If create method is for switch, this + // field will be SAI_NULL_OBJECT_ID. + + sai_object_id_t switchOid = oid; + + status = processOid(metaKey.objecttype, oid, switchOid, api, attr_count, attr_list); + } + + if (api == SAI_COMMON_API_GET) + { + sendGetResponse(metaKey.objecttype, strObjectId, status, attr_count, attr_list); + } + else if (status != SAI_STATUS_SUCCESS) + { + sendApiResponse(api, status, oid); + + SWSS_LOG_ERROR("api failed: %s (%s): %s", + key.c_str(), + op.c_str(), + sai_serialize_status(status).c_str()); + + for (const auto &v: values) + { + SWSS_LOG_ERROR("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); + } + } + else // non GET api, status is SUCCESS + { + sendApiResponse(api, status, oid); + } + + return status; +} + +void ServerSai::sendApiResponse( + _In_ sai_common_api_t api, + _In_ sai_status_t status, + _In_ sai_object_id_t oid) +{ + SWSS_LOG_ENTER(); + + switch (api) + { + case SAI_COMMON_API_CREATE: + case SAI_COMMON_API_REMOVE: + case SAI_COMMON_API_SET: + break; + + default: + SWSS_LOG_THROW("api %s not supported by this function", + sai_serialize_common_api(api).c_str()); + } + + std::vector entry; + + if (api == SAI_COMMON_API_CREATE) + { + // in case of create api, we need to return OID value that was created + // to the client + + entry.emplace_back("oid", sai_serialize_object_id(oid)); + } + + std::string strStatus = sai_serialize_status(status); + + SWSS_LOG_INFO("sending response for %s api with status: %s", + sai_serialize_common_api(api).c_str(), + strStatus.c_str()); + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); +} + +void ServerSai::sendGetResponse( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ sai_status_t status, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + entry = SaiAttributeList::serialize_attr_list( + objectType, + attr_count, + attr_list, + false); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + /* + * In this case we got correct values for list, but list was too small + * so serialize only count without list itself, sairedis will need to + * take this into account when deserialize. + * + * If there was a list somewhere, count will be changed to actual value + * different attributes can have different lists, many of them may + * serialize only count, and will need to support that on the receiver. + */ + + entry = SaiAttributeList::serialize_attr_list( + objectType, + attr_count, + attr_list, + true); + } + else + { + /* + * Some other error, don't send attributes at all. + */ + } + + for (const auto &e: entry) + { + SWSS_LOG_DEBUG("attr: %s: %s", fvField(e).c_str(), fvValue(e).c_str()); + } + + std::string strStatus = sai_serialize_status(status); + + SWSS_LOG_INFO("sending response for GET api with status: %s", strStatus.c_str()); + + /* + * Since we have only one get at a time, we don't have to serialize object + * type and object id, only get status is required to be returned. Get + * response will not put any data to table, only queue is used. + */ + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + SWSS_LOG_INFO("response for GET api was send"); +} + +sai_status_t ServerSai::processEntry( + _In_ sai_object_meta_key_t metaKey, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + switch (api) + { + case SAI_COMMON_API_CREATE: + return m_sai->create(metaKey, SAI_NULL_OBJECT_ID, attr_count, attr_list); + + case SAI_COMMON_API_REMOVE: + return m_sai->remove(metaKey); + + case SAI_COMMON_API_SET: + return m_sai->set(metaKey, attr_list); + + case SAI_COMMON_API_GET: + return m_sai->get(metaKey, attr_count, attr_list); + + default: + + SWSS_LOG_THROW("api %s not supported", sai_serialize_common_api(api).c_str()); + } +} + +sai_status_t ServerSai::processOid( + _In_ sai_object_type_t objectType, + _Inout_ sai_object_id_t& oid, + _In_ sai_object_id_t switchOid, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("calling %s for %s", + sai_serialize_common_api(api).c_str(), + sai_serialize_object_type(objectType).c_str()); + + auto info = sai_metadata_get_object_type_info(objectType); + + if (info->isnonobjectid) + { + SWSS_LOG_THROW("passing non object id %s as generic object", info->objecttypename); + } + + switch (api) + { + case SAI_COMMON_API_CREATE: + return m_sai->create(objectType, &oid, switchOid, attr_count, attr_list); + + case SAI_COMMON_API_REMOVE: + return m_sai->remove(objectType, oid); + + case SAI_COMMON_API_SET: + return m_sai->set(objectType, oid, attr_list); + + case SAI_COMMON_API_GET: + return m_sai->get(objectType, oid, attr_count, attr_list); + + default: + + SWSS_LOG_THROW("common api (%s) is not implemented", sai_serialize_common_api(api).c_str()); + } +} diff --git a/lib/src/ZeroMQChannel.cpp b/lib/src/ZeroMQChannel.cpp index 9d05ca228a63..bb38091f4cbe 100644 --- a/lib/src/ZeroMQChannel.cpp +++ b/lib/src/ZeroMQChannel.cpp @@ -223,9 +223,10 @@ void ZeroMQChannel::set( if (rc <= 0) { - SWSS_LOG_THROW("zmq_send failed, on endpoint %s, zmqerrno: %d", + SWSS_LOG_THROW("zmq_send failed, on endpoint %s, zmqerrno: %d: %s", m_endpoint.c_str(), - zmq_errno()); + zmq_errno(), + zmq_strerror(zmq_errno())); } } @@ -249,9 +250,10 @@ void ZeroMQChannel::del( if (rc <= 0) { - SWSS_LOG_THROW("zmq_send failed, on endpoint %s, zmqerrno: %d", + SWSS_LOG_THROW("zmq_send failed, on endpoint %s, zmqerrno: %d: %s", m_endpoint.c_str(), - zmq_errno()); + zmq_errno(), + zmq_strerror(zmq_errno())); } } diff --git a/lib/src/sai_redis_interfacequery.cpp b/lib/src/sai_redis_interfacequery.cpp index 59c2edea4e26..3481c93fb43a 100644 --- a/lib/src/sai_redis_interfacequery.cpp +++ b/lib/src/sai_redis_interfacequery.cpp @@ -1,8 +1,9 @@ #include "sai_redis.h" +#include "ClientServerSai.h" using namespace sairedis; -std::shared_ptr redis_sai = std::make_shared(); +std::shared_ptr redis_sai = std::make_shared(); sai_status_t sai_api_initialize( _In_ uint64_t flags, diff --git a/meta/Meta.cpp b/meta/Meta.cpp index 923857ae572f..2e7dfdeacf0a 100644 --- a/meta/Meta.cpp +++ b/meta/Meta.cpp @@ -6648,12 +6648,31 @@ void Meta::meta_generic_validation_post_create( { SWSS_LOG_ENTER(); + bool connectToSwitch = false; + + if (meta_key.objecttype == SAI_OBJECT_TYPE_SWITCH) + { + auto attr = sai_metadata_get_attr_by_id(SAI_SWITCH_ATTR_INIT_SWITCH, attr_count, attr_list); + + if (attr && attr->value.booldata == false) + { + SWSS_LOG_NOTICE("connecting to existing switch %s", + sai_serialize_object_id(switch_id).c_str()); + + connectToSwitch = true; + } + } + if (m_saiObjectCollection.objectExists(meta_key)) { if (m_warmBoot && meta_key.objecttype == SAI_OBJECT_TYPE_SWITCH) { SWSS_LOG_NOTICE("post switch create after WARM BOOT"); } + else if (connectToSwitch) + { + // ok, object already exists since we are connecting to existing switch + } else { SWSS_LOG_ERROR("object key %s already exists (vendor bug?)", @@ -6667,6 +6686,10 @@ void Meta::meta_generic_validation_post_create( { SWSS_LOG_NOTICE("skipping create switch on WARM BOOT since it was already created"); } + else if (connectToSwitch) + { + // don't create object, since it already exists and we are connecting to existing switch + } else { m_saiObjectCollection.createObject(meta_key); @@ -6753,6 +6776,10 @@ void Meta::meta_generic_validation_post_create( { SWSS_LOG_NOTICE("skip insert switch reference insert in WARM_BOOT"); } + else if (connectToSwitch) + { + // don't create object reference, since we are connecting to existing switch + } else { m_oids.objectReferenceInsert(oid); diff --git a/saiplayer/saiplayer_main.cpp b/saiplayer/saiplayer_main.cpp index 381c0cab5c27..fd26e8685d3b 100644 --- a/saiplayer/saiplayer_main.cpp +++ b/saiplayer/saiplayer_main.cpp @@ -1,7 +1,7 @@ #include "SaiPlayer.h" #include "CommandLineOptionsParser.h" -#include "../lib/inc/Sai.h" +#include "../lib/inc/ClientServerSai.h" #include "../syncd/MetadataLogger.h" #include "swss/logger.h" @@ -20,7 +20,7 @@ int main(int argc, char **argv) auto commandLineOptions = CommandLineOptionsParser::parseCommandLine(argc, argv); - auto sai = std::make_shared(); + auto sai = std::make_shared(); auto player = std::make_shared(sai, commandLineOptions); diff --git a/syncd/ZeroMQSelectableChannel.cpp b/syncd/ZeroMQSelectableChannel.cpp index f0da72e9f74d..ddaadb29e819 100644 --- a/syncd/ZeroMQSelectableChannel.cpp +++ b/syncd/ZeroMQSelectableChannel.cpp @@ -8,7 +8,8 @@ #define ZMQ_RESPONSE_BUFFER_SIZE (4*1024*1024) -#define ZMQ_POLL_TIMEOUT (2*60*1000) +//#define ZMQ_POLL_TIMEOUT (2*60*1000) +#define ZMQ_POLL_TIMEOUT (1000) using namespace syncd; @@ -23,6 +24,8 @@ ZeroMQSelectableChannel::ZeroMQSelectableChannel( { SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("binding on %s", endpoint.c_str()); + m_buffer.resize(ZMQ_RESPONSE_BUFFER_SIZE); m_context = zmq_ctx_new();; @@ -62,11 +65,11 @@ ZeroMQSelectableChannel::~ZeroMQSelectableChannel() zmq_close(m_socket); zmq_ctx_destroy(m_context); - SWSS_LOG_NOTICE("ending zmq poll thread"); + SWSS_LOG_NOTICE("ending zmq poll thread for channel %s", m_endpoint.c_str()); m_zmlPollThread->join(); - SWSS_LOG_NOTICE("ended zmq poll thread"); + SWSS_LOG_NOTICE("ended zmq poll thread for channel %s", m_endpoint.c_str()); } void ZeroMQSelectableChannel::zmqPollThread() @@ -87,7 +90,10 @@ void ZeroMQSelectableChannel::zmqPollThread() int rc = zmq_poll(items, 1, ZMQ_POLL_TIMEOUT); if (m_runThread == false) + { + SWSS_LOG_NOTICE("ending pool thread, since run is false"); break; + } if (rc <= 0 && zmq_errno() == ETERM) { @@ -97,7 +103,7 @@ void ZeroMQSelectableChannel::zmqPollThread() if (rc == 0) { - SWSS_LOG_INFO("zmq_poll: no events, continue"); + SWSS_LOG_DEBUG("zmq_poll: no events, continue"); continue; } @@ -201,9 +207,10 @@ void ZeroMQSelectableChannel::set( if (rc <= 0) { - SWSS_LOG_THROW("zmq_send failed, on endpoint %s, zmqerrno: %d", + SWSS_LOG_THROW("zmq_send failed, on endpoint %s, zmqerrno: %d: %s", m_endpoint.c_str(), - zmq_errno()); + zmq_errno(), + zmq_strerror(zmq_errno())); } } diff --git a/tests/BCM56850.pl b/tests/BCM56850.pl index 3fab3d397878..fd2d34671d2e 100755 --- a/tests/BCM56850.pl +++ b/tests/BCM56850.pl @@ -660,8 +660,30 @@ sub test_macsec_p2p_establishment play "test_macsec_p2p_establishment.rec"; } +sub test_sairedis_client +{ + fresh_start; + + # saiplayer here is acting as OA + + system("../saiplayer/saiplayer -u $utils::DIR/client_switch.rec &"); + + sleep 1; + + `./testclient`; + + `killall -9 saiplayer`; + + if ($? != 0) + { + print color('red') . "test client failed" . color('reset') . "\n"; + exit 1; + } +} + # RUN TESTS +test_sairedis_client; test_macsec_p2p_establishment; test_no_lag_label; test_lag_label; diff --git a/tests/BCM56850/client_switch.rec b/tests/BCM56850/client_switch.rec new file mode 100644 index 000000000000..368458f0c3cc --- /dev/null +++ b/tests/BCM56850/client_switch.rec @@ -0,0 +1,7 @@ +2020-12-31.21:34:31.861834|a|INIT_VIEW +2020-12-31.21:34:31.862379|A|SAI_STATUS_SUCCESS +2020-12-31.21:34:31.864591|c|SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000|SAI_SWITCH_ATTR_INIT_SWITCH=true +2020-12-31.21:34:41.215885|a|APPLY_VIEW +2020-12-31.21:34:41.216326|A|SAI_STATUS_SUCCESS +2020-12-31.21:34:41.216326|#|give 5 seconds to client to connect to existing switch +2020-12-31.21:34:41.216326|@|5000 diff --git a/tests/Makefile.am b/tests/Makefile.am index d474ca137838..7d1ff2aa0d77 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/vslib/inc -I$(top_srcdir)/lib/inc -I$(top_srcdir)/SAI/inc -I$(top_srcdir)/SAI/meta -I$(top_srcdir)/SAI/experimental -bin_PROGRAMS = vssyncd tests +bin_PROGRAMS = vssyncd tests testclient if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -27,4 +27,12 @@ tests_LDADD = -lhiredis -lswsscommon -lpthread \ -L$(top_srcdir)/meta/.libs \ -lsaimetadata -lsaimeta -lzmq +testclient_SOURCES = testclient.cpp +testclient_CPPFLAGS = $(DBGFLAGS) $(AM_CPPFLAGS) $(CFLAGS_COMMON) +testclient_LDADD = -lhiredis -lswsscommon -lpthread \ + $(top_srcdir)/lib/src/libsairedis.la \ + $(top_srcdir)/syncd/libSyncd.a \ + -L$(top_srcdir)/meta/.libs \ + -lsaimetadata -lsaimeta -lzmq + TESTS = aspellcheck.pl conflictnames.pl swsslogentercheck.sh tests BCM56850.pl MLNX2700.pl diff --git a/tests/aspell.en.pws b/tests/aspell.en.pws index 4e2c20902dac..89a7bed3efab 100644 --- a/tests/aspell.en.pws +++ b/tests/aspell.en.pws @@ -335,3 +335,5 @@ zero zeromq zmq ZMQ +idx +kco diff --git a/tests/testclient.cpp b/tests/testclient.cpp new file mode 100644 index 000000000000..474923b7359e --- /dev/null +++ b/tests/testclient.cpp @@ -0,0 +1,206 @@ +#include "sairedis.h" + +#include "../syncd/ServiceMethodTable.h" + +#include "meta/sai_serialize.h" + +#include +#include + +using namespace std::placeholders; + +class TestClient +{ + public: + + TestClient(); + + virtual ~TestClient(); + + public: + + void test_create_vlan(); + + private: + + int profileGetNextValue( + _In_ sai_switch_profile_id_t profile_id, + _Out_ const char** variable, + _Out_ const char** value); + + const char* profileGetValue( + _In_ sai_switch_profile_id_t profile_id, + _In_ const char* variable); + + private: + + std::map m_profileMap; + + std::map::iterator m_profileIter; + + syncd::ServiceMethodTable m_smt; + + sai_service_method_table_t m_test_services; +}; + +TestClient::TestClient() +{ + SWSS_LOG_ENTER(); + + // empty intentionally +} + +TestClient::~TestClient() +{ + SWSS_LOG_ENTER(); + + // empty intentionally +} + +const char* TestClient::profileGetValue( + _In_ sai_switch_profile_id_t profile_id, + _In_ const char* variable) +{ + SWSS_LOG_ENTER(); + + if (variable == NULL) + { + SWSS_LOG_WARN("variable is null"); + return NULL; + } + + auto it = m_profileMap.find(variable); + + if (it == m_profileMap.end()) + { + SWSS_LOG_NOTICE("%s: NULL", variable); + return NULL; + } + + SWSS_LOG_NOTICE("%s: %s", variable, it->second.c_str()); + + return it->second.c_str(); +} + +int TestClient::profileGetNextValue( + _In_ sai_switch_profile_id_t profile_id, + _Out_ const char** variable, + _Out_ const char** value) +{ + SWSS_LOG_ENTER(); + + if (value == NULL) + { + SWSS_LOG_INFO("resetting profile map iterator"); + + m_profileIter = m_profileMap.begin(); + return 0; + } + + if (variable == NULL) + { + SWSS_LOG_WARN("variable is null"); + return -1; + } + + if (m_profileIter == m_profileMap.end()) + { + SWSS_LOG_INFO("iterator reached end"); + return -1; + } + + *variable = m_profileIter->first.c_str(); + *value = m_profileIter->second.c_str(); + + SWSS_LOG_INFO("key: %s:%s", *variable, *value); + + m_profileIter++; + + return 0; +} + +#define ASSERT_TRUE(x) \ + if (!(x)) \ +{\ + SWSS_LOG_THROW("assert true failed '%s', line: %d", # x, __LINE__);\ +} + +#define ASSERT_SUCCESS(x) \ + if (x != SAI_STATUS_SUCCESS) \ +{\ + SWSS_LOG_THROW("expected success, line: %d, got: %s", __LINE__, sai_serialize_status(x).c_str());\ +} + +void TestClient::test_create_vlan() +{ + SWSS_LOG_ENTER(); + + m_profileMap.clear(); + + m_profileMap[SAI_REDIS_KEY_ENABLE_CLIENT] = "true"; // act as a client + + m_profileIter = m_profileMap.begin(); + + m_smt.profileGetValue = std::bind(&TestClient::profileGetValue, this, _1, _2); + m_smt.profileGetNextValue = std::bind(&TestClient::profileGetNextValue, this, _1, _2, _3); + + m_test_services = m_smt.getServiceMethodTable(); + + ASSERT_SUCCESS(sai_api_initialize(0, &m_test_services)); + + sai_switch_api_t* switch_api; + + ASSERT_SUCCESS(sai_api_query(SAI_API_SWITCH, (void**)&switch_api)); + + sai_attribute_t attr; + + // connect to existing switch + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = false; + + sai_object_id_t switch_id = SAI_NULL_OBJECT_ID; + + ASSERT_SUCCESS(switch_api->create_switch(&switch_id, 1, &attr)); + + ASSERT_TRUE(switch_id != SAI_NULL_OBJECT_ID); + + SWSS_LOG_NOTICE("switchId: %s", sai_serialize_object_id(switch_id).c_str()); + + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + + ASSERT_SUCCESS(switch_api->get_switch_attribute(switch_id, 1, &attr)); + + SWSS_LOG_NOTICE("got VRID: %s", sai_serialize_object_id(attr.value.oid).c_str()); + + sai_vlan_api_t* vlan_api; + + ASSERT_SUCCESS(sai_api_query(SAI_API_VLAN, (void**)&vlan_api)); + + attr.id = SAI_VLAN_ATTR_VLAN_ID; + attr.value.u16 = 200; + + sai_object_id_t vlan_id; + + ASSERT_SUCCESS(vlan_api->create_vlan(&vlan_id, switch_id, 1, &attr)); + + ASSERT_TRUE(vlan_id != SAI_NULL_OBJECT_ID); + + ASSERT_SUCCESS(vlan_api->remove_vlan(vlan_id)); + + ASSERT_SUCCESS(sai_api_uninitialize()); +} + +int main() +{ + swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG); + + SWSS_LOG_ENTER(); + + swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_NOTICE); + + TestClient tc; + + tc.test_create_vlan(); + + return EXIT_SUCCESS; +}