From 625e253b7c60f593fb3b929296f04c6cf77d8310 Mon Sep 17 00:00:00 2001 From: fperez Date: Fri, 2 Aug 2024 16:58:08 -0300 Subject: [PATCH] Add new FM API methods New API methods have been added to allow getting, setting, and clearing faults by providing both alarm_id and entity_instance_id. Changes in this commit: - For clear_fault operation, the query has been updated to allow the removal of multiple rows with a single query. The entity_instance_id parameter can now be a prefix, enabling the matching of multiple alarms with the same alarm_id. This change does not affect existing use cases. - New method get_faults_by_id_n_eid, This method allows API clients to retrieve a list of alarms that match an alarm_id and a prefix of entity_instance_id, thereby matching multiple alarms. - New method set_faults. This method accepts a list of alarms as a parameter and calls the same core set_fault function for each alarm. [PASS] Build and install packages [PASS] Remove several alarms that matches 'alarm_id', 'entity_instance_id=%' [PASS] Get list of alarms that match 'alarm_id' and 'entity_instance_id=%' [PASS] Verify that the new behavior for removing alarms does not affect previous use cases. [PASS] Test set_faults API function providing a list of alarms. Verify the alarms in the list are being created. [PASS] Enable debug mode to verify that queries are as expected. Story: 2011106 task: 50756 Change-Id: Ib9dcfa97960a5d50865133a61810681e5a09edbe Signed-off-by: fperez --- fm-api/source/fm_api/fm_api.py | 31 +++- fm-api/source/fm_api_test.py | 13 ++ fm-common/sources/fmAPI.cpp | 105 ++++++++++++- fm-common/sources/fmAPI.h | 8 +- fm-common/sources/fmDbAlarm.cpp | 121 ++++++++++----- fm-common/sources/fmDbAlarm.h | 7 + fm-common/sources/fmDbUtils.cpp | 10 +- fm-common/sources/fmMsg.h | 3 +- fm-common/sources/fmMsgServer.cpp | 184 ++++++++++++++++++++--- fm-common/sources/fmMsgServer.h | 2 + fm-common/sources/fm_python_mod_main.cpp | 135 ++++++++++++++++- 11 files changed, 547 insertions(+), 72 deletions(-) diff --git a/fm-api/source/fm_api/fm_api.py b/fm-api/source/fm_api/fm_api.py index 617aefc3..155d35a4 100755 --- a/fm-api/source/fm_api/fm_api.py +++ b/fm-api/source/fm_api/fm_api.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2024 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -185,6 +185,19 @@ class FaultAPIs(FaultAPIsBase): except (RuntimeError, SystemError, TypeError): return None + def set_faults(self, data): + buff_list = [] + with fm_api_lock: + for alarm_data in data: + self._check_required_attributes(alarm_data) + self._validate_attributes(alarm_data) + buff = self._alarm_to_str(alarm_data) + buff_list.append(buff) + try: + return fm_core.set_fault_list(buff_list) + except (RuntimeError, SystemError, TypeError): + return None + def clear_fault(self, alarm_id, entity_instance_id): with fm_api_lock: sep = constants.FM_CLIENT_STR_SEP @@ -244,6 +257,22 @@ class FaultAPIs(FaultAPIsBase): pass return None + def get_faults_by_id_n_eid(self, alarm_id, entity_instance_id): + with fm_api_lock: + sep = constants.FM_CLIENT_STR_SEP + buff = (sep + self._check_val(alarm_id) + sep + + self._check_val(entity_instance_id) + sep) + try: + resp = fm_core.get_by_id_n_eid(buff) + if resp: + data = [] + for i in resp: + data.append(self._str_to_alarm(i)) + return data + except (RuntimeError, SystemError, TypeError): + pass + return None + def get_faults_by_id(self, alarm_id): with fm_api_lock: try: diff --git a/fm-api/source/fm_api_test.py b/fm-api/source/fm_api_test.py index 3a7f2942..a2d29455 100644 --- a/fm-api/source/fm_api_test.py +++ b/fm-api/source/fm_api_test.py @@ -81,6 +81,17 @@ def get_all(instance_id): print("No alarm returned") +def get_all_by_id_n_eid(alarm_id, instance_id): + ll = ser.get_faults_by_id_n_eid(alarm_id, instance_id) + if ll is not None: + print("Total alarm returned: %d\n" + % len(ll)) + for i in ll: + print_alarm(i) + else: + print("No alarm returned") + + def get_list(alarm_id): ll = ser.get_faults_by_id(alarm_id) if ll is not None: @@ -105,3 +116,5 @@ if __name__ == "__main__": sys.exit(del_all(sys.argv[2])) elif sys.argv[1] == "get_list": sys.exit(get_list(sys.argv[2])) + elif sys.argv[1] == "get_list_id_eid": + sys.exit(get_all_by_id_n_eid(sys.argv[2], sys.argv[3])) diff --git a/fm-common/sources/fmAPI.cpp b/fm-common/sources/fmAPI.cpp index 5f3613ba..2ed8024c 100644 --- a/fm-common/sources/fmAPI.cpp +++ b/fm-common/sources/fmAPI.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2017,2023 Wind River Systems, Inc. +// Copyright (c) 2017,2024 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -287,7 +287,24 @@ EFmErrorT fm_set_fault(const SFmAlarmDataT *alarm, return FM_ERR_OK; } - +/** + * Clears fault entries based on the specified filter. + * + * This function sends a request to the FM server which performs the + * query for the clear_fault operation. + * In addition, this allows the removal of multiple rows with a single query. + * + * **Parameters:** + * - `filter`: A pointer to an `AlarmFilter` structure. This includes `alarm_id` + * and `entity_instance_id`. + * + * **Returns:** + * - `FM_ERR_OK` if the operation was successful. + * - `FM_ERR_NOCONNECT` if the function failed to connect to the server or if the + * connection was lost during the operation. + * - Other error codes as defined in the `EFmErrorT` enumeration if the operation + * encountered other issues. + */ EFmErrorT fm_clear_fault(AlarmFilter *filter) { CFmMutexGuard m(getAPIMutex()); @@ -479,6 +496,90 @@ EFmErrorT fm_get_faults_by_id(fm_alarm_id *alarm_id, return FM_ERR_OK; } +/** + * Retrieves alarms based on a filter with alarm_id and + * entity_instance_id. + * + * This function sends a request to the FM server to retrieve faults + * information matching the given filter criteria. + * The entity_instance_id parameter can now be a prefix (not complete), + * enabling the matching of multiple alarms with the same alarm_id + * + * **Parameters:** + * - `filter`: A pointer to an `AlarmFilter` structure. This includes `alarm_id` + * and `entity_instance_id`. + * - `alarm`: A pointer to an array of `SFmAlarmDataT` structures where + * the retrieved alarms will be stored. + * - `max_alarms_to_get`: On input, specifies the maximum number of alarms + * to retrieve. On output, specifies the number of alarms actually retrieved. + * + * **Returns:** + * An error code indicating the result of the operation. Possible values include: + * - `FM_ERR_OK`: Operation was successful. + * - `FM_ERR_NOCONNECT`: Failed to connect to the FM core. + * - Other error codes as defined in the `EFmErrorT` enumeration. + */ +EFmErrorT fm_get_faults_by_id_n_eid(AlarmFilter *filter, + SFmAlarmDataT *alarm, + unsigned int *max_alarms_to_get) { + + CFmMutexGuard m(getAPIMutex()); + if (!fm_lib_reconnect()) return FM_ERR_NOCONNECT; + fm_check_thread_pending_request(); + + fm_buff_t buff; + buff.clear(); + EFmErrorT erc = fm_msg_utils_prep_requet_msg(buff, EFmGetFaultsByIdnEid, + filter,sizeof(*filter)); + if (erc!=FM_ERR_OK) return erc; + + // if it was not correctly sent by the API, set to the expected value + if (*max_alarms_to_get == 0){ + *max_alarms_to_get = DEF_MAX_ALARMS; + } + + if (m_client.write_packet(buff)) { + if (!m_client.read_packet(buff)) { + m_connected = false; + return FM_ERR_NOCONNECT; + } + + if (ptr_to_hdr(buff)->msg_rc != FM_ERR_OK){ + *max_alarms_to_get = 0; + EFmErrorT rc = (EFmErrorT)ptr_to_hdr(buff)->msg_rc; + return rc; + } + + uint32_t pkt_size = ptr_to_hdr(buff)->msg_size; + if (pkt_size < sizeof(uint32_t)) { + FM_ERROR_LOG("Received invalid pkt size: %u\n",pkt_size ); + m_connected = false; + return FM_ERR_COMMUNICATIONS; + } + // Decrement pkt_size by the size of the length field in the header + // to get the size of the actual alarm data + pkt_size-=sizeof(uint32_t); + + char *dptr = (char*)ptr_to_data(buff); + + uint32_t *len = (uint32_t*)dptr; + dptr+=sizeof(uint32_t); + if (*max_alarms_to_get < *len) { + return FM_ERR_NOT_ENOUGH_SPACE; + } + if (pkt_size < (*len*sizeof(SFmAlarmDataT)) ) { + return FM_ERR_COMMUNICATIONS; + } + *max_alarms_to_get = *len; + memcpy(alarm,dptr,pkt_size); + } else { + m_connected = false; + return FM_ERR_NOCONNECT; + } + + return FM_ERR_OK; +} + /* * APIs that enqueue the request and return ok for acknowledgment. diff --git a/fm-common/sources/fmAPI.h b/fm-common/sources/fmAPI.h index b5b6fe2b..6da064e5 100644 --- a/fm-common/sources/fmAPI.h +++ b/fm-common/sources/fmAPI.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Wind River Systems, Inc. +// Copyright (c) 2024 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -27,6 +27,8 @@ typedef unsigned char FMBoolTypeT; #define FM_FALSE 0 +static const size_t DEF_MAX_ALARMS (1000); + typedef enum{ FM_ALARM_STATE_CLEAR = 0, FM_ALARM_STATE_SET = 1, @@ -204,6 +206,10 @@ EFmErrorT fm_get_faults(fm_ent_inst_t *inst_id, SFmAlarmDataT *alarm, EFmErrorT fm_get_faults_by_id(fm_alarm_id *alarm_id, SFmAlarmDataT *alarm, unsigned int *max_alarms_to_get); +EFmErrorT fm_get_faults_by_id_n_eid(AlarmFilter *filter, + SFmAlarmDataT *alarm, + unsigned int *max_alarms_to_get); + /* * APIs that enqueue the request and return ok for acknowledgment. diff --git a/fm-common/sources/fmDbAlarm.cpp b/fm-common/sources/fmDbAlarm.cpp index 9daa11e6..cf687c96 100644 --- a/fm-common/sources/fmDbAlarm.cpp +++ b/fm-common/sources/fmDbAlarm.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2014-2018 Wind River Systems, Inc. +// Copyright (c) 2014-2024 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -250,47 +250,29 @@ bool CFmDbAlarmOperation::get_alarm(CFmDBSession &sess, AlarmFilter &af, fm_db_r return true; } -bool CFmDbAlarmOperation::get_alarms(CFmDBSession &sess,const char *id, fm_db_result_t & alarms) { +/** Function similar to 'get_alarm', the only difference is that this could match + * several rows, since this is using the LIKE % clause. + */ +bool CFmDbAlarmOperation::get_alarms_eid_not_strict(CFmDBSession &sess, AlarmFilter &af, fm_db_result_t & alarms) { + std::string sql; - char query[FM_MAX_SQL_STATEMENT_MAX]; - fm_db_result_t res; - res.clear(); - sql = FM_DB_SELECT_FROM_TABLE(FM_ALARM_TABLE_NAME); - sql += " "; - - sql += " INNER JOIN "; - sql += FM_EVENT_SUPPRESSION_TABLE_NAME; - sql += " ON "; - sql += FM_ALARM_TABLE_NAME; - sql += "."; - sql += FM_ALARM_COLUMN_ALARM_ID; - sql += " = "; - sql += FM_EVENT_SUPPRESSION_TABLE_NAME; - sql += "."; - sql += FM_EVENT_SUPPRESSION_COLUMN_ALARM_ID; - - sql += " WHERE "; - sql += FM_EVENT_SUPPRESSION_TABLE_NAME; - sql += "."; - sql += FM_EVENT_SUPPRESSION_COLUMN_SUPPRESSION_STATUS; - sql += " = '"; - sql += FM_EVENT_SUPPRESSION_UNSUPPRESSED; - sql += "'"; - - if (id != NULL){ - snprintf(query, sizeof(query),"%s like '%s%s'", FM_ALARM_COLUMN_ENTITY_INSTANCE_ID, id,"%"); - if (NULL!=query) { - sql += " AND "; - sql += query; - } + if (strlen(af.entity_instance_id) == 0){ + snprintf(query, sizeof(query),"%s = '%s' AND %s = ' '", + FM_ALARM_COLUMN_ALARM_ID, af.alarm_id, + FM_ALARM_COLUMN_ENTITY_INSTANCE_ID); } - - FM_DEBUG_LOG("CMD:(%s)\n", sql.c_str()); - if ((sess.query(sql.c_str(), alarms)) != true) + else { + snprintf(query, sizeof(query), "%s = '%s' AND %s LIKE '%s%%'", + FM_ALARM_COLUMN_ALARM_ID, af.alarm_id, + FM_ALARM_COLUMN_ENTITY_INSTANCE_ID, af.entity_instance_id); + } + fm_db_util_build_sql_query((const char*)FM_ALARM_TABLE_NAME, query, sql); + FM_DEBUG_LOG("get_alarm:(%s)\n", sql.c_str()); + if ((sess.query(sql.c_str(), alarms)) != true){ return false; - + } return true; } @@ -311,6 +293,71 @@ bool CFmDbAlarmOperation::get_alarms_by_id(CFmDBSession &sess,const char *id, fm return true; } +bool CFmDbAlarmOperation::get_alarms(CFmDBSession &sess, const char *entity_instance_id, fm_db_result_t &alarms) { + std::string sql = CFmDbAlarmOperation::build_base_alarm_query(entity_instance_id); + + FM_DEBUG_LOG("CMD:(%s)\n", sql.c_str()); + if (!sess.query(sql.c_str(), alarms)) + return false; + + return true; +} + +bool CFmDbAlarmOperation::get_alarms_by_id_n_eid(CFmDBSession &sess, const AlarmFilter &af, fm_db_result_t &alarms) { + std::string sql = CFmDbAlarmOperation::build_base_alarm_query(af.entity_instance_id); + + if (strlen(af.alarm_id) > 0) { + char query[FM_MAX_SQL_STATEMENT_MAX]; + snprintf(query, sizeof(query), "%s.%s = '%s'", FM_ALARM_TABLE_NAME, + FM_ALARM_COLUMN_ALARM_ID, af.alarm_id); + sql += " AND "; + sql += query; + }else{ + FM_INFO_LOG("Alarm_id not provided \n"); + return false; + } + + FM_DEBUG_LOG("CMD:(%s)\n", sql.c_str()); + if (!sess.query(sql.c_str(), alarms)) + return false; + + return true; +} + +std::string CFmDbAlarmOperation::build_base_alarm_query(const char *entity_instance_id) { + std::string sql; + char query[FM_MAX_SQL_STATEMENT_MAX]; + + sql = FM_DB_SELECT_FROM_TABLE(FM_ALARM_TABLE_NAME); + sql += " INNER JOIN "; + sql += FM_EVENT_SUPPRESSION_TABLE_NAME; + sql += " ON "; + sql += FM_ALARM_TABLE_NAME; + sql += "."; + sql += FM_ALARM_COLUMN_ALARM_ID; + sql += " = "; + sql += FM_EVENT_SUPPRESSION_TABLE_NAME; + sql += "."; + sql += FM_EVENT_SUPPRESSION_COLUMN_ALARM_ID; + + sql += " WHERE "; + sql += FM_EVENT_SUPPRESSION_TABLE_NAME; + sql += "."; + sql += FM_EVENT_SUPPRESSION_COLUMN_SUPPRESSION_STATUS; + sql += " = '"; + sql += FM_EVENT_SUPPRESSION_UNSUPPRESSED; + sql += "'"; + + if (entity_instance_id != nullptr) { + snprintf(query, sizeof(query), "%s.%s LIKE '%s%s'", FM_ALARM_TABLE_NAME, + FM_ALARM_COLUMN_ENTITY_INSTANCE_ID, entity_instance_id, "%"); + sql += " AND "; + sql += query; + } + + return sql; +} + bool CFmDbAlarmOperation::get_all_alarms(CFmDBSession &sess, SFmAlarmDataT **alarms, size_t *len ) { fm_db_result_t res; diff --git a/fm-common/sources/fmDbAlarm.h b/fm-common/sources/fmDbAlarm.h index b955c4c8..45d52f52 100644 --- a/fm-common/sources/fmDbAlarm.h +++ b/fm-common/sources/fmDbAlarm.h @@ -66,9 +66,16 @@ public: bool get_alarms_by_id(CFmDBSession &sess, const char *id, fm_db_result_t & alarms); + bool get_alarms_by_id_n_eid(CFmDBSession &sess, const AlarmFilter &af, fm_db_result_t &alarms); + + bool get_alarms_eid_not_strict(CFmDBSession &sess, AlarmFilter &af, fm_db_result_t & alarms); + bool mask_unmask_alarms(CFmDBSession &sess, SFmAlarmDataT &a, bool mask = true); bool add_alarm_history(CFmDBSession &sess, SFmAlarmDataT &a, bool set); + +private: + static std::string build_base_alarm_query(const char *entity_instance_id); }; #endif /* FMDBALARM_H_ */ diff --git a/fm-common/sources/fmDbUtils.cpp b/fm-common/sources/fmDbUtils.cpp index dc917498..1814c68a 100644 --- a/fm-common/sources/fmDbUtils.cpp +++ b/fm-common/sources/fmDbUtils.cpp @@ -436,14 +436,14 @@ bool fm_db_util_build_sql_delete(const char* db_table, AlarmFilter *db_data, char sql[FM_MAX_SQL_STATEMENT_MAX]; if (strlen(db_data->entity_instance_id) == 0){ - snprintf(sql, sizeof(sql), "DElETE FROM %s WHERE %s = '%s' AND %s = ' '", + snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s = '%s' AND %s = ' '", db_table, FM_ALARM_COLUMN_ALARM_ID, db_data->alarm_id, FM_ALARM_COLUMN_ENTITY_INSTANCE_ID); } else{ - snprintf(sql, sizeof(sql), "DElETE FROM %s WHERE %s = '%s' AND %s = '%s'", + snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s = '%s' AND %s like '%s%s'", db_table, FM_ALARM_COLUMN_ALARM_ID, db_data->alarm_id, - FM_ALARM_COLUMN_ENTITY_INSTANCE_ID, db_data->entity_instance_id); + FM_ALARM_COLUMN_ENTITY_INSTANCE_ID, db_data->entity_instance_id,"%"); } db_cmd.assign(sql); return true; @@ -456,7 +456,7 @@ bool fm_db_util_build_sql_delete_row(const char* db_table, int id, char sql[FM_MAX_SQL_STATEMENT_MAX]; - snprintf(sql, sizeof(sql), "DElETE FROM %s WHERE %s = %d", + snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s = %d", db_table, FM_ALARM_COLUMN_ID, id); db_cmd.assign(sql); return true; @@ -467,7 +467,7 @@ bool fm_db_util_build_sql_delete_all(const char* db_table, const char *id, char sql[FM_MAX_SQL_STATEMENT_MAX]; - snprintf(sql, sizeof(sql), "DElETE FROM %s WHERE %s like '%s%s'", db_table, + snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s like '%s%s'", db_table, FM_ALARM_COLUMN_ENTITY_INSTANCE_ID, id,"%"); db_cmd.assign(sql); return true; diff --git a/fm-common/sources/fmMsg.h b/fm-common/sources/fmMsg.h index 42e3413a..c8ccecb1 100644 --- a/fm-common/sources/fmMsg.h +++ b/fm-common/sources/fmMsg.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2014 Wind River Systems, Inc. +// Copyright (c) 2024 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -36,6 +36,7 @@ typedef enum { EFmGetFaults, EFmReturnUUID, EFmGetFaultsById, + EFmGetFaultsByIdnEid, EFmActMax }EFmMsgActionsT; diff --git a/fm-common/sources/fmMsgServer.cpp b/fm-common/sources/fmMsgServer.cpp index 9e1db8e9..5b1683fb 100644 --- a/fm-common/sources/fmMsgServer.cpp +++ b/fm-common/sources/fmMsgServer.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2017-2018 Wind River Systems, Inc. +// Copyright (c) 2017-2024 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -288,6 +288,97 @@ void get_db_alarms_by_id(CFmDBSession &sess, sFmGetReq &req, void *context){ } } +/** + * Retrieves faults from the database based on the specified alarm ID and entity instance ID. + * + * This function queries the database for alarms that match the provided alarm ID + * and entity instance ID. The results are then sent back to the client through + * the provided socket server processor. + * + * **Parameters:** + * - `sess`: A reference to a `CFmDBSession` object representing the database session. + * - `req`: A reference to an `sFmGetReq` structure containing the request data. + * - `context`: A pointer to a context object. + * + * **Process Flow:** + * - The function initializes a buffer with the request data and extracts the header and data + * sections. + * - It casts the data section to an `AlarmFilter` pointer and the context to an + * `FmSocketServerProcessor` pointer. + * - Database Query + * - If alarms are found, it fills a vector with the alarm data. + * - Sets the number of alarms found in the buffer and copies the alarm data into the buffer. + * - It sends the response back to the client using the `send_response` method of the + * `FmSocketServerProcessor`. + * + * - If no alarms are found, it logs a message and sets the response code to + * `FM_ERR_ENTITY_NOT_FOUND`. + * + * **Returns:** + * - The function does not return a value but sends a response back to the client + * through the provided socket server processor. + */ +void get_db_alarms_by_id_n_eid(CFmDBSession &sess, sFmGetReq &req, void *context){ + + fm_buff_t buff = req.data; + SFmMsgHdrT *hdr = (SFmMsgHdrT *)&buff[0]; + void * data = &buff[sizeof(SFmMsgHdrT)]; + AlarmFilter *filter = (AlarmFilter *)data; + FmSocketServerProcessor *srv = (FmSocketServerProcessor *)context; + CFmDbAlarmOperation op; + fm_db_result_t res; + std::vector alarmv; + + FM_DEBUG_LOG("handle get_db_alarms_by_id_n_eid:%s\n", filter->alarm_id); + + hdr->msg_rc = FM_ERR_OK; + res.clear(); + if (op.get_alarms_by_id_n_eid(sess, *filter, res) != true){ + hdr->msg_rc = FM_ERR_DB_OPERATION_FAILURE; + }else if (res.size() > 0){ + int ix = 0; + int resp_len = res.size(); + SFmAlarmDataT alarm; + alarmv.clear(); + // Fill the tuple data vector for the response. + for ( ; ix < resp_len ; ++ix ) { + CFmDbAlarm::convert_to(res[ix],&alarm); + alarmv.push_back(alarm); + } + } else { + FM_DEBUG_LOG("No alarms found for alarm id (%s), " + "entity_instance_id (%s)\n", + filter->alarm_id, filter->entity_instance_id); + hdr->msg_rc = FM_ERR_ENTITY_NOT_FOUND; + } + + if ((hdr->msg_rc==FM_ERR_OK) && (alarmv.size() > 0)){ + int found_num_alarms=alarmv.size(); + FM_DEBUG_LOG("Get faults: found alarms: (%d)", found_num_alarms); + + // (num of alarms found * size of alarm structure) + + // space to report number of alarms found. + int total_len =(found_num_alarms * sizeof(SFmAlarmDataT)) + sizeof(uint32_t); + + void * buffer = malloc(total_len); + if (buffer==NULL) { + hdr->msg_rc =FM_ERR_SERVER_NO_MEM; + srv->send_response(req.fd,hdr,NULL,0); + return; + } + uint32_t *alen = (uint32_t*) buffer; + *alen = found_num_alarms; + + SFmAlarmDataT * alarms = (SFmAlarmDataT*) ( ((char*)buffer)+sizeof(uint32_t)); + + memcpy(alarms,&(alarmv[0]),alarmv.size() * sizeof(SFmAlarmDataT)); + srv->send_response(req.fd,hdr,buffer,total_len); + free(buffer); + } else { + srv->send_response(req.fd,hdr,NULL,0); + } +} + void fm_handle_job_request(CFmDBSession &sess, sFmJobReq &req){ CFmDbAlarmOperation op; CFmEventSuppressionOperation event_suppression_op; @@ -333,6 +424,7 @@ void fm_handle_get_request(CFmDBSession &sess, sFmGetReq &req, case EFmGetFault:get_db_alarm(sess,req,context); break; case EFmGetFaults:get_db_alarms(sess,req,context); break; case EFmGetFaultsById:get_db_alarms_by_id(sess,req,context); break; + case EFmGetFaultsByIdnEid:get_db_alarms_by_id_n_eid(sess,req,context); break; default: FM_ERROR_LOG("Unexpected job request, action:%u\n",hdr->action); break; @@ -471,8 +563,29 @@ void FmSocketServerProcessor::handle_delete_faults(int fd, send_response(fd,hdr,NULL,0); } -void FmSocketServerProcessor::handle_delete_fault(int fd, - SFmMsgHdrT *hdr, std::vector &rdata, CFmDBSession &sess) { +/** + * Handles the deletion of faults based on the specified filter. + * + * This function processes the request to delete faults from the database. + * It first validates the request, then retrieves matching alarms and deletes + * them from the database. If multiple alarms are found, it handles each + * alarm separately and enqueues a job for each cleared alarm. + * + * **Parameters:** + * - `fd`: The file descriptor for the client connection. + * - `hdr`: A pointer to an `SFmMsgHdrT` structure containing the message header. + * - `rdata`: A reference to a vector of characters containing the request data. + * - `sess`: A reference to a `CFmDBSession` object representing the database session. + * + * **Returns:** + * - The function does not return a value but sends a response back to the client through + * the provided file descriptor. + */ +void FmSocketServerProcessor::handle_delete_fault( + int fd, + SFmMsgHdrT *hdr, + std::vector &rdata, + CFmDBSession &sess) { CFmDbAlarmOperation op; sFmJobReq req; @@ -480,36 +593,54 @@ void FmSocketServerProcessor::handle_delete_fault(int fd, SFmAlarmDataT alarm; fm_db_result_t res; - is_request_valid(hdr->msg_size,AlarmFilter); + is_request_valid(hdr->msg_size, AlarmFilter); void * data = &(rdata[sizeof(SFmMsgHdrT)]); AlarmFilter *filter = (AlarmFilter *)(data); hdr->msg_rc = FM_ERR_OK; res.clear(); - if ((op.get_alarm(sess, *filter, res)) != true){ + if ((op.get_alarms_eid_not_strict(sess, *filter, res)) != true) { hdr->msg_rc = FM_ERR_DB_OPERATION_FAILURE; - }else{ - if (res.size() > 0){ - if(op.delete_alarm(sess, *filter) > 0){ - FM_INFO_LOG("Deleted alarm: (%s) (%s)\n", - filter->alarm_id, filter->entity_instance_id); - CFmDbAlarm::convert_to(res[0],&alarm); - fm_uuid_create(alarm.uuid); - req.type = FM_ALARM_CLEAR; - req.set = false; - req.data = alarm; - enqueue_job(req); - }else{ + } else { + if (res.size() > 0) { + if (op.delete_alarm(sess, *filter) > 0) { + FM_INFO_LOG("Deleted alarm(s): (%s) (%s)\n", + filter->alarm_id, filter->entity_instance_id); + if (res.size() == 1) { + // normal workflow, just one alarm match + CFmDbAlarm::convert_to(res[0], &alarm); + fm_uuid_create(alarm.uuid); + req.type = FM_ALARM_CLEAR; + req.set = false; + req.data = alarm; + enqueue_job(req); + } else { + // Multiple alarms received + for (const auto &entry : res) { + SFmAlarmDataT alarm; + CFmDbAlarm::data_type entry_copy = entry; + CFmDbAlarm::convert_to(entry_copy, &alarm); + fm_uuid_create(alarm.uuid); + req.type = FM_ALARM_CLEAR; + req.set = false; + req.data = alarm; + enqueue_job(req); + } + } + } else { hdr->msg_rc = FM_ERR_DB_OPERATION_FAILURE; + FM_INFO_LOG("Deleted alarm failed: (%s) (%s) (%s)\n", + filter->alarm_id, filter->entity_instance_id, + fm_error_from_int((EFmErrorT)hdr->msg_rc).c_str()); } - }else{ + } else { hdr->msg_rc = FM_ERR_ENTITY_NOT_FOUND; FM_INFO_LOG("Deleted alarm failed: (%s) (%s) (%s)\n", - filter->alarm_id, filter->entity_instance_id, - fm_error_from_int((EFmErrorT)hdr->msg_rc).c_str()); + filter->alarm_id, filter->entity_instance_id, + fm_error_from_int((EFmErrorT)hdr->msg_rc).c_str()); } } FM_INFO_LOG("Response to delete fault: %u\n", hdr->msg_rc); - send_response(fd,hdr,NULL,0); + send_response(fd, hdr, NULL, 0); } void FmSocketServerProcessor::handle_get_faults_by_id(int fd, @@ -522,6 +653,16 @@ void FmSocketServerProcessor::handle_get_faults_by_id(int fd, enqueue_get(req); } +void FmSocketServerProcessor::handle_get_faults_by_id_n_eid(int fd, + SFmMsgHdrT *hdr, std::vector &rdata) { + + is_request_valid(hdr->msg_size,AlarmFilter); + sFmGetReq req; + req.fd = fd; + req.data = rdata; + enqueue_get(req); +} + void FmSocketServerProcessor::handle_get_faults(int fd, SFmMsgHdrT *hdr, std::vector &rdata) { @@ -555,6 +696,7 @@ void FmSocketServerProcessor::handle_socket_data(int fd, case EFmGetFault:handle_get_fault(fd,hdr,rdata); break; case EFmGetFaults:handle_get_faults(fd,hdr,rdata); break; case EFmGetFaultsById:handle_get_faults_by_id(fd,hdr,rdata); break; + case EFmGetFaultsByIdnEid:handle_get_faults_by_id_n_eid(fd,hdr,rdata); break; default: FM_ERROR_LOG("Unexpected client request, action:%u\n",hdr->action); break; diff --git a/fm-common/sources/fmMsgServer.h b/fm-common/sources/fmMsgServer.h index d93adc55..88b18043 100644 --- a/fm-common/sources/fmMsgServer.h +++ b/fm-common/sources/fmMsgServer.h @@ -31,6 +31,8 @@ protected: std::vector &rdata); virtual void handle_get_faults_by_id(int fd, SFmMsgHdrT *hdr, std::vector &rdata); + virtual void handle_get_faults_by_id_n_eid(int fd, SFmMsgHdrT *hdr, + std::vector &rdata); public: void send_response(int fd, SFmMsgHdrT *hdr, void *data, size_t len); }; diff --git a/fm-common/sources/fm_python_mod_main.cpp b/fm-common/sources/fm_python_mod_main.cpp index 40a83017..6a7705d0 100644 --- a/fm-common/sources/fm_python_mod_main.cpp +++ b/fm-common/sources/fm_python_mod_main.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Wind River Systems, Inc. +// Copyright (c) 2024 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -13,7 +13,6 @@ #include "fmAlarmUtils.h" -static const size_t DEF_MAX_ALARMS (1000); static const size_t MAXSTRINGSIZE (500); static PyObject *logging = NULL; @@ -21,8 +20,8 @@ enum { error, warning, info, debug, max_level }; #define LOG_MSG(level,data,...) \ - log_msg(level, "fm_python_extension: "\ - data, ## __VA_ARGS__ ) + log_msg(level, "%s:%d:%s: " data, __FILE__, __LINE__, \ + __FUNCTION__, ##__VA_ARGS__) #define ERROR_LOG(data,...) \ LOG_MSG(error, data, ## __VA_ARGS__) @@ -100,6 +99,65 @@ static PyObject * _fm_set(PyObject * self, PyObject *args) { Py_RETURN_NONE; } +static PyObject * _fm_set_list(PyObject * self, PyObject *args) { + /* Receives a PyObject expected to be a list of strings + containing different alarm information. + The function then parses each string into different alarm + structures, which are filled and sent to the C++ core. */ + + std::string alarm; + fm_uuid_t tmp_uuid; + PyObject *uuidList = PyList_New(0); + EFmErrorT rc = FM_ERR_INVALID_REQ; + + PyObject *listObj = nullptr; + if (!PyArg_ParseTuple(args, "O", &listObj)) { + ERROR_LOG("Failed to parse args."); + Py_RETURN_NONE; + } + + if (!PyList_Check(listObj)) { + ERROR_LOG("Expected a list."); + Py_RETURN_NONE; + } + + Py_ssize_t num_items = PyList_Size(listObj); + for (Py_ssize_t i = 0; i < num_items; i++) { + PyObject *item = PyList_GetItem(listObj, i); + if (!PyUnicode_Check(item)) { + ERROR_LOG("List items must be strings."); + Py_RETURN_NONE; + } + + const char *alm_str = PyUnicode_AsUTF8(item); + std::string alarm(alm_str); + + SFmAlarmDataT alm_data; + if (!fm_alarm_from_string(alarm, &alm_data)) { + ERROR_LOG("Failed to convert string to alarm."); + continue; + } + + rc = fm_set_fault(&alm_data, &tmp_uuid); + if (rc == FM_ERR_OK) { + PyObject *uuidStr = PyUnicode_FromString(tmp_uuid); + PyList_Append(uuidList, uuidStr); + Py_DECREF(uuidStr); + } else if (rc == FM_ERR_NOCONNECT) { + WARNING_LOG("Failed to connect to FM manager"); + } else { + ERROR_LOG("Failed to generate an alarm: (%s) (%s)", + alm_data.alarm_id, alm_data.entity_instance_id); + } + } + + if (rc == FM_ERR_OK) { + return uuidList; + } + + Py_RETURN_NONE; +} + static PyObject * _fm_get(PyObject * self, PyObject *args) { const char *filter; @@ -237,6 +295,71 @@ static PyObject * _fm_get_by_eid(PyObject * self, PyObject *args, PyObject* kwar Py_RETURN_FALSE; } +static PyObject * _fm_get_by_id_n_eid(PyObject * self, PyObject *args) { + /* Receive a PyObject expected to be a filter containing + alarm_id and entity_instance_id. The entity_instance_id + does not need to be complete, allowing it to match + more than one row. + The function parses this object into different alarm + strings and fills a filter alarm structure that is + sent to the C++ core. */ + const char *filter; + AlarmFilter af; + std::string alm_str, filter_str; + std::vector< SFmAlarmDataT > lst; + unsigned int max= DEF_MAX_ALARMS; + EFmErrorT rc; + + if (!PyArg_ParseTuple(args, "s", &filter)) { + ERROR_LOG("Failed to parse args."); + Py_RETURN_FALSE; + } + + filter_str.assign(filter); + if (!fm_alarm_filter_from_string(filter_str, &af)) { + ERROR_LOG("Invalid alarm filter: (%s)", + filter_str.c_str()); + Py_RETURN_FALSE; + } + + try { + lst.resize(max); + } catch(...) { + ERROR_LOG("Failed to allocate memory."); + Py_RETURN_FALSE; + } + + unsigned int max_alarms_to_get = max; + rc = fm_get_faults_by_id_n_eid(&af, &(lst[0]), &max_alarms_to_get); + if (rc == FM_ERR_OK) { + PyObject *__lst = PyList_New(0); + for ( size_t ix = 0; ix < max_alarms_to_get; ++ix ) { + std::string s; + fm_alarm_to_string(&lst[ix], s); + if (s.size() > 0) { + if (PyList_Append(__lst,PyUnicode_FromString(s.c_str())) != 0) { + ERROR_LOG("Failed to append alarm to the list"); + } + } + } + /* python will garbage collect if the reference count is correct + (it should be 1 at this point) */ + return __lst; + } + + if (rc == FM_ERR_ENTITY_NOT_FOUND) { + DEBUG_LOG("Alarm id (%s), Entity id:(%s) not found", + af.alarm_id, af.entity_instance_id); + Py_RETURN_NONE; + } else if (rc == FM_ERR_NOCONNECT) { + WARNING_LOG("Failed to connect to FM manager"); + } else { + ERROR_LOG("Failed to get alarm list for entity id (%s), error code: (%d)", + af.entity_instance_id, rc); + } + Py_RETURN_FALSE; +} + static PyObject * _fm_clear(PyObject * self, PyObject *args) { const char *filter; @@ -312,6 +435,10 @@ static PyMethodDef _methods [] = { "Get alarms by alarm id" }, { "get_by_eid", (PyCFunction)_fm_get_by_eid, METH_VARARGS | METH_KEYWORDS, "Get alarms by entity instance id" }, + { "get_by_id_n_eid", _fm_get_by_id_n_eid, METH_VARARGS, + "Get list of alarms by filter" }, + { "set_fault_list", _fm_set_list, METH_VARARGS, + "Set alarm list" }, { NULL, NULL, 0, NULL } };