From 9aed2ff818ea74ab30d8c5aa074bd536a428da69 Mon Sep 17 00:00:00 2001 From: Kamil Cudnik Date: Tue, 15 Jun 2021 12:20:42 +0200 Subject: [PATCH] [sairedis] Add support for client server architecture (#838) With this feature libsairedis will have ability to act as a client and server, for example as client (pbhorch) can connect to existing server (OA) and call SAI api in server scope. Client connects over zmq channel and can be running in separate docker, connecting over ipc pipe or tcp. --- lib/inc/ClientSai.h | 394 ++++++ lib/inc/ClientServerSai.h | 259 ++++ lib/inc/RedisRemoteSaiInterface.h | 10 +- lib/inc/Sai.h | 1 - lib/inc/ServerSai.h | 312 +++++ lib/inc/sai_redis.h | 6 +- lib/inc/sairedis.h | 11 + lib/src/ClientSai.cpp | 1706 ++++++++++++++++++++++++++ lib/src/ClientServerSai.cpp | 571 +++++++++ lib/src/Context.cpp | 11 + lib/src/Makefile.am | 5 + lib/src/RedisRemoteSaiInterface.cpp | 12 +- lib/src/Sai.cpp | 2 + lib/src/ServerSai.cpp | 988 +++++++++++++++ lib/src/ZeroMQChannel.cpp | 10 +- lib/src/sai_redis_interfacequery.cpp | 3 +- meta/Meta.cpp | 27 + saiplayer/saiplayer_main.cpp | 4 +- syncd/ZeroMQSelectableChannel.cpp | 19 +- tests/BCM56850.pl | 22 + tests/BCM56850/client_switch.rec | 7 + tests/Makefile.am | 10 +- tests/aspell.en.pws | 2 + tests/testclient.cpp | 206 ++++ 24 files changed, 4575 insertions(+), 23 deletions(-) create mode 100644 lib/inc/ClientSai.h create mode 100644 lib/inc/ClientServerSai.h create mode 100644 lib/inc/ServerSai.h create mode 100644 lib/src/ClientSai.cpp create mode 100644 lib/src/ClientServerSai.cpp create mode 100644 lib/src/ServerSai.cpp create mode 100644 tests/BCM56850/client_switch.rec create mode 100644 tests/testclient.cpp 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; +}