Monitoring Netconf connectivity feature

Issue-ID: SIM-111
Change-Id: I99a798296bca6154fd2e07b988edce6661b07ba5
Signed-off-by: Alex Stancu <alexandru.stancu@highstreet-technologies.com>
diff --git a/ntsimulator/deploy/base/build_ntsim-ng.sh b/ntsimulator/deploy/base/build_ntsim-ng.sh
index 096dc3f..cbce07a 100755
--- a/ntsimulator/deploy/base/build_ntsim-ng.sh
+++ b/ntsimulator/deploy/base/build_ntsim-ng.sh
@@ -34,6 +34,7 @@
     "core/app/manager_sysrepo.c"
     "core/app/network_function.c"
     "core/app/nf_oran_du.c"
+    "core/app/nf_oran_ru_supervision.c"
     "core/app/blank.c"
     "core/datastore/schema.c"
     "core/datastore/operations.c"
diff --git a/ntsimulator/deploy/o-ran-ru-fh/config.json b/ntsimulator/deploy/o-ran-ru-fh/config.json
index 1934a03..4d614cf 100644
--- a/ntsimulator/deploy/o-ran-ru-fh/config.json
+++ b/ntsimulator/deploy/o-ran-ru-fh/config.json
@@ -1,7 +1,7 @@
 {
     "container-rules": {
         "excluded-modules": [],
-        "excluded-features": []
+        "excluded-features": ["o-ran-wg4-features:SUPERVISION-WITH-SESSION-ID"]
     },
 
     "supervisor-rules": {
@@ -12,7 +12,7 @@
             "stdout": "log/netopeer-stdout.log",
             "stderr": "log/netopeer-stderr.log"
         },
-    
+
         "sshd": {
             "path": "/usr/sbin/sshd",
             "args": ["-D"],
@@ -20,7 +20,7 @@
             "stdout": "log/sshd-stdout.log",
             "stderr": "log/sshd-stderr.log"
         },
-    
+
         "ntsim-network-function": {
             "path": "/opt/dev/ntsim-ng/ntsim-ng",
             "args": ["-w/opt/dev/ntsim-ng", "-f"],
@@ -53,7 +53,7 @@
             "o-ran-shared-cell",
             "nts-network-function"
         ],
-        
+
         "default-list-instances": 2,
         "custom-list-instances" : [
             {"/ietf-interfaces:interfaces/interface": 4}
@@ -89,7 +89,7 @@
                 "severity"  : "MAJOR",
                 "date-time" : "$$time$$",
                 "specific-problem" : "Interface Fault",
-                
+
                 "fault-id": "30",
                 "fault-severity" : "MAJOR",
                 "affected-object" : "$$hostname$$",
@@ -140,4 +140,4 @@
             }
         ]
     }
-}
\ No newline at end of file
+}
diff --git a/ntsimulator/nts-ng-docker-image-build-ubuntu.yaml b/ntsimulator/nts-ng-docker-image-build-ubuntu.yaml
index c86d387..e351e4b 100644
--- a/ntsimulator/nts-ng-docker-image-build-ubuntu.yaml
+++ b/ntsimulator/nts-ng-docker-image-build-ubuntu.yaml
@@ -67,6 +67,8 @@
       labels:
        - "description=nts-ng"
       dockerfile: local.Dockerfile
+    depends_on:
+      - nts-ng-base
 
   ###################################################
   ####### O-RAN DU
diff --git a/ntsimulator/ntsim-ng/core/app/network_function.c b/ntsimulator/ntsim-ng/core/app/network_function.c
index e34d85c..c655dfb 100644
--- a/ntsimulator/ntsim-ng/core/app/network_function.c
+++ b/ntsimulator/ntsim-ng/core/app/network_function.c
@@ -48,6 +48,7 @@
 
 #include "app_common.h"
 #include "nf_oran_du.h"
+#include "nf_oran_ru_supervision.h"
 
 #define NF_FUNCTION_CONTROL_BUFFER_LENGTH                       32
 
@@ -96,9 +97,9 @@
         }
     }
 
-    if(pthread_mutex_init(&nf_function_control_lock, NULL) != 0) { 
-        log_error("mutex init has failed\n"); 
-        return NTS_ERR_FAILED; 
+    if(pthread_mutex_init(&nf_function_control_lock, NULL) != 0) {
+        log_error("mutex init has failed\n");
+        return NTS_ERR_FAILED;
     }
 
     //ietf-netconf-monitoring schemas populate with modules and submodules (overwrite default Netopeer2 behaviour)
@@ -167,9 +168,9 @@
         return NTS_ERR_FAILED;
     }
 
-    if(pthread_mutex_init(&faults_lock, NULL) != 0) { 
-        log_error("mutex init has failed\n"); 
-        return NTS_ERR_FAILED; 
+    if(pthread_mutex_init(&faults_lock, NULL) != 0) {
+        log_error("mutex init has failed\n");
+        return NTS_ERR_FAILED;
     }
 
     if(pthread_create(&faults_thread, 0, faults_thread_routine, 0)) {
@@ -188,7 +189,7 @@
             nf_function_control_buffer_in = 0;
         }
 
-        log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"running in NETWORK FUNCTION STANDALONE mode!\n"LOG_COLOR_RESET);
+        log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"running in NETWORK FUNCTION STANDALONE mode as a %s\n"LOG_COLOR_RESET, framework_environment.nts.function_type);
         log_add_verbose(1, "Currently enabled features are: %s\n", framework_environment.nts.nf_standalone_start_features);
         log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"Docker IP:"LOG_COLOR_RESET" %s\n", framework_environment.settings.ip_v6_enabled ? framework_environment.settings.ip_v6 : framework_environment.settings.ip_v4);
         log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"Docker ports"LOG_COLOR_RESET": ");
@@ -222,8 +223,16 @@
     if(strcmp(framework_environment.nts.function_type, "NTS_FUNCTION_TYPE_O_RAN_O_DU") == 0) {
         rc = nf_oran_du_init();
         if(rc != NTS_ERR_OK) {
-            log_error("nf_oran_du_init failed\n"); 
-            return NTS_ERR_FAILED; 
+            log_error("nf_oran_du_init failed\n");
+            return NTS_ERR_FAILED;
+        }
+    }
+
+    if(strcmp(framework_environment.nts.function_type, "NTS_FUNCTION_TYPE_O_RAN_O_RU_FH") == 0) {
+        log_add_verbose(1, "Initializing o-ran-supervision function...\n");
+        rc = nf_oran_ru_supervision_init();
+        if(rc != NTS_ERR_OK) {
+            log_error("nf_oran_ru_supervision_init failed, continuing...\n");
         }
     }
 
@@ -397,7 +406,7 @@
         if (!strcmp("sysrepo", mod->name) || !strcmp("sysrepo-monitoring", mod->name) || !strcmp("sysrepo-plugind", mod->name)) {
             continue;
         }
-        
+
         list = lyd_new(root, NULL, "schema");
         lyd_new_leaf(list, NULL, "identifier", mod->name);
         lyd_new_leaf(list, NULL, "version", (mod->rev ? mod->rev[0].date : NULL));
@@ -428,7 +437,7 @@
     struct lyd_node *stream = lyd_new_path(root, 0, NC_NOTIFICATIONS_STREAMS_SCHEMA_XPATH"/stream[name='NETCONF']", NULL, 0, 0);
     lyd_new_leaf(stream, stream->schema->module, "description", "Default NETCONF stream containing notifications from all the modules. Replays only notifications for modules that support replay.");
     lyd_new_leaf(stream, stream->schema->module, "replaySupport", "true");
-    
+
     /* all other streams */
     struct lyd_node *sr_data;
     struct lyd_node *sr_mod;
@@ -463,7 +472,7 @@
                 rep_sup = set->set.d[0];
             }
             ly_set_free(set);
-            
+
             lyd_new_leaf(stream, NULL, "replaySupport", rep_sup ? "true" : "false");
             if(rep_sup) {
                 char buf[26];
@@ -594,7 +603,7 @@
         pthread_mutex_unlock(&nf_function_control_lock);
     }
 
-    
+
 
     return rc;
 }
@@ -689,7 +698,7 @@
     if(ves_file_ready_feature_get_status()) {
         strcat(started_features, "ves-file-ready ");
     }
-    
+
     if(ves_pnf_registration_feature_get_status()) {
         strcat(started_features, "ves-pnf-registration ");
     }
@@ -697,7 +706,7 @@
     if(ves_heartbeat_feature_get_status()) {
         strcat(started_features, "ves-heartbeat ");
     }
-    
+
     if(manual_notification_feature_get_status()) {
         strcat(started_features, "manual-notification-generation ");
     }
@@ -705,7 +714,7 @@
     if(netconf_call_home_feature_get_status()) {
         strcat(started_features, "netconf-call-home ");
     }
-    
+
     if(web_cut_through_feature_get_status()) {
         strcat(started_features, "web-cut-through ");
     }
@@ -738,7 +747,7 @@
     assert_session();
 
     int ret = NTS_ERR_OK;
-    
+
     int rc;
     struct lyd_node *data;
     rc = sr_get_subtree(session, NTS_NF_FAULT_GENERATION_SCHEMA_XPATH, 0, &data);
@@ -771,9 +780,9 @@
                 }
             }
         }
-        
+
     }
-    
+
     faults_update_config_free:
     lyd_free(data);
 
@@ -848,7 +857,7 @@
                     log_error("lyd_parse_mem failed\n");
                     goto fault_send_ves;
                 }
-                
+
                 rc = sr_event_notif_send_tree(current_session_running, notif);
                 lyd_free(notif);
                 if(rc != SR_ERR_OK) {
@@ -894,7 +903,7 @@
         }
 
         while((rc = sr_get_change_next(session, it, &oper, &old_value, &new_value)) == SR_ERR_OK) {
-            
+
             if(new_value->xpath && (strcmp(new_value->xpath, NTS_NF_NETWORK_FUNCTION_FTYPE_SCHEMA_XPATH) == 0)) {
                 if(old_value && !old_value->dflt) {
                     rc = sr_set_item(session, old_value->xpath, old_value, 0);
diff --git a/ntsimulator/ntsim-ng/core/app/nf_oran_du.c b/ntsimulator/ntsim-ng/core/app/nf_oran_du.c
index 2bdd63c..4c429f4 100644
--- a/ntsimulator/ntsim-ng/core/app/nf_oran_du.c
+++ b/ntsimulator/ntsim-ng/core/app/nf_oran_du.c
@@ -116,7 +116,7 @@
     if(value_count) {
         log_add_verbose(2, "pm jobs already found (%d). cleaning up for fresh start...\n", value_count);
 
-        for(int i = 0; i < value_count; i++) {           
+        for(int i = 0; i < value_count; i++) {
             rc = sr_delete_item(session_running, values[i].xpath, 0);
             if(rc != SR_ERR_OK) {
                 log_error("sr_delete_item failed\n");
@@ -336,7 +336,7 @@
                 }
             }
             else if(oper == SR_OP_DELETED) {
-                int job_id = -1; 
+                int job_id = -1;
                 for(int i = 0; i < pm_jobs_count; i++) {
                     if(strcmp(pm_jobs[i]->id, old_value->data.string_val) == 0) {
                         job_id = i;
@@ -350,7 +350,7 @@
 
                 pm_job_t *job = pm_jobs[job_id];
                 job->terminate = 1;
-                
+
                 //remove from list
                 for(int i = job_id; i < pm_jobs_count - 1; i++) {
                     pm_jobs[i] = pm_jobs[i + 1];
@@ -395,7 +395,7 @@
         sleep(1);
     }
     log_add_verbose(1, "pm_job_thread_routine[%s] finished waiting...\n", job->id);
-    
+
     char *xpath_to_get = 0;
     asprintf(&xpath_to_get, "%s[id='%s']", NTS_NF_ORAN_DU_PM_JOBS_SCHEMA_XPATH, job->id);
     if(xpath_to_get == 0) {
@@ -461,7 +461,7 @@
 
         if(strcmp(chd->schema->name, "performance-metrics") == 0) {
             const char *val = ((const struct lyd_node_leaf_list *)chd)->value_str;
-            
+
             job->performance_metrics_count++;
             job->performance_metrics = (char **)realloc(job->performance_metrics, sizeof(char **) * job->performance_metrics_count);
             if(job->performance_metrics == 0) {
@@ -534,7 +534,7 @@
     asprintf(&details.field_value[3], "%d", job->granularity_period);
     details.field_name[4] = strdup("%%job-id%%");
     details.field_value[4] = strdup(job->id);
-    
+
     long int now = get_microseconds_since_epoch() / 1000000;
     long int start_time = now - (now % job->granularity_period);
     long int end_time = start_time + job->granularity_period;
@@ -546,7 +546,7 @@
     asprintf(&details.field_value[6], "%lu", end_time);
 
     details.field_name[7] = strdup("%%starttime-literal%%");
-    
+
     struct tm tm = *localtime(&now);
     asprintf(&details.field_value[7], "%04d-%02d-%02dT%02d:%02d:%02d.%01dZ",
                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
@@ -604,7 +604,7 @@
 
 static void nf_du_template_free(nf_du_template_details_t *details) {
     assert(details);
-    
+
     for(int j = 0; j < details->field_count; j++) {
         free(details->field_name[j]);
         free(details->field_value[j]);
@@ -631,7 +631,7 @@
 
     char **vars = 0;
     int vars_count = 0;
-    
+
     char **funcs = 0;
     int funcs_count = 0;
 
@@ -831,7 +831,7 @@
                 log_error("str_replace failed\n");
                 return 0;
             }
-            
+
             int nl = strlen(ci) + 2;   //\0 and perhaps comma
             char *ret2 = (char *)malloc(sizeof(char) * (strlen(ret) + nl));
             if(ret2 == 0) {
@@ -860,7 +860,7 @@
     }
     else if(strcmp(function, "$$uint32_counter$$") == 0) {
         char *ret = 0;
-        
+
         asprintf(&ret, "%d", details->job->stream->counter);
         details->job->stream->counter++;
         return ret;
diff --git a/ntsimulator/ntsim-ng/core/app/nf_oran_ru_supervision.c b/ntsimulator/ntsim-ng/core/app/nf_oran_ru_supervision.c
new file mode 100644
index 0000000..7d12fce
--- /dev/null
+++ b/ntsimulator/ntsim-ng/core/app/nf_oran_ru_supervision.c
@@ -0,0 +1,159 @@
+/*************************************************************************
+*
+* Copyright 2021 highstreet technologies GmbH and others
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+***************************************************************************/
+
+#define _GNU_SOURCE
+
+#include "nf_oran_ru_supervision.h"
+#include "utils/log_utils.h"
+#include "utils/sys_utils.h"
+#include "utils/nts_utils.h"
+#include "utils/rand_utils.h"
+#include "utils/http_client.h"
+#include <stdio.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include <sysrepo.h>
+#include <sysrepo/values.h>
+#include <libnetconf2/netconf.h>
+#include <libyang/libyang.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "core/framework.h"
+#include "core/context.h"
+#include "core/session.h"
+#include "core/xpath.h"
+
+int notification_timer_seconds=60;
+int supervision_timer_seconds=10;
+
+static pthread_t o_ran_supervision_thread = NULL;
+static void *o_ran_supervision_thread_routine(void *arg);
+
+static int
+supervision_watchdog_reset_rpc_cb(sr_session_ctx_t *session, const char *path, const sr_val_t *input, const size_t input_cnt,
+        sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data)
+{
+    size_t i;
+
+    (void)session;
+    (void)event;
+    (void)request_id;
+    (void)private_data;
+
+    bool notification_default = true;
+    bool supervision_default = true;
+
+    for (i = 0; i < input_cnt; ++i) {
+        if (!strcmp(input[i].xpath, "/o-ran-supervision:supervision-watchdog-reset/supervision-notification-interval")) {
+            notification_timer_seconds = input[i].data.uint16_val;
+            notification_default = false;
+        }
+        else if (!strcmp(input[i].xpath, "/o-ran-supervision:supervision-watchdog-reset/guard-timer-overhead")) {
+            supervision_timer_seconds = input[i].data.uint16_val;
+            supervision_default = false;
+        }
+    }
+
+    if (notification_default) {
+        notification_timer_seconds = 60;
+    }
+    if (supervision_default) {
+        supervision_timer_seconds = 10;
+    }
+
+    if (!strcmp(path, "/o-ran-supervision:supervision-watchdog-reset")) {
+        *output = malloc(sizeof **output);
+        *output_cnt = 1;
+
+        (*output)[0].xpath = strdup("/o-ran-supervision:supervision-watchdog-reset/next-update-at");
+        (*output)[0].type = SR_STRING_T;
+        (*output)[0].dflt = 0;
+        (*output)[0].data.string_val = get_current_date_and_time_delay_seconds(notification_timer_seconds);
+    }
+
+    if (o_ran_supervision_thread != NULL) {
+        pthread_cancel(o_ran_supervision_thread);
+        o_ran_supervision_thread = NULL;
+    }
+
+    if(pthread_create(&o_ran_supervision_thread, 0, o_ran_supervision_thread_routine, 0)) {
+        log_error("could not create thread for o-ran-supervision\n");
+        return NTS_ERR_FAILED;
+    }
+
+    return NTS_ERR_OK;
+}
+
+int nf_oran_ru_supervision_init(void) {
+    int rc;
+
+    rc = sr_rpc_subscribe(session_running, O_RAN_SUPERVISION_WATCHDOG_RESET_RPC_SCHEMA_XPATH, supervision_watchdog_reset_rpc_cb, NULL, 0, 0, &session_subscription);
+    if (rc != SR_ERR_OK) {
+        log_add_verbose(1, "Subscribing for RPC \"%s\" failed.\n", O_RAN_SUPERVISION_WATCHDOG_RESET_RPC_SCHEMA_XPATH);
+        return NTS_ERR_FAILED;
+    }
+
+    if(pthread_create(&o_ran_supervision_thread, 0, o_ran_supervision_thread_routine, 0)) {
+        log_error("could not create thread for o-ran-supervision\n");
+        return NTS_ERR_FAILED;
+    }
+
+    return NTS_ERR_OK;
+}
+
+static void *o_ran_supervision_thread_routine(void *arg) {
+    int notification_counter = notification_timer_seconds;
+    int supervision_counter = notification_timer_seconds + supervision_timer_seconds;
+    int rc;
+
+    log_add_verbose(1, "Starting thread for o-ran-supervision...\n");
+
+    struct lyd_node *notif = NULL;
+    notif = lyd_new_path(NULL, session_context, O_RAN_SUPERVISION_NOTIFICATION_SCHEMA_XPATH, NULL, 0, 0);
+    if (!notif) {
+        log_error("Creating notification \"%s\" failed.\n", O_RAN_SUPERVISION_NOTIFICATION_SCHEMA_XPATH);
+        return NTS_ERR_FAILED;
+    }
+
+    while (supervision_counter > 0) {
+        sleep(1);
+        supervision_counter--;
+        notification_counter--;
+
+        if (notification_counter == 0) {
+            log_add_verbose(1, "Sending o-ran-supervision supervision-notification..\n");
+            rc = sr_event_notif_send_tree(session_running, notif);
+            if (rc != SR_ERR_OK) {
+                lyd_free(notif);
+                return NTS_ERR_FAILED;
+            }
+        }
+    }
+
+    log_add_verbose(1, "Failed to receive watchdog reset, terminating supervision timer for o-ran-supervision...\n");
+    pthread_exit((void *)1);
+}
+
+void nf_oran_ru_supervision_free(void) {
+
+}
+
diff --git a/ntsimulator/ntsim-ng/core/app/nf_oran_ru_supervision.h b/ntsimulator/ntsim-ng/core/app/nf_oran_ru_supervision.h
new file mode 100644
index 0000000..f663d73
--- /dev/null
+++ b/ntsimulator/ntsim-ng/core/app/nf_oran_ru_supervision.h
@@ -0,0 +1,21 @@
+/*************************************************************************
+*
+* Copyright 2023 highstreet technologies GmbH and others
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+***************************************************************************/
+
+#pragma once
+
+int nf_oran_ru_supervision_init(void);
+void nf_oran_ru_supervision_free(void);
diff --git a/ntsimulator/ntsim-ng/core/xpath.h b/ntsimulator/ntsim-ng/core/xpath.h
index 984ed9b..b74bb90 100644
--- a/ntsimulator/ntsim-ng/core/xpath.h
+++ b/ntsimulator/ntsim-ng/core/xpath.h
@@ -96,3 +96,7 @@
 
 #define NC_NOTIFICATIONS_MODULE                                 "nc-notifications"
 #define NC_NOTIFICATIONS_STREAMS_SCHEMA_XPATH                   "/nc-notifications:netconf/streams"
+
+#define O_RAN_SUPERVISION_NOTIFICATION_SCHEMA_XPATH             "/o-ran-supervision:supervision-notification"
+#define O_RAN_SUPERVISION_NOTIFICATION_SESSION_ID_SCHEMA_XPATH  "/o-ran-supervision:supervision-notification/session-id"
+#define O_RAN_SUPERVISION_WATCHDOG_RESET_RPC_SCHEMA_XPATH       "/o-ran-supervision:supervision-watchdog-reset"
diff --git a/ntsimulator/ntsim-ng/utils/sys_utils.c b/ntsimulator/ntsim-ng/utils/sys_utils.c
index 2b93d52..66ad6d3 100644
--- a/ntsimulator/ntsim-ng/utils/sys_utils.c
+++ b/ntsimulator/ntsim-ng/utils/sys_utils.c
@@ -135,6 +135,25 @@
     return date_and_time;
 }
 
+char *get_current_date_and_time_delay_seconds(int seconds) {
+    char *date_and_time = 0;
+
+	time_t t = time(0);
+	struct tm tm = *localtime(&t);
+	struct timeval tv;
+
+	gettimeofday(&tv, 0);
+
+    tm.tm_sec += seconds;
+    mktime(&tm);
+
+	asprintf(&date_and_time, "%04d-%02d-%02dT%02d:%02d:%02d.0Z",
+                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+    return date_and_time;
+}
+
 long int get_microseconds_since_epoch(void) {
     time_t t = time(0);
     struct timeval tv;
diff --git a/ntsimulator/ntsim-ng/utils/sys_utils.h b/ntsimulator/ntsim-ng/utils/sys_utils.h
index eea514c..152f395 100644
--- a/ntsimulator/ntsim-ng/utils/sys_utils.h
+++ b/ntsimulator/ntsim-ng/utils/sys_utils.h
@@ -38,6 +38,7 @@
 
 int get_int_from_string_with_default(const char *string, int default_value);
 char *get_current_date_and_time(void);
+char *get_current_date_and_time_delay_seconds(int seconds);
 long int get_microseconds_since_epoch(void);
 
 //networking functions