lib/Recorder.cpp (909 lines of code) (raw):

#include "Recorder.h" #include "meta/sai_serialize.h" #include "meta/SaiAttributeList.h" #include "meta/Globals.h" #include "meta/SaiInterface.h" #include <unistd.h> #include <inttypes.h> #include <cstring> #include <vector> #include <fstream> using namespace sairedis; using namespace saimeta; std::vector<swss::FieldValueTuple> 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); #define MUTEX() std::lock_guard<std::mutex> _lock(m_mutex) #define DEFAULT_RECORDING_FILE_NAME "sairedis.rec" Recorder::Recorder() { SWSS_LOG_ENTER(); m_recordingFileName = DEFAULT_RECORDING_FILE_NAME; m_recordingOutputDirectory = "."; m_performLogRotate = false; m_enabled = false; m_recordStats = true; } Recorder::~Recorder() { SWSS_LOG_ENTER(); stopRecording(); } bool Recorder::setRecordingOutputDirectory( _In_ const sai_attribute_t &attr) { SWSS_LOG_ENTER(); if (attr.value.s8list.count == 0) { m_recordingOutputDirectory = "."; SWSS_LOG_NOTICE("setting recording directory to: %s", m_recordingOutputDirectory.c_str()); requestLogRotate(); return true; } if (attr.value.s8list.list == NULL) { SWSS_LOG_ERROR("list pointer is NULL"); return false; } size_t len = strnlen((const char *)attr.value.s8list.list, attr.value.s8list.count); if (len != (size_t)attr.value.s8list.count) { SWSS_LOG_ERROR("count (%u) is different than strnlen (%zu)", attr.value.s8list.count, len); return false; } std::string dir((const char*)attr.value.s8list.list, len); int result = access(dir.c_str(), W_OK); if (result != 0) { SWSS_LOG_ERROR("can't access dir '%s' for writing", dir.c_str()); return false; } m_recordingOutputDirectory = dir; // perform log rotate when log directory gets changed requestLogRotate(); return true; } bool Recorder::setRecordingFilename( _In_ const sai_attribute_t &attr) { SWSS_LOG_ENTER(); if (attr.value.s8list.count == 0) { m_recordingFileName = DEFAULT_RECORDING_FILE_NAME; SWSS_LOG_NOTICE("setting recording filename to default filename: %s", m_recordingFileName.c_str()); requestLogRotate(); return true; } if (attr.value.s8list.list == NULL) { SWSS_LOG_ERROR("list pointer is NULL"); return false; } size_t len = strnlen((const char *)attr.value.s8list.list, attr.value.s8list.count); if (len != (size_t)attr.value.s8list.count) { SWSS_LOG_ERROR("count (%u) is different than strnlen (%zu)", attr.value.s8list.count, len); return false; } std::string filename((const char*)attr.value.s8list.list, len); /// Stop the recording with old file before updating the filename if (m_enabled) { stopRecording(); } m_recordingFileName = filename; SWSS_LOG_NOTICE("setting recording filename : %s", m_recordingFileName.c_str()); /// Start recording with new file if (m_enabled) { startRecording(); } return true; } void Recorder::enableRecording( _In_ bool enabled) { SWSS_LOG_ENTER(); m_enabled = enabled; stopRecording(); if (enabled) { startRecording(); } } void Recorder::recordLine( _In_ const std::string& line) { MUTEX(); SWSS_LOG_ENTER(); if (!m_enabled) { return; } if (m_ofstream.is_open()) { m_ofstream << getTimestamp() << "|" << line << std::endl; } } void Recorder::requestLogRotate() { SWSS_LOG_ENTER(); recordingFileReopen(); /* double check since reopen could fail */ recordLine("#|logrotate on: " + m_recordingFile); } void Recorder::recordingFileReopen() { MUTEX(); SWSS_LOG_ENTER(); m_ofstream.close(); /* * On log rotate we will use the same file name, we are assuming that * logrotate daemon move filename to filename.1 and we will create new * empty file here. */ m_recordingFile = m_recordingOutputDirectory + "/" + m_recordingFileName; m_ofstream.open(m_recordingFile, std::ofstream::out | std::ofstream::app); if (!m_ofstream.is_open()) { SWSS_LOG_ERROR("failed to open recording file %s: %s", m_recordingFile.c_str(), strerror(errno)); return; } } void Recorder::startRecording() { SWSS_LOG_ENTER(); m_recordingFile = m_recordingOutputDirectory + "/" + m_recordingFileName; { MUTEX(); m_ofstream.open(m_recordingFile, std::ofstream::out | std::ofstream::app); if (!m_ofstream.is_open()) { SWSS_LOG_ERROR("failed to open recording file %s: %s", m_recordingFile.c_str(), strerror(errno)); return; } } recordLine("#|recording on: " + m_recordingFile); SWSS_LOG_NOTICE("started recording: %s", m_recordingFileName.c_str()); } void Recorder::stopRecording() { MUTEX(); SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("stopped recording"); if (m_ofstream.is_open()) { m_ofstream.close(); SWSS_LOG_NOTICE("closed recording file: %s", m_recordingFileName.c_str()); } } std::string Recorder::getTimestamp() { SWSS_LOG_ENTER(); char buffer[64]; struct timeval tv; gettimeofday(&tv, NULL); struct tm now; localtime_r(&tv.tv_sec, &now); size_t size = strftime(buffer, 32, "%Y-%m-%d.%T.", &now); snprintf(&buffer[size], 32, "%06ld", tv.tv_usec); return std::string(buffer); } // SAI APIs record functions void Recorder::recordFlushFdbEntries( _In_ sai_object_id_t switchId, _In_ uint32_t attrCount, _In_ const sai_attribute_t *attrList) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> entry = SaiAttributeList::serialize_attr_list( SAI_OBJECT_TYPE_FDB_FLUSH, attrCount, attrList, false); std::string serializedObjectType = sai_serialize_object_type(SAI_OBJECT_TYPE_FDB_FLUSH); // NOTE ! we actually give switch ID since FLUSH is not real object std::string key = serializedObjectType + ":" + sai_serialize_object_id(switchId); SWSS_LOG_NOTICE("flush key: %s, fields: %lu", key.c_str(), entry.size()); recordFlushFdbEntries(key, entry); } void Recorder::recordFlushFdbEntries( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("f|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordFlushFdbEntriesResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); recordLine("F|" + sai_serialize_status(status)); } void Recorder::recordQueryAttributeCapability( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("q|attribute_capability|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordQueryAttributeCapabilityResponse( _In_ sai_status_t status, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("Q|attribute_capability|" + sai_serialize_status(status) + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordQueryAttributeEnumValuesCapability( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("q|attribute_enum_values_capability|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordQueryAttributeEnumValuesCapabilityResponse( _In_ sai_status_t status, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("Q|attribute_enum_values_capability|" + sai_serialize_status(status) + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordObjectTypeGetAvailability( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("q|object_type_get_availability|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordObjectTypeGetAvailabilityResponse( _In_ sai_status_t status, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("Q|object_type_get_availability|" + sai_serialize_status(status) + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordQueryStatsCapability( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); recordLine("q|stats_capability|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordQueryStatsCapabilityResponse( _In_ sai_status_t status, _In_ const std::string& arguments) { SWSS_LOG_ENTER(); recordLine("Q|stats_capability|" + sai_serialize_status(status) + "|" + arguments); } void Recorder::recordNotifySyncd( _In_ const std::string& key) { SWSS_LOG_ENTER(); // lower case 'a' stands for notify syncd request recordLine("a|" + key); } void Recorder::recordNotifySyncdResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); // capital 'A' stands for notify syncd response recordLine("A|" + sai_serialize_status(status)); } void Recorder::recordGenericCreate( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); // lower case 'c' stands for create api recordLine("c|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordGenericCreateResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordGenericCreateResponse( _In_ sai_status_t status, _In_ sai_object_id_t objectId) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordBulkGenericCreate( _In_ const std::string& objectType, _In_ const std::vector<swss::FieldValueTuple>& entriesWithStatus) { SWSS_LOG_ENTER(); std::string joined; for (const auto &e: entriesWithStatus) { // ||obj_id|attr=val|attr=val|status||obj_id|attr=val|attr=val|status joined += "||" + fvField(e) + "|" + fvValue(e); } // capital 'C' stands for bulk CREATE operation. recordLine("C|" + objectType + joined); } void Recorder::recordBulkGenericCreateResponse( _In_ sai_status_t status, _In_ uint32_t objectCount, _In_ const sai_status_t *objectStatuses) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordGenericRemove( _In_ sai_object_type_t objectType, _In_ sai_object_id_t objectId) { SWSS_LOG_ENTER(); auto key = sai_serialize_object_type(objectType) + ":" + sai_serialize_object_id(objectId); // lower case 'r' stands for REMOVE api recordLine("r|" + key); } void Recorder::recordGenericRemove( _In_ const std::string& key) { SWSS_LOG_ENTER(); // lower case 'r' stands for REMOVE api recordLine("r|" + key); } void Recorder::recordGenericRemoveResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordBulkGenericRemove( _In_ const std::string& objectType, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); std::string joined; // TODO revisit for (const auto &e: arguments) { // ||obj_id||obj_id||... joined += "||" + fvField(e); } // capital 'R' stands for bulk REMOVE operation. recordLine("R|" + objectType + joined); } void Recorder::recordBulkGenericRemoveResponse( _In_ sai_status_t status, _In_ uint32_t objectCount, _In_ const sai_status_t *objectStatuses) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordGenericSet( _In_ sai_object_type_t objectType, _In_ sai_object_id_t objectId, _In_ const sai_attribute_t *attr) { SWSS_LOG_ENTER(); recordSet(objectType, sai_serialize_object_id(objectId), attr); } void Recorder::recordGenericSet( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); // lower case 's' stands for SET api recordLine("s|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordGenericCounterPolling( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); // lower case 'p' stands for counter Polling recordLine("p|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordGenericSetResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordBulkGenericSet( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); std::string joined; for (const auto &e: arguments) { // ||obj_id|attr=val|status||obj_id|attr=val|status joined += "||" + fvField(e) + "|" + fvValue(e); } // capital 'S' stands for bulk SET operation. recordLine("S|" + key + joined); } void Recorder::recordBulkGenericSetResponse( _In_ sai_status_t status, _In_ uint32_t objectCount, _In_ const sai_status_t *objectStatuses) { SWSS_LOG_ENTER(); // TODO currently empty since used in async mode, but we should log this in // synchronous mode, and we could use "G" from GET api as response } void Recorder::recordGenericGet( _In_ sai_object_type_t objectType, _In_ sai_object_id_t objectId, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { SWSS_LOG_ENTER(); recordGet( objectType, sai_serialize_object_id(objectId), attr_count, attr_list); } void Recorder::recordGenericGet( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); // lower case 'g' stands for GET api recordLine("g|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordGenericGetResponse( _In_ sai_status_t status, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); // capital 'G' stands for GET api response recordLine("G|" + sai_serialize_status(status) + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordBulkGenericGet( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); std::string joined; for (const auto &e: arguments) { joined += "||" + fvField(e) + "|" + fvValue(e); } // capital 'B' stands for Bulk GET operation. Note: 'G' already used for get response. recordLine("B|" + key + joined); } void Recorder::recordBulkGenericGetResponse( _In_ sai_status_t status, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); std::string joined; for (const auto &e: arguments) { joined += "||" + fvField(e) + "|" + fvValue(e); } recordLine("G|" + sai_serialize_status(status) + "|" + joined); } void Recorder::recordGenericGetStats( _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) { SWSS_LOG_ENTER(); if (!m_recordStats) return; 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()); recordGenericGetStats(key, entry); } void Recorder::recordGenericGetStats( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); if (!m_recordStats) return; recordLine("q|get_stats|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordGenericGetStatsResponse( _In_ sai_status_t status, _In_ uint32_t count, _In_ const uint64_t *counters) { SWSS_LOG_ENTER(); if (!m_recordStats) return; std::string joined; for (uint32_t idx = 0; idx < count; idx ++) { joined += "|" + std::to_string(counters[idx]); } recordLine("Q|get_stats|" + sai_serialize_status(status) + joined); } void Recorder::recordGenericClearStats( _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) { SWSS_LOG_ENTER(); if (!m_recordStats) return; 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); 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 clear stats key: %s, fields: %lu", key.c_str(), values.size()); recordGenericClearStats(key, values); } void Recorder::recordGenericClearStats( _In_ const std::string& key, _In_ const std::vector<swss::FieldValueTuple>& arguments) { SWSS_LOG_ENTER(); if (!m_recordStats) return; recordLine("q|clear_stats|" + key + "|" + Globals::joinFieldValues(arguments)); } void Recorder::recordGenericClearStatsResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); if (!m_recordStats) return; recordLine("Q|clear_stats|" + sai_serialize_status(status)); } void Recorder::recordNotification( _In_ const std::string &name, _In_ const std::string &serializedNotification, _In_ const std::vector<swss::FieldValueTuple> &values) { SWSS_LOG_ENTER(); recordLine("n|" + name + "|" + serializedNotification + "|" + Globals::joinFieldValues(values)); } void Recorder::recordRemove( _In_ sai_object_type_t objectType, _In_ const std::string& serializedObjectId) { SWSS_LOG_ENTER(); auto key = sai_serialize_object_type(objectType) + ":" + serializedObjectId; recordGenericRemove(key); } void Recorder::recordGenericCreate( _In_ sai_object_type_t objectType, _In_ sai_object_id_t objectId, // already allocated _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { SWSS_LOG_ENTER(); recordCreate( objectType, sai_serialize_object_id(objectId), attr_count, attr_list); } void Recorder::recordCreate( _In_ sai_object_type_t objectType, _In_ const std::string& serializedObjectId, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> entry = SaiAttributeList::serialize_attr_list( objectType, attr_count, attr_list, false); if (entry.size() == 0) { // 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 serializedObjectType = sai_serialize_object_type(objectType); std::string key = serializedObjectType + ":" + serializedObjectId; SWSS_LOG_DEBUG("generic create key: %s, fields: %" PRIu64, key.c_str(), entry.size()); recordGenericCreate(key, entry); } void Recorder::recordSet( _In_ sai_object_type_t objectType, _In_ const std::string &serializedObjectId, _In_ const sai_attribute_t *attr) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> entry = SaiAttributeList::serialize_attr_list( objectType, 1, attr, false); auto serializedObjectType = sai_serialize_object_type(objectType); auto key = serializedObjectType + ":" + serializedObjectId; SWSS_LOG_DEBUG("generic set key: %s, fields: %lu", key.c_str(), entry.size()); recordGenericSet(key, entry); } void Recorder::recordGet( _In_ sai_object_type_t objectType, _In_ const std::string &serializedObjectId, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> 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()); recordGenericGet(key, entry); } void Recorder::recordGenericGetResponse( _In_ sai_status_t status, _In_ sai_object_type_t objectType, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { SWSS_LOG_ENTER(); if (status == SAI_STATUS_SUCCESS) { auto entry = SaiAttributeList::serialize_attr_list( objectType, attr_count, attr_list, false); recordGenericGetResponse(status, entry); } else if (status == SAI_STATUS_BUFFER_OVERFLOW) { // will only record COUNT values for lists, since count is expected // values, and user buffer is not enough to return all from SAI auto entry = SaiAttributeList::serialize_attr_list( objectType, attr_count, attr_list, true); recordGenericGetResponse(status, entry); } else { recordGenericGetResponse(status, {}); } } #define DECLARE_RECORD_REMOVE_ENTRY(OT,ot) \ void Recorder::recordRemove( \ _In_ const sai_ ## ot ## _t* ot) \ { \ SWSS_LOG_ENTER(); \ recordRemove((sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \ sai_serialize_ ## ot(*ot)); \ } SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_RECORD_REMOVE_ENTRY); #define DECLARE_RECORD_CREATE_ENTRY(OT,ot) \ void Recorder::recordCreate( \ _In_ const sai_ ## ot ## _t* ot, \ _In_ uint32_t attr_count, \ _In_ const sai_attribute_t *attr_list) \ { \ SWSS_LOG_ENTER(); \ recordCreate((sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \ sai_serialize_ ## ot(*ot), attr_count, attr_list); \ } SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_RECORD_CREATE_ENTRY); #define DECLARE_RECORD_SET_ENTRY(OT,ot) \ void Recorder::recordSet( \ _In_ const sai_ ## ot ## _t* ot, \ _In_ const sai_attribute_t *attr) \ { \ SWSS_LOG_ENTER(); \ recordSet((sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \ sai_serialize_ ## ot(*ot), attr); \ } SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_RECORD_SET_ENTRY); #define DECLARE_RECORD_GET_ENTRY(OT,ot) \ void Recorder::recordGet( \ _In_ const sai_ ## ot ## _t* ot, \ _In_ uint32_t attr_count, \ _In_ const sai_attribute_t *attr_list) \ { \ SWSS_LOG_ENTER(); \ recordGet((sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \ sai_serialize_ ## ot(*ot), attr_count, attr_list); \ } SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_RECORD_GET_ENTRY); void Recorder::recordObjectTypeGetAvailability( _In_ sai_object_id_t switchId, _In_ sai_object_type_t objectType, _In_ uint32_t attrCount, _In_ const sai_attribute_t *attrList) { SWSS_LOG_ENTER(); auto key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + sai_serialize_object_id(switchId); auto values = SaiAttributeList::serialize_attr_list( objectType, attrCount, attrList, false); SWSS_LOG_DEBUG("Query arguments: switch: %s, attributes: %s", key.c_str(), Globals::joinFieldValues(values).c_str()); recordObjectTypeGetAvailability(key, values); } void Recorder::recordObjectTypeGetAvailabilityResponse( _In_ sai_status_t status, _In_ const uint64_t *count) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> values; values.push_back(swss::FieldValueTuple("COUNT", std::to_string(*count))); recordObjectTypeGetAvailabilityResponse(status, values); } void Recorder::recordQueryAttributeCapability( _In_ sai_object_id_t switchId, _In_ sai_object_type_t objectType, _In_ sai_attr_id_t attrId, _Out_ sai_attr_capability_t *Capability) { SWSS_LOG_ENTER(); 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", sai_serialize_object_type(objectType).c_str(), attrId); return; } auto key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + sai_serialize_object_id(switchId); auto object_type_str = sai_serialize_object_type(objectType); const std::string attr_id_str = meta->attridname; const std::vector<swss::FieldValueTuple> values = { swss::FieldValueTuple("OBJECT_TYPE", object_type_str), swss::FieldValueTuple("ATTR_ID", attr_id_str) }; SWSS_LOG_DEBUG("Query arguments: switch %s, object_type: %s, attribute: %s", key.c_str(), object_type_str.c_str(), meta->attridname); recordQueryAttributeCapability(key, values); } void Recorder::recordQueryAttributeCapabilityResponse( _In_ sai_status_t status, _In_ sai_object_type_t objectType, _In_ sai_attr_id_t attrId, _In_ const sai_attr_capability_t* capability) { SWSS_LOG_ENTER(); 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", sai_serialize_object_type(objectType).c_str(), attrId); return; } auto object_type_str = sai_serialize_object_type(objectType); const std::string attr_id_str = meta->attridname; const std::string create_str = (status == SAI_STATUS_SUCCESS ? (capability->create_implemented? "true":"false"): "false"); const std::string set_str = (status == SAI_STATUS_SUCCESS ? (capability->set_implemented? "true":"false"): "false"); const std::string get_str = (status == SAI_STATUS_SUCCESS ? (capability->get_implemented? "true":"false"): "false"); const std::vector<swss::FieldValueTuple> values = { swss::FieldValueTuple("OBJECT_TYPE", object_type_str), swss::FieldValueTuple("ATTR_ID", attr_id_str), swss::FieldValueTuple("CREATE_IMP", create_str), swss::FieldValueTuple("SET_IMP", set_str), swss::FieldValueTuple("GET_IMP", get_str) }; recordQueryAttributeCapabilityResponse(status, values); } void Recorder::recordQueryAttributeEnumValuesCapability( _In_ sai_object_id_t switchId, _In_ sai_object_type_t objectType, _In_ sai_attr_id_t attrId, _Inout_ sai_s32_list_t *enumValuesCapability) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> values; 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", sai_serialize_object_type(objectType).c_str(), attrId); return; } if (!meta->enummetadata) { SWSS_LOG_ERROR("Attribute %s is not enum/enumlist!", meta->attridname); return; } auto key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + sai_serialize_object_id(switchId); auto str_attr_id = sai_serialize_attr_id(*meta); auto str_enum_list = sai_serialize_enum_list(*enumValuesCapability, meta->enummetadata, true); // we only need to serialize count values.emplace_back(str_attr_id, str_enum_list); SWSS_LOG_DEBUG("Query arguments: switch %s, attribute: %s, count: %u", key.c_str(), meta->attridname, enumValuesCapability->count); recordQueryAttributeEnumValuesCapability(key, values); } void Recorder::recordQueryAttributeEnumValuesCapabilityResponse( _In_ sai_status_t status, _In_ sai_object_type_t objectType, _In_ sai_attr_id_t attrId, _In_ const sai_s32_list_t* enumValuesCapability) { SWSS_LOG_ENTER(); std::vector<swss::FieldValueTuple> values; 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", sai_serialize_object_type(objectType).c_str(), attrId); return; } if (!meta->enummetadata) { SWSS_LOG_ERROR("Attribute %s is not enum/enumlist!", meta->attridname); return; } bool countOnly = (status == SAI_STATUS_BUFFER_OVERFLOW); if (status == SAI_STATUS_SUCCESS || status == SAI_STATUS_BUFFER_OVERFLOW) { auto str_attr_id = sai_serialize_attr_id(*meta); auto str_enum_list = sai_serialize_enum_list(*enumValuesCapability, meta->enummetadata, countOnly); values.emplace_back(str_attr_id, str_enum_list); } recordQueryAttributeEnumValuesCapabilityResponse(status, values); } void Recorder::recordQueryStatsCapability( _In_ sai_object_id_t switchId, _In_ sai_object_type_t object_type, _Inout_ sai_stat_capability_list_t* stats_capability) { SWSS_LOG_ENTER(); auto key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + sai_serialize_object_id(switchId); auto object_type_str = sai_serialize_object_type(object_type); const std::string list_size = std::to_string(stats_capability->count); const std::vector<swss::FieldValueTuple> values = { swss::FieldValueTuple("OBJECT_TYPE", object_type_str), swss::FieldValueTuple("LIST_SIZE", list_size) }; SWSS_LOG_DEBUG("Query arguments: switch %s, object_type: %s, count: %s", key.c_str(), object_type_str.c_str(), list_size.c_str()); recordQueryStatsCapability(key, values); } void Recorder::recordQueryStatsCapabilityResponse( _In_ sai_status_t status, _In_ sai_object_type_t objectType, _In_ const sai_stat_capability_list_t *stats_capability) { SWSS_LOG_ENTER(); std::string str_stats_list; auto meta = sai_metadata_get_object_type_info(objectType); if (meta == NULL) { SWSS_LOG_ERROR("Failed to find object metadata: object type %s", sai_serialize_object_type(objectType).c_str()); return; } if (meta->statenum == NULL) { SWSS_LOG_ERROR("%s does not support stats", meta->objecttypename); return; } bool countOnly = (status == SAI_STATUS_BUFFER_OVERFLOW); if (status == SAI_STATUS_SUCCESS || status == SAI_STATUS_BUFFER_OVERFLOW) { str_stats_list = sai_serialize_stats_capability_list(*stats_capability, meta->statenum, countOnly); } recordQueryStatsCapabilityResponse(status, str_stats_list); } void Recorder::recordNotifySyncd( _In_ sai_object_id_t switchId, _In_ sai_redis_notify_syncd_t redisNotifySyncd) { SWSS_LOG_ENTER(); recordNotifySyncd(sai_serialize(redisNotifySyncd)); } void Recorder::recordStats( _In_ bool enable) { SWSS_LOG_ENTER(); m_recordStats = enable; } void Recorder::recordGenericResponse( _In_ sai_status_t status) { SWSS_LOG_ENTER(); if (status != SAI_STATUS_SUCCESS) { // record only when response is not success recordLine("E|" + sai_serialize_status(status)); } } void Recorder::recordBulkGenericResponse( _In_ sai_status_t status, _In_ uint32_t objectCount, _In_ const sai_status_t *objectStatuses) { SWSS_LOG_ENTER(); if (status != SAI_STATUS_SUCCESS) { // record only when response is not success std::string statuses = ""; for (uint32_t i = 0; i < objectCount; i++) { statuses += "|" + sai_serialize_status(objectStatuses[i]); } recordLine("E|" + sai_serialize_status(status) + "|" + statuses); } } void Recorder::recordComment( _In_ const std::string& comment) { SWSS_LOG_ENTER(); recordLine("#|" + comment); }