1 /*************************************************************************
3 * Copyright 2020 highstreet technologies GmbH and others
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
20 #include "ves_heartbeat.h"
21 #include "utils/log_utils.h"
22 #include "utils/sys_utils.h"
23 #include "utils/http_client.h"
24 #include "utils/nts_utils.h"
32 #include "core/session.h"
33 #include "core/framework.h"
35 #define HEARTBEAT_SCHEMA_XPATH "/nts-network-function:simulation/network-function/ves/heartbeat-period"
37 static volatile int ves_heartbeat_period;
38 static int ves_sequence_number;
40 static pthread_t ves_heartbeat_thread;
41 static pthread_mutex_t ves_heartbeat_lock;
44 //mutex-guarded access
45 static int ves_heartbeat_period_get(void);
46 static void ves_heartbeat_period_set(int new_period);
48 static int ves_heartbeat_send_ves_message(void);
49 static void *ves_heartbeat_thread_routine(void *arg);
50 static cJSON* ves_create_heartbeat_fields(int heartbeat_period);
51 static int heartbeat_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data);
53 int ves_heartbeat_feature_start(sr_session_ctx_t *current_session) {
57 if(pthread_mutex_init(&ves_heartbeat_lock, NULL) != 0) {
58 log_error("mutex init has failed");
59 return NTS_ERR_FAILED;
62 ves_heartbeat_period = 0;
63 ves_sequence_number = 0;
65 int rc = sr_get_item(current_session, HEARTBEAT_SCHEMA_XPATH, 0, &value);
67 ves_heartbeat_period_set(value->data.uint16_val);
70 else if(rc != SR_ERR_NOT_FOUND) {
71 log_error("sr_get_item failed");
74 rc = sr_module_change_subscribe(current_session, "nts-network-function", HEARTBEAT_SCHEMA_XPATH, heartbeat_change_cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &session_subscription);
76 log_error("could not subscribe to heartbeat");
77 return NTS_ERR_FAILED;
80 if(pthread_create(&ves_heartbeat_thread, 0, ves_heartbeat_thread_routine, 0)) {
81 log_error("could not create thread for heartbeat");
82 return NTS_ERR_FAILED;
88 static int ves_heartbeat_period_get(void) {
89 pthread_mutex_lock(&ves_heartbeat_lock);
90 int ret = ves_heartbeat_period;
91 pthread_mutex_unlock(&ves_heartbeat_lock);
95 static void ves_heartbeat_period_set(int new_period) {
96 pthread_mutex_lock(&ves_heartbeat_lock);
97 ves_heartbeat_period = new_period;
98 pthread_mutex_unlock(&ves_heartbeat_lock);
101 static int ves_heartbeat_send_ves_message(void) {
102 char *hostname_string = framework_environment.hostname;
103 cJSON *post_data_json = cJSON_CreateObject();
104 if(post_data_json == 0) {
105 log_error("cJSON_CreateObject failed");
106 return NTS_ERR_FAILED;
109 cJSON *event = cJSON_CreateObject();
111 log_error("cJSON_CreateObject failed");
112 cJSON_Delete(post_data_json);
113 return NTS_ERR_FAILED;
116 if(cJSON_AddItemToObject(post_data_json, "event", event) == 0) {
117 log_error("cJOSN_AddItemToObject failed");
118 cJSON_Delete(post_data_json);
119 return NTS_ERR_FAILED;
122 cJSON *common_event_header = ves_create_common_event_header("heartbeat", "Controller", hostname_string, "Low", ves_sequence_number++);
123 if(common_event_header == 0) {
124 log_error("ves_create_common_event_header failed");
125 cJSON_Delete(post_data_json);
126 return NTS_ERR_FAILED;
129 if(cJSON_AddItemToObject(event, "commonEventHeader", common_event_header) == 0) {
130 log_error("cJOSN_AddItemToObject failed");
131 cJSON_Delete(post_data_json);
132 return NTS_ERR_FAILED;
135 cJSON *heartbeat_fields = ves_create_heartbeat_fields(ves_heartbeat_period_get());
136 if(heartbeat_fields == 0) {
137 log_error("ves_create_heartbeat_fields failed");
138 cJSON_Delete(post_data_json);
139 return NTS_ERR_FAILED;
142 if(cJSON_AddItemToObject(event, "heartbeatFields", heartbeat_fields) == 0) {
143 log_error("cJOSN_AddItemToObject failed");
144 cJSON_Delete(post_data_json);
145 return NTS_ERR_FAILED;
148 char *post_data = cJSON_PrintUnformatted(post_data_json);
149 cJSON_Delete(post_data_json);
151 log_error("cJSON_PrintUnformatted failed");
152 return NTS_ERR_FAILED;
155 ves_details_t *ves_details = ves_endpoint_details_get(0);
157 log_error("ves_endpoint_details_get failed");
159 return NTS_ERR_FAILED;
162 int rc = http_request(ves_details->url, ves_details->username, ves_details->password, "POST", post_data, 0, 0);
163 ves_details_free(ves_details);
166 if(rc != NTS_ERR_OK) {
167 log_error("http_request failed");
168 return NTS_ERR_FAILED;
174 static void *ves_heartbeat_thread_routine(void *arg) {
175 int current_heartbeat_period = 0;
176 unsigned int timer_counter = 0;
178 while(!framework_sigint) {
179 current_heartbeat_period = ves_heartbeat_period_get();
182 if((timer_counter >= current_heartbeat_period) && (current_heartbeat_period > 0)) {
185 int rc = ves_heartbeat_send_ves_message();
186 if(rc != NTS_ERR_FAILED) {
187 log_error("could not send VES heartbeat");
197 static cJSON* ves_create_heartbeat_fields(int heartbeat_period) {
198 cJSON *heartbeat_fields = cJSON_CreateObject();
199 if(heartbeat_fields == 0) {
200 log_error("could not create JSON object");
204 if(cJSON_AddStringToObject(heartbeat_fields, "heartbeatFieldsVersion", "3.0") == 0) {
205 log_error("cJSON_Add*ToObject failed");
206 cJSON_Delete(heartbeat_fields);
210 if(cJSON_AddNumberToObject(heartbeat_fields, "heartbeatInterval", (double)(heartbeat_period)) == 0) {
211 log_error("cJSON_Add*ToObject failed");
212 cJSON_Delete(heartbeat_fields);
216 cJSON *additional_fields = cJSON_CreateObject();
217 if(additional_fields == 0) {
218 log_error("could not create JSON object");
219 log_error("cJSON_Add*ToObject failed");
220 cJSON_Delete(heartbeat_fields);
224 if(cJSON_AddItemToObject(heartbeat_fields, "additionalFields", additional_fields) == 0) {
225 log_error("cJSON_Add*ToObject failed");
226 cJSON_Delete(heartbeat_fields);
230 char *current_date_and_time = get_current_date_and_time();
231 if(current_date_and_time == 0) {
232 log_error("get_current_date_and_time failed");
233 cJSON_Delete(heartbeat_fields);
237 if(cJSON_AddStringToObject(additional_fields, "eventTime", current_date_and_time) == 0) {
238 log_error("cJSON_Add*ToObject failed");
239 cJSON_Delete(heartbeat_fields);
240 free(current_date_and_time);
243 free(current_date_and_time);
245 return heartbeat_fields;
248 static int heartbeat_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data) {
249 sr_change_iter_t *it = 0;
251 sr_change_oper_t oper;
252 sr_val_t *old_value = 0;
253 sr_val_t *new_value = 0;
255 if(event == SR_EV_DONE) {
256 rc = sr_get_changes_iter(session, HEARTBEAT_SCHEMA_XPATH, &it);
257 if(rc != SR_ERR_OK) {
258 log_error("sr_get_changes_iter failed");
259 return SR_ERR_VALIDATION_FAILED;
262 while((rc = sr_get_change_next(session, it, &oper, &old_value, &new_value)) == SR_ERR_OK) {
263 ves_heartbeat_period_set(new_value->data.uint16_val);
264 sr_free_val(old_value);
265 sr_free_val(new_value);
268 sr_free_change_iter(it);