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"
33 #include "core/session.h"
34 #include "core/framework.h"
36 #define HEARTBEAT_SCHEMA_XPATH "/nts-network-function:simulation/network-function/ves/heartbeat-period"
38 static volatile int ves_heartbeat_period;
39 static int ves_sequence_number;
41 static pthread_t ves_heartbeat_thread;
42 static pthread_mutex_t ves_heartbeat_lock;
45 //mutex-guarded access
46 static int ves_heartbeat_period_get(void);
47 static void ves_heartbeat_period_set(int new_period);
49 static int ves_heartbeat_send_ves_message(int port);
50 static void *ves_heartbeat_thread_routine(void *arg);
51 static cJSON* ves_create_heartbeat_fields(int heartbeat_period);
52 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);
54 static volatile sig_atomic_t ves_heartbeat_stopsig;
55 static sr_subscription_ctx_t *ves_heartbeat_subscription = 0;
57 int ves_heartbeat_feature_get_status(void) {
58 return (ves_heartbeat_subscription != 0);
61 int ves_heartbeat_feature_start(sr_session_ctx_t *current_session) {
63 assert(current_session);
65 if(ves_heartbeat_subscription == 0) {
67 if(pthread_mutex_init(&ves_heartbeat_lock, NULL) != 0) {
68 log_error("mutex init has failed\n");
69 return NTS_ERR_FAILED;
72 ves_heartbeat_stopsig = 0;
73 ves_heartbeat_period = 0;
74 ves_sequence_number = 0;
76 int rc = sr_get_item(current_session, HEARTBEAT_SCHEMA_XPATH, 0, &value);
78 ves_heartbeat_period_set(value->data.uint16_val);
81 else if(rc != SR_ERR_NOT_FOUND) {
82 log_error("sr_get_item failed\n");
85 rc = sr_module_change_subscribe(current_session, "nts-network-function", HEARTBEAT_SCHEMA_XPATH, heartbeat_change_cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &ves_heartbeat_subscription);
87 log_error("could not subscribe to heartbeat\n");
88 return NTS_ERR_FAILED;
91 if(pthread_create(&ves_heartbeat_thread, 0, ves_heartbeat_thread_routine, 0)) {
92 log_error("could not create thread for heartbeat\n");
93 return NTS_ERR_FAILED;
100 int ves_heartbeat_feature_stop(void) {
103 if(ves_heartbeat_subscription) {
104 int rc = sr_unsubscribe(ves_heartbeat_subscription);
105 if(rc != SR_ERR_OK) {
106 log_error("could not subscribe to heartbeat\n");
107 return NTS_ERR_FAILED;
111 ves_heartbeat_stopsig = 1;
112 pthread_join(ves_heartbeat_thread, &status);
113 pthread_mutex_destroy(&ves_heartbeat_lock);
115 ves_heartbeat_subscription = 0;
122 static int ves_heartbeat_period_get(void) {
123 pthread_mutex_lock(&ves_heartbeat_lock);
124 int ret = ves_heartbeat_period;
125 pthread_mutex_unlock(&ves_heartbeat_lock);
129 static void ves_heartbeat_period_set(int new_period) {
130 pthread_mutex_lock(&ves_heartbeat_lock);
131 ves_heartbeat_period = new_period;
132 pthread_mutex_unlock(&ves_heartbeat_lock);
135 static int ves_heartbeat_send_ves_message(int port) {
136 char *hostname_string = framework_environment.settings.hostname;
137 cJSON *post_data_json = cJSON_CreateObject();
138 if(post_data_json == 0) {
139 log_error("cJSON_CreateObject failed\n");
140 return NTS_ERR_FAILED;
143 cJSON *event = cJSON_CreateObject();
145 log_error("cJSON_CreateObject failed\n");
146 cJSON_Delete(post_data_json);
147 return NTS_ERR_FAILED;
150 if(cJSON_AddItemToObject(post_data_json, "event", event) == 0) {
151 log_error("cJOSN_AddItemToObject failed\n");
152 cJSON_Delete(post_data_json);
153 return NTS_ERR_FAILED;
156 cJSON *common_event_header = ves_create_common_event_header("heartbeat", "Controller", hostname_string, port, "Low", ves_sequence_number++);
157 if(common_event_header == 0) {
158 log_error("ves_create_common_event_header failed\n");
159 cJSON_Delete(post_data_json);
160 return NTS_ERR_FAILED;
163 if(cJSON_AddItemToObject(event, "commonEventHeader", common_event_header) == 0) {
164 log_error("cJOSN_AddItemToObject failed\n");
165 cJSON_Delete(post_data_json);
166 return NTS_ERR_FAILED;
169 cJSON *heartbeat_fields = ves_create_heartbeat_fields(ves_heartbeat_period_get());
170 if(heartbeat_fields == 0) {
171 log_error("ves_create_heartbeat_fields failed\n");
172 cJSON_Delete(post_data_json);
173 return NTS_ERR_FAILED;
176 if(cJSON_AddItemToObject(event, "heartbeatFields", heartbeat_fields) == 0) {
177 log_error("cJOSN_AddItemToObject failed\n");
178 cJSON_Delete(post_data_json);
179 return NTS_ERR_FAILED;
182 char *post_data = cJSON_PrintUnformatted(post_data_json);
183 cJSON_Delete(post_data_json);
185 log_error("cJSON_PrintUnformatted failed\n");
186 return NTS_ERR_FAILED;
189 ves_details_t *ves_details = ves_endpoint_details_get(0);
191 log_error("ves_endpoint_details_get failed\n");
193 return NTS_ERR_FAILED;
196 int rc = http_request(ves_details->url, ves_details->username, ves_details->password, "POST", post_data, 0, 0);
197 ves_details_free(ves_details);
200 if(rc != NTS_ERR_OK) {
201 log_error("http_request failed\n");
202 return NTS_ERR_FAILED;
208 static void *ves_heartbeat_thread_routine(void *arg) {
211 int current_heartbeat_period = 0;
212 unsigned int timer_counter = 0;
214 int ssh_base_port = 0;
215 int tls_base_port = 0;
216 nts_mount_point_addressing_method_t mp = nts_mount_point_addressing_method_get(0);
217 if(mp == UNKNOWN_MAPPING) {
218 log_error("mount-point-addressing-method failed, assuming DOCKER_MAPPING\n");
220 ssh_base_port = STANDARD_NETCONF_PORT;
221 tls_base_port = ssh_base_port + framework_environment.settings.ssh_connections;
223 else if(mp == DOCKER_MAPPING) {
224 ssh_base_port = STANDARD_NETCONF_PORT;
225 tls_base_port = ssh_base_port + framework_environment.settings.ssh_connections;
228 ssh_base_port = framework_environment.host.ssh_base_port;
229 tls_base_port = framework_environment.host.tls_base_port;
233 while((!framework_sigint) && (!ves_heartbeat_stopsig)) {
234 current_heartbeat_period = ves_heartbeat_period_get();
237 if((timer_counter >= current_heartbeat_period) && (current_heartbeat_period > 0)) {
240 if((framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections) > 1) {
241 for(int port = ssh_base_port; port < ssh_base_port + framework_environment.settings.ssh_connections; port++) {
242 int rc = ves_heartbeat_send_ves_message(port);
243 if(rc != NTS_ERR_FAILED) {
244 log_error("could not send VES heartbeat\n");
248 for(int port = tls_base_port; port < tls_base_port + framework_environment.settings.tls_connections; port++) {
249 int rc = ves_heartbeat_send_ves_message(port);
250 if(rc != NTS_ERR_FAILED) {
251 log_error("could not send VES heartbeat\n");
256 int rc = ves_heartbeat_send_ves_message(0);
257 if(rc != NTS_ERR_FAILED) {
258 log_error("could not send VES heartbeat\n");
270 static cJSON* ves_create_heartbeat_fields(int heartbeat_period) {
271 cJSON *heartbeat_fields = cJSON_CreateObject();
272 if(heartbeat_fields == 0) {
273 log_error("could not create JSON object\n");
277 if(cJSON_AddStringToObject(heartbeat_fields, "heartbeatFieldsVersion", "3.0") == 0) {
278 log_error("cJSON_Add*ToObject failed\n");
279 cJSON_Delete(heartbeat_fields);
283 if(cJSON_AddNumberToObject(heartbeat_fields, "heartbeatInterval", (double)(heartbeat_period)) == 0) {
284 log_error("cJSON_Add*ToObject failed\n");
285 cJSON_Delete(heartbeat_fields);
289 cJSON *additional_fields = cJSON_CreateObject();
290 if(additional_fields == 0) {
291 log_error("could not create JSON object\n");
292 log_error("cJSON_Add*ToObject failed\n");
293 cJSON_Delete(heartbeat_fields);
297 if(cJSON_AddItemToObject(heartbeat_fields, "additionalFields", additional_fields) == 0) {
298 log_error("cJSON_Add*ToObject failed\n");
299 cJSON_Delete(heartbeat_fields);
303 char *current_date_and_time = get_current_date_and_time();
304 if(current_date_and_time == 0) {
305 log_error("get_current_date_and_time failed\n");
306 cJSON_Delete(heartbeat_fields);
310 if(cJSON_AddStringToObject(additional_fields, "eventTime", current_date_and_time) == 0) {
311 log_error("cJSON_Add*ToObject failed\n");
312 cJSON_Delete(heartbeat_fields);
313 free(current_date_and_time);
316 free(current_date_and_time);
318 return heartbeat_fields;
321 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) {
322 sr_change_iter_t *it = 0;
324 sr_change_oper_t oper;
325 sr_val_t *old_value = 0;
326 sr_val_t *new_value = 0;
328 if(event == SR_EV_DONE) {
329 rc = sr_get_changes_iter(session, HEARTBEAT_SCHEMA_XPATH, &it);
330 if(rc != SR_ERR_OK) {
331 log_error("sr_get_changes_iter failed\n");
332 return SR_ERR_VALIDATION_FAILED;
335 while((rc = sr_get_change_next(session, it, &oper, &old_value, &new_value)) == SR_ERR_OK) {
336 ves_heartbeat_period_set(new_value->data.uint16_val);
337 sr_free_val(old_value);
338 sr_free_val(new_value);
341 sr_free_change_iter(it);