From af7aa682517d3ccaace9a089194e6a4333f4cdaa Mon Sep 17 00:00:00 2001 From: PrakashH Date: Mon, 28 Jan 2019 17:14:52 +0000 Subject: [PATCH] VES EVEL Library VES 5.4.1 enhancements HB-Fault-Measurement-Syslog-2Collector enhancement. Issue-ID: CERT-17 Change-Id: Ieff01ee5461e50c7e7d07d10ec66c1c97cf8c5b1 Signed-off-by: PrakashH --- .../evel-library/code/VESreporting_HB/LICENSE.TXT | 22 + .../evel-library/code/VESreporting_HB/Makefile | 52 + .../evel-library/code/VESreporting_HB/README.md | 37 + .../evel/evel-library/code/VESreporting_HB/dep.xml | 25 + .../evel-library/code/VESreporting_HB/go-client.sh | 6 + .../code/VESreporting_HB/go-client_2_collectors.sh | 12 + .../code/VESreporting_HB/hb_config.json | 13 + .../evel/evel-library/code/VESreporting_HB/jsmn.c | 615 +++++++++ .../evel/evel-library/code/VESreporting_HB/jsmn.h | 134 ++ .../evel/evel-library/code/VESreporting_HB/pom.xml | 83 ++ .../VESreporting_HB/sample_heartbeat_events.txt | 26 + .../code/VESreporting_HB/ves_heartbeat_reporter.c | 255 ++++ .../code/VESreporting_fault/LICENSE.TXT | 22 + .../evel-library/code/VESreporting_fault/Makefile | 52 + .../evel-library/code/VESreporting_fault/README.md | 37 + .../evel-library/code/VESreporting_fault/dep.xml | 25 + .../code/VESreporting_fault/flt_config.json | 71 ++ .../code/VESreporting_fault/go-client.sh | 6 + .../VESreporting_fault/go-client_2_collectors.sh | 12 + .../evel-library/code/VESreporting_fault/jsmn.c | 672 ++++++++++ .../evel-library/code/VESreporting_fault/jsmn.h | 136 ++ .../evel-library/code/VESreporting_fault/pom.xml | 83 ++ .../VESreporting_fault/sample_fault_events.txt | 98 ++ .../code/VESreporting_fault/ves_fault_reporter.c | 1328 ++++++++++++++++++++ .../code/VESreporting_syslog/LICENSE.TXT | 22 + .../evel-library/code/VESreporting_syslog/Makefile | 52 + .../code/VESreporting_syslog/README.md | 37 + .../evel-library/code/VESreporting_syslog/dep.xml | 25 + .../code/VESreporting_syslog/go-client.sh | 6 + .../VESreporting_syslog/go-client_2_collectors.sh | 12 + .../evel-library/code/VESreporting_syslog/jsmn.c | 672 ++++++++++ .../evel-library/code/VESreporting_syslog/jsmn.h | 136 ++ .../evel-library/code/VESreporting_syslog/pom.xml | 83 ++ .../VESreporting_syslog/sample_syslog_event.txt | 30 + .../code/VESreporting_syslog/syslog_config.json | 20 + .../code/VESreporting_syslog/ves_syslog_reporter.c | 375 ++++++ .../evel-library/code/VESreporting_vFW/LICENSE.TXT | 22 + .../evel-library/code/VESreporting_vFW/Makefile | 52 + .../evel-library/code/VESreporting_vFW/README.md | 37 + .../evel-library/code/VESreporting_vFW/dep.xml | 25 + .../code/VESreporting_vFW/go-client.sh | 6 + .../VESreporting_vFW/go-client_2_collectors.sh | 12 + .../evel/evel-library/code/VESreporting_vFW/jsmn.c | 672 ++++++++++ .../evel/evel-library/code/VESreporting_vFW/jsmn.h | 136 ++ .../code/VESreporting_vFW/meas_config.json | 50 + .../evel-library/code/VESreporting_vFW/pom.xml | 83 ++ .../VESreporting_vFW/sample_measurement_event.txt | 62 + .../VESreporting_vFW/vpp_measurement_reporter.c | 685 ++++++++++ .../evel/evel-library/code/evel_library/evel.h | 38 + .../evel-library/code/evel_library/evel_event.c | 58 + .../code/evel_library/evel_event_mgr.c | 325 ++--- .../code/evel_library/evel_heartbeat_fields.c | 7 +- .../VES5.0/evel/evel-library/libs/x86_64/libevel.a | Bin 0 -> 562325 bytes vnfs/VESreporting_vFW5.0/Makefile | 1 + vnfs/VESreporting_vFW5.0/README.md | 4 +- vnfs/VESreporting_vFW5.0/go-client.sh | 3 +- vnfs/VESreporting_vFW5.0/go-client_2_collector.sh | 8 + .../VESreporting_vFW5.0/vpp_measurement_reporter.c | 231 +--- vnfs/VESreporting_vLB5.0/Makefile | 10 +- vnfs/VESreporting_vLB5.0/README.md | 3 +- vnfs/VESreporting_vLB5.0/go-client_2_collector.sh | 8 + .../VESreporting_vLB5.0/vpp_measurement_reporter.c | 4 +- 62 files changed, 7485 insertions(+), 349 deletions(-) create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/LICENSE.TXT create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/Makefile create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/README.md create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/dep.xml create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client.sh create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client_2_collectors.sh create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/hb_config.json create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.h create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/pom.xml create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/sample_heartbeat_events.txt create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/ves_heartbeat_reporter.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/LICENSE.TXT create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/Makefile create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/README.md create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/dep.xml create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/flt_config.json create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client.sh create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client_2_collectors.sh create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.h create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/pom.xml create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/sample_fault_events.txt create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/ves_fault_reporter.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/LICENSE.TXT create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/Makefile create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/README.md create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/dep.xml create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client.sh create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client_2_collectors.sh create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.h create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/pom.xml create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/sample_syslog_event.txt create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/syslog_config.json create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/ves_syslog_reporter.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/LICENSE.TXT create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/Makefile create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/README.md create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/dep.xml create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client.sh create mode 100755 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client_2_collectors.sh create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.c create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.h create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/meas_config.json create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/pom.xml create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/sample_measurement_event.txt create mode 100644 vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/vpp_measurement_reporter.c create mode 100755 vnfs/VES5.0/evel/evel-library/libs/x86_64/libevel.a create mode 100755 vnfs/VESreporting_vFW5.0/go-client_2_collector.sh create mode 100755 vnfs/VESreporting_vLB5.0/go-client_2_collector.sh diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/LICENSE.TXT b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/LICENSE.TXT new file mode 100644 index 00000000..16285cd2 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/LICENSE.TXT @@ -0,0 +1,22 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * Unless otherwise specified, all software contained herein is + * 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. + * ============LICENSE_END============================================ + * + * ECOMP is trademark and service mark of AT&T Intellectual Property. + * + */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/Makefile b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/Makefile new file mode 100644 index 00000000..e0077597 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/Makefile @@ -0,0 +1,52 @@ +############################################################################# +# +# Copyright © 2018 AT&T Intellectual Property. All rights reserved. +# +# 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. +# +############################################################################# + +CC=gcc +ARCH=$(shell getconf LONG_BIT) +CODE_ROOT=$(CURDIR)/../.. +#CODE_ROOT=../code/evel-library +LIBS_DIR=$(CODE_ROOT)/libs/x86_$(ARCH) +#LIBS_DIR=/usr/lib +INCLUDE_DIR= -I $(CODE_ROOT)/code/evel_library -I . + +#****************************************************************************** +# Standard compiler flags. * +#****************************************************************************** +CPPFLAGS= +CFLAGS=-Wall -g -fPIC +FILEOBJLIST= jsmn.o ves_heartbeat_reporter.o + +all: ves_heartbeat_reporter + +clean: + rm -f *.o ves_heartbeat_reporter + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE_DIR) -c $< -o $@ + +jsmn.o: jsmn.c jsmn.h +ves_heartbeat_reporter.o: ves_heartbeat_reporter.c + +ves_heartbeat_reporter: $(FILEOBJLIST) + $(CC) $(CPPFLAGS) $(CFLAGS) -o ves_heartbeat_reporter \ + -L $(LIBS_DIR) \ + $(FILEOBJLIST) \ + -lpthread \ + -level \ + -lcurl + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/README.md b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/README.md new file mode 100644 index 00000000..a0d8e762 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/README.md @@ -0,0 +1,37 @@ + +PROJECT DESCRIPTION + +--- +This project contains the source code and scripts for the periodic generation of heartbeat events. The folder contains: + + - README.md: this file. + + - LICENSE.TXT: the license text. + + - ves_heartbeat_reporter.c and other .c files: source code that uses the ECOMP Vendor Event Listener Library (VES) to generate the periodic hertbeat events. It reads hb_config.json file for parameter values and poppulate the heartbeat event. If eventName parameter value is not given, the application terminates. If reportingEntityName and sourceName parameter values are not given, then it gets the hostname and poppulates it. If heartbeatInterval is not given, it defaults to 60 seconds. + + - Makefile: makefile that compiles ves_heartbeat_reporter.c and generates ves_heartbeat_reporter binary. + + - go-client.sh/go-client_2_collectors.sh: bash script that starts up the ves_heartbeat_reporter. It reads input parameters like DCAE IP address and port from configuration files contained in /opt/config. Based on the collector configuration, use go-client.sh for single collector configuration, or use go-client_2_collectors.sh for 2 collectors configuration. + + +USAGE +----- + +Update the configuration files with proper parameters values so that events generated would contain those values + +To run the ves_heartbeat_reporter in single collector configuration, please execute the following steps: + + - Make the go-client.sh script executable + chmod +x go-client.sh + + - Run the go-client.sh script + ./go-client.sh + +For 2 collectors configuration, please execute following steps: + + - Make the go-client.sh script executable + chmod +x go-client_2_collectors.sh + + - Run the go-client_2_collectors.sh script + ./go-client_2_collectors.sh diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/dep.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/dep.xml new file mode 100644 index 00000000..fc18229f --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/dep.xml @@ -0,0 +1,25 @@ + + demo + + tar.gz + + + + + . + / + + *.sh + *.md + *.TXT + *.c + Makefile + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client.sh new file mode 100755 index 00000000..22b930d1 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +./ves_heartbeat_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client_2_collectors.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client_2_collectors.sh new file mode 100755 index 00000000..b3747e29 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/go-client_2_collectors.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" + +#Usage for 2 collectors: +#./ves_heartbeat_reporter | | + +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +DCAE_COLLECTOR_IP2=$(cat /opt/config/dcae_collector_ip2.txt) +DCAE_COLLECTOR_PORT2=$(cat /opt/config/dcae_collector_port2.txt) +./ves_heartbeat_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT $DCAE_COLLECTOR_IP2 $DCAE_COLLECTOR_PORT2 diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/hb_config.json b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/hb_config.json new file mode 100644 index 00000000..db32d2db --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/hb_config.json @@ -0,0 +1,13 @@ +{ + "tmp_directParameters": { + "eventType": "platform", + "eventName": "Heartbeat_VNF-AT&T_heartbeat", + "nfcNamingCode": "ssc", + "nfNamingCode": "scfx", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "heartbeatInterval": 10 + } +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.c new file mode 100644 index 00000000..f663403e --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.c @@ -0,0 +1,615 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * Unless otherwise specified, all software contained herein is + * 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. + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + ****************************************************************************/ +#include +#include +#include +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +//#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +//#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { +//printf("jsmn_fill_token:: start-%d, end-%d\n", start, end); + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { +//printf("jsmn_parse_primitive:: the char is - %c\n", js[parser->pos]); + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + char d = js[parser->pos + 1]; +// char e = js[parser->pos + 2]; +//printf("jsmn_parse_string: value-%c, pos-%d\n", c,parser->pos); + + /* Quote: end of string */ +// if (c == '\"') { + if (d == '\"') { +// if ((d == '\"')&&((e == ' ')||(e == ','))) { +parser->pos++; +//printf("jsmn_parse_string: end of string\n"); + if (tokens == NULL) { +//printf("jsmn_parse_string: end tokens is NULL\n"); + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); +//printf("jsmn_parse_string: Allocated tokens \n"); + if (token == NULL) { +//printf("jsmn_parse_string: Allocated tokens is NULL\n"); + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +//printf("jsmn_parse_string: Allocated tokens is filled\n"); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; +//printf("jsmn_parse_string: value - %c, POS-%3d \n",c, js[parser->pos]); + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; +printf("jsmn_parse_string: exiting with ERROR_PART, pos-%d", parser->pos); + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; +//printf("jsmn_parse: value of c - %c\n",c); + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +//#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// if (token->type != type) { +// return JSMN_ERROR_INVAL; +// } +// parser->toksuper = -1; +// token->end = parser->pos + 1; +// break; +// } +// } +// /* Error if unmatched closing bracket */ +// if (i == -1) return JSMN_ERROR_INVAL; +// for (; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// parser->toksuper = i; +// break; +// } +// } +//#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; +//printf("jsmn_parse: value of c is :: - %c\n",c); + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +//#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { +// if (tokens[i].start != -1 && tokens[i].end == -1) { +// parser->toksuper = i; +// break; +// } +// } +// } +//#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { +//printf("index -%2d, start is %3d, end is %3d\n", i, tokens[i].start, tokens[i].end); + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +int jsoneq(const char *json, jsmntok_t *tok, const char *s) +{ + if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} + +void printToken(char * js, jsmntok_t * tokens, int numToken) +{ + for (int i = 1; i < numToken; i++) + { + printf("Token number-%2d, parent-%2d, type-%d size-%2d, parameter -", i, tokens[i].parent, tokens[i].type, tokens[i].size); + if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) { + printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start); + } else if (tokens[i].type == JSMN_ARRAY) { + printf("[%d elems]\n", tokens[i].size); + } else if (tokens[i].type == JSMN_OBJECT) { + printf("{%d elems}\n", tokens[i].size); + } else { + printf("value?? - "); + TOKEN_PRINT(tokens[i]); + } + } +} + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + { + continue; + } + + if(tokens[i+1].type != JSMN_PRIMITIVE) + return 3; //Wrong parameter type + +// printf("INT parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], val); + *value = atoi(val); +// printf(" - %d\n", *value); + + return 0; //success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +void parseDirectParameters(char * js, jsmntok_t * tokens, int numToken) +{ + int i = 0; + int dpToken = 0; + char param[128]; + char value[128]; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], "directParameters") == 0) + break; + } + + if (i < numToken) + { + dpToken = ++i; + } + + for (int i = 1; i < numToken; i++) + { + memset(param, 0, 128); + memset(value, 0, 128); + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + TOKEN_COPY(js, tokens[i], param); +// printf("parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], value); +// printf(" - %s\n", value); + } + } +} + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_ARRAY) + { + *numElements = tokens[i+1].size; +// printf("[%d elems]\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k], arrayValue[k].arrayString); +// printf(" - %s\n", arrayValue[k].arrayString); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING) + { + if(jsoneq(js, &tokens[i], param) != 0) + { + return 0; //Token present + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //Token Not present +} + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_OBJECT) + { + *numElements = tokens[i+1].size; +// printf("{%d elems}\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k*2], keyValueResultList[k].keyStr); +// printf("Key - %s", keyValueResultList[k].keyStr); + TOKEN_COPY(js, tokens[i+3+k*2], keyValueResultList[k].valStr); +// printf("Value - %s\n", keyValueResultList[k].valStr); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.h b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.h new file mode 100644 index 00000000..67034943 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/jsmn.h @@ -0,0 +1,134 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +typedef struct arrayValues { + char arrayString[32]; +} ARRAYVAL; + +typedef struct keyValResult { + char keyStr[80]; + char valStr[250]; + char resultStr[80]; +} KEYVALRESULT; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +//#ifdef JSMN_PARENT_LINKS + int parent; +//#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +#define TOKEN_EQ(t, tok_start, tok_end, tok_type) \ + ((t).start == tok_start \ + && (t).end == tok_end \ + && (t).type == (tok_type)) + +#define TOKEN_STRING(js, t, s) \ + (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \ + && strlen(s) == (t).end - (t).start) + +#define TOKEN_COPY(js, t, s) \ + strncpy(s, js+(t).start, (t).end - (t).start) + +#define TOKEN_PRINT(t) \ + printf("start: %d, end: %d, type: %d, size: %d\n", \ + (t).start, (t).end, (t).type, (t).size) + + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +int jsoneq(const char *json, jsmntok_t *tok, const char *s); + +void printToken(char * js, jsmntok_t * tokens, int numToken); + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize); + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value); + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements); + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam); + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/pom.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/pom.xml new file mode 100644 index 00000000..f9f42ebb --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/pom.xml @@ -0,0 +1,83 @@ + + + + + + org.onap.demo.vnf + demo-aggregator + 1.2.0-SNAPSHOT + ../../pom.xml + + + 4.0.0 + org.onap.demo.vnf.ves5 + ves_vfw_reporting + + + + + + maven-jar-plugin + 2.3.2 + + + default-jar + never + + + + + + maven-assembly-plugin + 2.5.3 + + dep.xml + + + + create-archive + package + + single + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + none + + + + true + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/sample_heartbeat_events.txt b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/sample_heartbeat_events.txt new file mode 100644 index 00000000..f318fa7e --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/sample_heartbeat_events.txt @@ -0,0 +1,26 @@ +{ + "event": { + "commonEventHeader": { + "domain": "heartbeat", + "eventId": "heartbeat000000001", + "eventName": "Heartbeat_VNF-AT&T_heartbeat", + "eventType": "platform", + "lastEpochMicrosec": 1548490889528042, + "nfNamingCode": "scfx", + "nfcNamingCode": "ssc", + "priority": "Normal", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sequence": 0, + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "startEpochMicrosec": 1548490879514800, + "version": 3.0 + }, + "heartbeatField": { + "heartbeatFieldsVersion": 1.0, + "heartbeatInterval": 10 + } + } +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/ves_heartbeat_reporter.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/ves_heartbeat_reporter.c new file mode 100644 index 00000000..70779252 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_HB/ves_heartbeat_reporter.c @@ -0,0 +1,255 @@ +/*************************************************************************//** + * + * Copyright © 2018 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "jsmn.h" +#include "evel.h" + +#define BUFSIZE 128 +#define MAX_BUFFER_SIZE 4096 +#define MAX_TOKENS 1000 + +void *HeartbeatThread(void *threadarg); + +unsigned long long epoch_start = 0; + +int main(int argc, char** argv) +{ + char* fqdn = argv[1]; + int port = atoi(argv[2]); + int i=0; + int rc; + pthread_attr_t attr; + pthread_t hb_thread; + char* fqdn2 = NULL; + int port2 = 0; + + if(argc == 5) + { + fqdn2 = argv[3]; + port2 = atoi(argv[4]); + } + + if (!((argc == 3) || (argc == 5))) + { + fprintf(stderr, "Usage: %s | | \n", argv[0]); + fprintf(stderr, "OR\n"); + fprintf(stderr, "Usage: %s | \n", argv[0]); + exit(-1); + } + + /**************************************************************************/ + /* Initialize */ + /**************************************************************************/ + if(evel_initialize(fqdn, /* FQDN */ + port, /* Port */ + fqdn2, /* Backup FQDN */ + port2, /* Backup port */ + NULL, /* optional path */ + NULL, /* optional topic */ + 100, /* Ring Buffer size */ + 0, /* HTTPS? */ + NULL, /* cert file */ + NULL, /* key file */ + NULL, /* ca info */ + NULL, /* ca file */ + 0, /* verify peer */ + 0, /* verify host */ + "sample1", /* Username */ + "sample1", /* Password */ + "sample1", /* Username2 */ + "sample1", /* Password2 */ + NULL, /* Source ip */ + NULL, /* Source ip2 */ + EVEL_SOURCE_VIRTUAL_MACHINE, /* Source type */ + "vHeartbeat", /* Role */ + 1)) /* Verbosity */ + { + fprintf(stderr, "\nFailed to initialize the EVEL library!!!\n"); + exit(-1); + } + else + { + printf("\nInitialization completed\n"); + } + + /* Initialize and set thread detached attribute */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + printf("Main:Creating thread \n"); + rc = pthread_create(&hb_thread, NULL, HeartbeatThread, &i); + if (rc) + { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + printf("Main:Created HB thread \n"); + + pthread_join(hb_thread, NULL); + + evel_terminate(); + printf("Terminated\n"); + return 0; +} + +void *HeartbeatThread(void *threadarg) +{ + + EVENT_HEARTBEAT_FIELD * event = NULL; + EVENT_HEADER* hb_header = NULL; + EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; + + int numToken; + jsmntok_t tokens[MAX_TOKENS]; + char js[MAX_BUFFER_SIZE]; + + jsmn_parser p; + char ch[BUFSIZE]; + int ret = 0; + char stringVal[BUFSIZE]; + char eName[BUFSIZE]; + int hb_interval; + + char event_id1[10] = "heartbeat"; + char event_id2[15] = {0}; + int event_id3 = 0; + char event_id[BUFSIZE] = {0}; + + sleep(1); + printf("Running HB thread \n"); + fflush(stdout); + + while(1) + { + + FILE * file = fopen("hb_config.json", "r"); + + memset(js, 0, MAX_BUFFER_SIZE); + memset(ch, 0, BUFSIZE); + + while((fgets(ch, (BUFSIZE-1), file)) !=NULL) + { + strcat(js, ch); + memset(ch, 0, BUFSIZE); + } +// printf("the file content is \n %s \n", js); + + jsmn_init(&p); + numToken = jsmn_parse(&p, js, strlen(js), tokens, MAX_TOKENS); + printf("Token count-%d\n", numToken); + +// printToken(js,tokens, numToken); + + ret = getStringToken(js, tokens, numToken, "eventName", "tmp_directParameters", eName, BUFSIZE); + if (ret != 0) + { + printf("Missing mandatory parameters - eventName is not there in tmp_directParameters. Exiting...\n"); + exit(1); + } + + ret = getIntToken(js, tokens, numToken, "heartbeatInterval", "tmp_directParameters", &hb_interval); + if (ret != 0) + { + printf("The parameter heartbeatInterval is not defined, defaulted to 60 seconds\n"); + hb_interval = 60; + } + + /***************************************************************************/ + /* Heartbeat */ + /***************************************************************************/ + memset(event_id, 0, BUFSIZE); + memset(event_id2, 0, 15); + sprintf(event_id2, "%09d", event_id3++); + strcat(event_id, event_id1); + strcat(event_id, event_id2); + + event = evel_new_heartbeat_field(hb_interval, eName, event_id); + if (event != NULL) + { + hb_header = (EVENT_HEADER *)event; + + ret = getStringToken(js, tokens, numToken, "eventType", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + { + evel_header_type_set(&event->header, stringVal); + } + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + unsigned long long epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; + + evel_start_epoch_set(&event->header, epoch_start); + evel_last_epoch_set(&event->header, epoch_now); + epoch_start = epoch_now; + + ret = getStringToken(js, tokens, numToken, "nfcNamingCode", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_nfcnamingcode_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "nfNamingCode", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_nfnamingcode_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "reportingEntityName", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_reporting_entity_name_set(&event->header, stringVal); + else + { + printf("Missing mandatory parameters - reportingEntityName is not there in tmp_directParameters\n"); + printf("Defaulting reportingEntityName to hostname\n"); + } + + ret = getStringToken(js, tokens, numToken, "reportingEntityId", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_reporting_entity_id_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "sourceId", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_source_id_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "sourceName", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_source_name_set(&event->header, stringVal); + else + { + printf("Missing mandatory parameters - sourceName is not there in tmp_directParameters\n"); + printf("Defaulting sourceName to hostname\n"); + } + + evel_rc = evel_post_event(hb_header); + if (evel_rc != EVEL_SUCCESS) + { + EVEL_ERROR("Post failed %d (%s)", evel_rc, evel_error_string()); + } + } + else + { + EVEL_ERROR("New Heartbeat failed"); + } + printf(" Processed Heartbeat\n"); + + sleep(hb_interval); + } +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/LICENSE.TXT b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/LICENSE.TXT new file mode 100644 index 00000000..16285cd2 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/LICENSE.TXT @@ -0,0 +1,22 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * Unless otherwise specified, all software contained herein is + * 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. + * ============LICENSE_END============================================ + * + * ECOMP is trademark and service mark of AT&T Intellectual Property. + * + */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/Makefile b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/Makefile new file mode 100644 index 00000000..4837c7d1 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/Makefile @@ -0,0 +1,52 @@ +############################################################################# +# +# Copyright © 2019 AT&T Intellectual Property. All rights reserved. +# +# 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. +# +############################################################################# + +CC=gcc +ARCH=$(shell getconf LONG_BIT) +CODE_ROOT=$(CURDIR)/../.. +#CODE_ROOT=../code/evel-library +LIBS_DIR=$(CODE_ROOT)/libs/x86_$(ARCH) +#LIBS_DIR=/usr/lib +INCLUDE_DIR= -I $(CODE_ROOT)/code/evel_library -I . + +#****************************************************************************** +# Standard compiler flags. * +#****************************************************************************** +CPPFLAGS= +CFLAGS=-Wall -g -fPIC +FILEOBJLIST= jsmn.o ves_fault_reporter.o + +all: ves_fault_reporter + +clean: + rm -f *.o ves_fault_reporter + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE_DIR) -c $< -o $@ + +jsmn.o: jsmn.c jsmn.h +ves_fault_reporter.o: ves_fault_reporter.c + +ves_fault_reporter: $(FILEOBJLIST) + $(CC) $(CPPFLAGS) $(CFLAGS) -o ves_fault_reporter \ + -L $(LIBS_DIR) \ + $(FILEOBJLIST) \ + -lpthread \ + -level \ + -lcurl + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/README.md b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/README.md new file mode 100644 index 00000000..5a7ea414 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/README.md @@ -0,0 +1,37 @@ + +PROJECT DESCRIPTION + +--- +This project contains the source code and scripts for the generation of fault events. The folder contains: + + - README.md: this file. + + - LICENSE.TXT: the license text. + + - ves_fault_reporter.c and other .c files: source code that uses the ECOMP Vendor Event Listener Library (VES) to generate the fault events. Fault is generated based on the link status. If number of bytes transmitted/received is less than the low water mark, the fault is generated. The application reads flt_config.json file for parameter values and poppulate the fault event. If eventName, eventSourceType, vfStatus, specificProblem or alarmCondition parameter value is not given, the application terminates. If reportingEntityName and sourceName parameter values are not given, then it gets the hostname and poppulates it. If tmp_faultCheckInterval is not given, it defaults to 60 seconds. + + - Makefile: makefile that compiles ves_fault_reporter.c and generates ves_fault_reporter binary. + + - go-client.sh/go-client_2_collectors.sh: bash script that starts up the ves_fault_reporter. It reads input parameters like DCAE IP address and port from configuration files contained in /opt/config. Based on the collector configuration, use go-client.sh for single collector configuration, or use go-client_2_collectors.sh for 2 collectors configuration. + + +USAGE +----- + +Update the configuration files with proper parameters values so that events generated would contain those values + +To run the ves_fault_reporter in single collector configuration, please execute the following steps: + + - Make the go-client.sh script executable + chmod +x go-client.sh + + - Run the go-client.sh script + ./go-client.sh + +For 2 collectors configuration, please execute following steps: + + - Make the go-client.sh script executable + chmod +x go-client_2_collectors.sh + + - Run the go-client_2_collectors.sh script + ./go-client_2_collectors.sh diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/dep.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/dep.xml new file mode 100644 index 00000000..fc18229f --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/dep.xml @@ -0,0 +1,25 @@ + + demo + + tar.gz + + + + + . + / + + *.sh + *.md + *.TXT + *.c + Makefile + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/flt_config.json b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/flt_config.json new file mode 100644 index 00000000..90654ef5 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/flt_config.json @@ -0,0 +1,71 @@ +{ + "tmp_directParameters": { + "eventType": "applicationVnf", + "nfcNamingCode": "AFX", + "nfNamingCode": "AFX", + "priority": "Low", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "vfStatus": "Idle", + "tmp_device": ["lo", "enp0s3", "docker0"] + + }, + "tmp_indirectParameters": [ + "tmp_faultInstance01": { + "eventName": "Fault_vFirewall-AT&T_linkDownError", + "eventCategory": "link", + "eventSourceType": "router", + "tmp_init":{ + "tmp_t0BytesIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f2", + "tmp_t0BytesOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f10", + "tmp_t0PacketsIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f3", + "tmp_t0PacketsOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f11" + }, + "alarmInterfaceA": "$tmp_device", + "tmp_faultCheckInterval": 20, + "tmp_lowWaterMark": 100, + "tmp_command": { + "tmp_t1BytesIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f2", + "tmp_t1BytesOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f10", + "tmp_t1PacketsIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f3", + "tmp_t1PacketsOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f11" + }, + "tmp_BytesIn": "$(tmp_t1BytesIn - tmp_t0BytesIn)", + "tmp_PacketsIn": "$(tmp_t1PacketsIn - tmp_t0PacketsIn)", + "tmp_BytesOut": "$(tmp_t1BytesOut - tmp_t0BytesOut)", + "tmp_PacketsOut": "$(tmp_t1PacketsOut - tmp_t0PacketsOut)", + "tmp_alarmSetParameters": { + "specificProblem": "physical or logical connection to a remote router is down", + "eventSeverity": "MAJOR", + "alarmCondition": "link down trap_alarm" + }, + "tmp_alarmClearParameters": { + "specificProblem": "physical or logical connection to a remote router is up", + "eventSeverity": "NORMAL", + "alarmCondition": "link up trap_alarm" + } + }, + "tmp_faultInstance02": { + "eventName": "Fault_vFirewall-AT&T_serviceDownError", + "alarmInterfaceA": "afx@input.service", + "eventCategory": "other", + "eventSourceType": "virtualMachine", + "tmp_faultCheckInterval": 10, + "tmp_command": { + "tmp_cmd1": "/bin/systemctl is-active afx@input* |grep -E 'inactive|failed' | wc -l" + }, + "tmp_alarmSetParameters": { + "specificProblem": "service is down", + "eventSeverity": "MAJOR", + "alarmCondition": "service down trap_alarm" + }, + "tmp_alarmClearParameters": { + "specificProblem": "service is up", + "eventSeverity": "NORMAL", + "alarmCondition": "service up trap_alarm" + } + } + ] +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client.sh new file mode 100755 index 00000000..5a598821 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +./ves_fault_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client_2_collectors.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client_2_collectors.sh new file mode 100755 index 00000000..09d65f03 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/go-client_2_collectors.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" + +#Usage for 2 collectors: +#./ves_fault_reporter | | + +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +DCAE_COLLECTOR_IP2=$(cat /opt/config/dcae_collector_ip2.txt) +DCAE_COLLECTOR_PORT2=$(cat /opt/config/dcae_collector_port2.txt) +./ves_fault_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT $DCAE_COLLECTOR_IP2 $DCAE_COLLECTOR_PORT2 diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.c new file mode 100644 index 00000000..1d72ba4d --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.c @@ -0,0 +1,672 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * Unless otherwise specified, all software contained herein is + * 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. + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + ****************************************************************************/ +#include +#include +#include +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +//#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +//#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { +//printf("jsmn_fill_token:: start-%d, end-%d\n", start, end); + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { +//printf("jsmn_parse_primitive:: the char is - %c\n", js[parser->pos]); + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + char d = js[parser->pos + 1]; +// char e = js[parser->pos + 2]; +//printf("jsmn_parse_string: value-%c, pos-%d\n", c,parser->pos); + + /* Quote: end of string */ +// if (c == '\"') { + if (d == '\"') { +// if ((d == '\"')&&((e == ' ')||(e == ','))) { +parser->pos++; +//printf("jsmn_parse_string: end of string\n"); + if (tokens == NULL) { +//printf("jsmn_parse_string: end tokens is NULL\n"); + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); +//printf("jsmn_parse_string: Allocated tokens \n"); + if (token == NULL) { +//printf("jsmn_parse_string: Allocated tokens is NULL\n"); + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +//printf("jsmn_parse_string: Allocated tokens is filled\n"); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; +//printf("jsmn_parse_string: value - %c, POS-%3d \n",c, js[parser->pos]); + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; +printf("jsmn_parse_string: exiting with ERROR_PART, pos-%d", parser->pos); + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; +//printf("jsmn_parse: value of c - %c\n",c); + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +//#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// if (token->type != type) { +// return JSMN_ERROR_INVAL; +// } +// parser->toksuper = -1; +// token->end = parser->pos + 1; +// break; +// } +// } +// /* Error if unmatched closing bracket */ +// if (i == -1) return JSMN_ERROR_INVAL; +// for (; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// parser->toksuper = i; +// break; +// } +// } +//#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; +//printf("jsmn_parse: value of c is :: - %c\n",c); + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +//#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { +// if (tokens[i].start != -1 && tokens[i].end == -1) { +// parser->toksuper = i; +// break; +// } +// } +// } +//#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { +//printf("index -%2d, start is %3d, end is %3d\n", i, tokens[i].start, tokens[i].end); + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +int jsoneq(const char *json, jsmntok_t *tok, const char *s) +{ + if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} + +void printToken(char * js, jsmntok_t * tokens, int numToken) +{ + for (int i = 1; i < numToken; i++) + { + printf("Token number-%2d, parent-%2d, type-%d size-%2d, parameter -", i, tokens[i].parent, tokens[i].type, tokens[i].size); + if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) { + printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start); + } else if (tokens[i].type == JSMN_ARRAY) { + printf("[%d elems]\n", tokens[i].size); + } else if (tokens[i].type == JSMN_OBJECT) { + printf("{%d elems}\n", tokens[i].size); + } else { + printf("value?? - "); + TOKEN_PRINT(tokens[i]); + } + } +} + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + + +int getStringTokenV2(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * gParam,char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], gParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Grand Parent token not seen + } + + for (i=dpToken; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 2; //Parent token not seen + } + + for (i=dpToken; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + { + continue; + } + + if(tokens[i+1].type != JSMN_PRIMITIVE) + return 3; //Wrong parameter type + +// printf("INT parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], val); + *value = atoi(val); +// printf(" - %d\n", *value); + + return 0; //success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +void parseDirectParameters(char * js, jsmntok_t * tokens, int numToken) +{ + int i = 0; + int dpToken = 0; + char param[128]; + char value[128]; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], "directParameters") == 0) + break; + } + + if (i < numToken) + { + dpToken = ++i; + } + + for (int i = 1; i < numToken; i++) + { + memset(param, 0, 128); + memset(value, 0, 128); + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + TOKEN_COPY(js, tokens[i], param); +// printf("parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], value); +// printf(" - %s\n", value); + } + } +} + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_ARRAY) + { + *numElements = tokens[i+1].size; +// printf("[%d elems]\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k], arrayValue[k].arrayString); +// printf(" - %s\n", arrayValue[k].arrayString); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING) + { + if(jsoneq(js, &tokens[i], param) == 0) + { + return 0; //Token present + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //Token Not present +} + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_OBJECT) + { + *numElements = tokens[i+1].size; +// printf("{%d elems}\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k*2], keyValueResultList[k].keyStr); +// printf("Key - %s", keyValueResultList[k].keyStr); + TOKEN_COPY(js, tokens[i+3+k*2], keyValueResultList[k].valStr); +// printf("Value - %s\n", keyValueResultList[k].valStr); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.h b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.h new file mode 100644 index 00000000..f9d838b6 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/jsmn.h @@ -0,0 +1,136 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +typedef struct arrayValues { + char arrayString[32]; +} ARRAYVAL; + +typedef struct keyValResult { + char keyStr[80]; + char valStr[250]; + char resultStr[80]; +} KEYVALRESULT; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +//#ifdef JSMN_PARENT_LINKS + int parent; +//#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +#define TOKEN_EQ(t, tok_start, tok_end, tok_type) \ + ((t).start == tok_start \ + && (t).end == tok_end \ + && (t).type == (tok_type)) + +#define TOKEN_STRING(js, t, s) \ + (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \ + && strlen(s) == (t).end - (t).start) + +#define TOKEN_COPY(js, t, s) \ + strncpy(s, js+(t).start, (t).end - (t).start) + +#define TOKEN_PRINT(t) \ + printf("start: %d, end: %d, type: %d, size: %d\n", \ + (t).start, (t).end, (t).type, (t).size) + + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +int jsoneq(const char *json, jsmntok_t *tok, const char *s); + +void printToken(char * js, jsmntok_t * tokens, int numToken); + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize); + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value); + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements); + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam); + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements); + +int getStringTokenV2(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char *gParam, char * value, int maxValueSize); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/pom.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/pom.xml new file mode 100644 index 00000000..f9f42ebb --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/pom.xml @@ -0,0 +1,83 @@ + + + + + + org.onap.demo.vnf + demo-aggregator + 1.2.0-SNAPSHOT + ../../pom.xml + + + 4.0.0 + org.onap.demo.vnf.ves5 + ves_vfw_reporting + + + + + + maven-jar-plugin + 2.3.2 + + + default-jar + never + + + + + + maven-assembly-plugin + 2.5.3 + + dep.xml + + + + create-archive + package + + single + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + none + + + + true + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/sample_fault_events.txt b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/sample_fault_events.txt new file mode 100644 index 00000000..516d1013 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/sample_fault_events.txt @@ -0,0 +1,98 @@ +{ + "event": { + "commonEventHeader": { + "domain": "fault", + "eventId": "fault000000001", + "eventName": "Fault_vFirewall-AT&T_linkDownError", + "eventType": "applicationVnf", + "lastEpochMicrosec": 1548491713599115, + "nfNamingCode": "AFX", + "nfcNamingCode": "AFX", + "priority": "Low", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sequence": 1, + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "startEpochMicrosec": 1548491713599115, + "version": 3.0 + }, + "faultFields": { + "alarmCondition": "link down trap_alarm", + "alarmInterfaceA": "docker0", + "eventCategory": "link", + "eventSeverity": "MAJOR", + "eventSourceType": "router", + "faultFieldsVersion": 2.0, + "specificProblem": "physical or logical connection to a remote router is down", + "vfStatus": "Idle" + } + } +} +=================================== + +{ + "event": { + "commonEventHeader": { + "domain": "fault", + "eventId": "fault000000001", + "eventName": "Fault_vFirewall-AT&T_serviceDownError", + "eventType": "applicationVnf", + "lastEpochMicrosec": 1548491702433326, + "nfNamingCode": "AFX", + "nfcNamingCode": "AFX", + "priority": "Low", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sequence": 1, + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "startEpochMicrosec": 1548491702433326, + "version": 3.0 + }, + "faultFields": { + "alarmCondition": "service down trap_alarm", + "alarmInterfaceA": "afx@input.service", + "eventCategory": "other", + "eventSeverity": "MAJOR", + "eventSourceType": "virtualMachine", + "faultFieldsVersion": 2.0, + "specificProblem": "service is down", + "vfStatus": "Idle" + } + } +} + +========================== +{ + "event": { + "commonEventHeader": { + "domain": "fault", + "eventId": "fault000000001", + "eventName": "Fault_vFirewall-AT&T_linkDownError", + "eventType": "applicationVnf", + "lastEpochMicrosec": 1548491569186353, + "nfNamingCode": "AFX", + "nfcNamingCode": "AFX", + "priority": "Low", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sequence": 0, + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "startEpochMicrosec": 1548491548714540, + "version": 3.0 + }, + "faultFields": { + "alarmCondition": "link up trap_alarm", + "alarmInterfaceA": "lo", + "eventCategory": "link", + "eventSeverity": "NORMAL", + "eventSourceType": "router", + "faultFieldsVersion": 2.0, + "specificProblem": "physical or logical connection to a remote router is up", + "vfStatus": "Idle" + } + } +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/ves_fault_reporter.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/ves_fault_reporter.c new file mode 100644 index 00000000..3757d177 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_fault/ves_fault_reporter.c @@ -0,0 +1,1328 @@ +/*************************************************************************//** + * + * Copyright © 2019 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "jsmn.h" +#include "evel.h" + +#define BUFSIZE 128 +#define MAX_BUFFER_SIZE 4096 +#define MAX_TOKENS 1000 +#define MAX_INTERFACES 40 + +void *FaultThread(void *threadarg); +void *FaultThread01(void *threadarg); +void *FaultThread02(void *threadarg); +void *FaultThread03(void *threadarg); + +typedef struct dummy_vpp_metrics_struct { + int curr_bytes_in; + int curr_bytes_out; + int curr_packets_in; + int curr_packets_out; + int last_bytes_in; + int last_bytes_out; + int last_packets_in; + int last_packets_out; +} vpp_metrics_struct; + +typedef struct linkstat { + + char linkname[32]; + char linkdescr[64]; + char linkmode[64]; + int speedmbps; + int fault_raised; + unsigned long long last_epoch; + +}LINKSTAT; + +//vpp_metrics_struct meas_intfstat[MAX_INTERFACES]; +//LINKSTAT meas_linkstat[MAX_INTERFACES]; +//int request_rate=0; +vpp_metrics_struct fault_intfstat[MAX_INTERFACES]; +LINKSTAT fault_linkstat[MAX_INTERFACES]; + +unsigned long long epoch_start = 0; + +int format_val_params(KEYVALRESULT * keyValArray, int numElements, const char *replace, const char *search) +{ + char *sp; + int i =0; + int search_len; + int replace_len; + int tail_len; + + for (i=0; i| | \n", argv[0]); + fprintf(stderr, "OR\n"); + fprintf(stderr, "Usage: %s | \n", argv[0]); + exit(-1); + } + + /**************************************************************************/ + /* Initialize */ + /**************************************************************************/ + if(evel_initialize(fqdn, /* FQDN */ + port, /* Port */ + fqdn2, /* Backup FQDN */ + port2, /* Backup port */ + NULL, /* optional path */ + NULL, /* optional topic */ + 100, /* Ring Buffer size */ + 0, /* HTTPS? */ + NULL, /* cert file */ + NULL, /* key file */ + NULL, /* ca info */ + NULL, /* ca file */ + 0, /* verify peer */ + 0, /* verify host */ + "sample1", /* Username */ + "sample1", /* Password */ + "sample1", /* Username2 */ + "sample1", /* Password2 */ + NULL, /* Source ip */ + NULL, /* Source ip2 */ + EVEL_SOURCE_VIRTUAL_MACHINE, /* Source type */ + "vFault", /* Role */ + 1)) /* Verbosity */ + { + fprintf(stderr, "\nFailed to initialize the EVEL library!!!\n"); + exit(-1); + } + else + { + printf("\nInitialization completed\n"); + } + + /* Initialize and set thread detached attribute */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + printf("Main:Creating thread \n"); + rc = pthread_create(&flt_thread, NULL, FaultThread, &i); + if (rc) + { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + printf("Main:Created Fault thread \n"); + + pthread_join(flt_thread, NULL); + + evel_terminate(); + printf("Terminated\n"); + return 0; +} + +void *FaultThread(void *mainFault) +{ + + pthread_attr_t attr; + pthread_t flt_thread01; + pthread_t flt_thread02; + pthread_t flt_thread03; + int faultInstance01 = 0; + int faultInstance02 = 0; + int faultInstance03 = 0; + int rc; + + int numToken; + jsmntok_t tokens[MAX_TOKENS]; + char js[MAX_BUFFER_SIZE]; + + jsmn_parser p; + char ch[BUFSIZE]; + int ret = 0; + + memset(js, 0, MAX_BUFFER_SIZE); + memset(ch, 0, BUFSIZE); + + sleep(1); + printf("Running Main Fault thread \n"); + fflush(stdout); + + /* Initialize and set thread detached attribute */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + FILE * file = fopen("flt_config.json", "r"); + + while((fgets(ch, (BUFSIZE-1), file)) !=NULL) + { + strcat(js, ch); + memset(ch, 0, BUFSIZE); + } + //printf("MainFaulThread::the file content is \n %s \n", js); + //printf("\n MainFaulThread::MMMMMMMMMMMMMMM\n"); + + jsmn_init(&p); + numToken = jsmn_parse(&p, js, strlen(js), tokens, MAX_TOKENS); + //printf("count-%d\n", numToken); + + //printToken(js,tokens, numToken); + + printf("Main Fault Thread: Creating other fault threads\n"); + + ret = isTokenPresent(js, tokens, numToken, "tmp_faultInstance01", "tmp_indirectParameters"); + if (ret == 0) + { +// rc = pthread_create(&flt_thread01, NULL, FaultThread01, "tmp_faultInstance01"); + rc = pthread_create(&flt_thread01, NULL, FaultThread01, &ret); + if (rc) + { + printf("Main Fault Thread::ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + faultInstance01 = 1; + } + + ret = isTokenPresent(js, tokens, numToken, "tmp_faultInstance02", "tmp_indirectParameters"); + if (ret == 0) + { +// rc = pthread_create(&flt_thread02, NULL, FaultThread02, "tmp_faultInstance02"); + rc = pthread_create(&flt_thread02, NULL, FaultThread02, &ret); + if (rc) + { + printf("Main Fault Thread::ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + faultInstance02 = 1; + } + + ret = isTokenPresent(js, tokens, numToken, "tmp_faultInstance03", "tmp_indirectParameters"); + if (ret == 0) + { + rc = pthread_create(&flt_thread03, NULL, FaultThread03, &ret); + if (rc) + { + printf("Main Fault Thread::ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + faultInstance03 = 1; + } + + if (faultInstance01 == 1) + { + pthread_join(flt_thread01, NULL); + } + + if (faultInstance02 == 1) + { + pthread_join(flt_thread02, NULL); + } + + if (faultInstance03 == 1) + { + pthread_join(flt_thread03, NULL); + } + + while(1) + { + sleep(100); + } +} + +void *FaultThread01(void *faultInstanceTag) +{ + EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; + EVENT_FAULT * fault = NULL; + EVENT_HEADER* fault_header = NULL; + int bytes_in; + int bytes_out; + int packets_in; + int packets_out; + unsigned long long epoch_now; + int lowWaterMark; + + struct timeval time_val; + char event_id1[10] = "fault"; + char event_id2[15] = {0}; + char event_id[BUFSIZE] = {0}; + int fault_event_id = 0; + + int numToken; + jsmntok_t tokens[MAX_TOKENS]; + char js[MAX_BUFFER_SIZE]; + + jsmn_parser p; + char ch[BUFSIZE]; + int ret = 0; + char eName[BUFSIZE]; + char eType[BUFSIZE]; + char nfcCode[BUFSIZE]; + char nfCode[BUFSIZE]; + char prio[BUFSIZE]; + char reportEId[BUFSIZE]; + char reportEName[BUFSIZE]; + char srcId[BUFSIZE]; + char srcName[BUFSIZE]; + char eCategory[BUFSIZE]; + char eventSrcTyp[BUFSIZE]; + char specProb[BUFSIZE]; + char alarmCondn[BUFSIZE]; + char eventSev[BUFSIZE]; + char vfStatus[BUFSIZE]; + + int priority; + int srcTyp; + int vfStat; + int eSev; + + char hostname[BUFSIZE]; + + int flt_interval; + ARRAYVAL intfArray[MAX_INTERFACES]; + KEYVALRESULT keyValResultArray[32]; + KEYVALRESULT commandArray[32]; + int numInitCommands = 0; + int numCommands = 0; + int linkCount = 0; + int i = 0; + + memset(hostname, 0, BUFSIZE); + gethostname(hostname, BUFSIZE); + printf("FAULT01::The hostname is %s\n", hostname); + + sleep(1); + printf("FAULT01::Running Fault thread \n"); + fflush(stdout); + + memset(&intfArray[0],0,(sizeof(ARRAYVAL) * MAX_INTERFACES)); + memset(&keyValResultArray[0],0,(sizeof(KEYVALRESULT) * 32)); + + memset(js, 0, MAX_BUFFER_SIZE); + memset(ch, 0, BUFSIZE); + memset(&fault_intfstat[0],0,(sizeof(vpp_metrics_struct)* MAX_INTERFACES)); + memset(&fault_linkstat[0],0,(sizeof(LINKSTAT) * MAX_INTERFACES)); + + FILE * file = fopen("flt_config.json", "r"); + + while((fgets(ch, (BUFSIZE-1), file)) !=NULL) + { + strcat(js, ch); + memset(ch, 0, BUFSIZE); + } +// printf("FAULT01::the file content is \n %s \n", js); + + jsmn_init(&p); + numToken = jsmn_parse(&p, js, strlen(js), tokens, MAX_TOKENS); + printf("FAULT01::count-%d\n", numToken); + +// printToken(js,tokens, numToken); + + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance01", &flt_interval); + if (ret != 0) + { + printf("FAULT01::The parameter tmp_faultCheckInterval is not defined, defaulted to 60 seconds\n"); + flt_interval = 60; + } + + ret = getIntToken(js, tokens, numToken, "tmp_lowWaterMark", "tmp_faultInstance01", &lowWaterMark); + if (ret != 0) + { + printf("FAULT01::The parameter tmp_lowWaterMark is not defined, defaulted to 100\n"); + lowWaterMark = 100; + } + + ret = getArrayTokens(js, tokens, numToken, "tmp_device", "tmp_directParameters", intfArray, &linkCount); + + printf("FAULT01::Array link count is %d\n", linkCount); + + /* Copy the link information */ + for(i=0;i 0) { + bytes_in = fault_intfstat[i].curr_bytes_in - fault_intfstat[i].last_bytes_in; + } + else { + bytes_in = 0; + } + if(fault_intfstat[i].curr_bytes_out - fault_intfstat[i].last_bytes_out > 0) { + bytes_out = fault_intfstat[i].curr_bytes_out - fault_intfstat[i].last_bytes_out; + } + else { + bytes_out = 0; + } + if(fault_intfstat[i].curr_packets_in - fault_intfstat[i].last_packets_in > 0) { + packets_in = fault_intfstat[i].curr_packets_in - fault_intfstat[i].last_packets_in; + } + else { + packets_in = 0; + } + if(fault_intfstat[i].curr_packets_out - fault_intfstat[i].last_packets_out > 0) { + packets_out = fault_intfstat[i].curr_packets_out - fault_intfstat[i].last_packets_out; + } + else { + packets_out = 0; + } + if (((bytes_in < lowWaterMark) || (bytes_out < lowWaterMark) || + (packets_in < lowWaterMark) || (packets_out < lowWaterMark)) && + (fault_linkstat[i].fault_raised == 0)) + { + printf("\n%d - bytes in %d, ouot %d, packets in %d, out %d", i, bytes_in, bytes_out, packets_in, packets_out); + printf("\nFAULT01::Raising fault\n"); + memset(event_id, 0, BUFSIZE); + fault_event_id = fault_event_id+1; + sprintf(event_id2, "%09d", fault_event_id); + strcat(event_id, event_id1); + strcat(event_id, event_id2); + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance01", &flt_interval); + if (ret != 0) + { + printf("FAULT01::The parameter tmp_faultCheckInterval is not defined, defaulted to 60 seconds\n"); + flt_interval = 60; + } + + ret = getIntToken(js, tokens, numToken, "tmp_lowWaterMark", "tmp_faultInstance01", &lowWaterMark); + if (ret != 0) + { + printf("FAULT01::The parameter tmp_lowWaterMark is not defined, defaulted to 100\n"); + lowWaterMark = 100; + } + ret = getStringToken(js, tokens, numToken, "reportingEntityName", "tmp_directParameters", reportEName, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - reportingEntityName is not there in tmp_directParameters\n"); + printf("FAULT01::Defaulting reportingEntityName to hostname\n"); + strcpy(reportEName, hostname); + } + ret = getStringToken(js, tokens, numToken, "sourceName", "tmp_directParameters", srcName, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - sourceName is not there in tmp_directParameters\n"); + printf("FAULT01::Defaulting sourceName to hostname\n"); + strcpy(srcName, hostname); + } + + ret = getStringToken(js, tokens, numToken, "priority", "tmp_directParameters", prio, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - priority is not there in tmp_directParameters\nDefaulting priority to Low\n"); + strcpy(prio, "Medium"); + } + priority = get_priority(prio); + if(priority == -1) + { + printf("FAULT01::Fault priority value is not matching, prioirty-%s \n", prio); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "specificProblem", "tmp_alarmSetParameters", "tmp_faultInstance01", specProb, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - specificProblem is not there in tmp_alarmSetParameters, exiting ...\n"); + exit(1); + } + ret = getStringTokenV2(js, tokens, numToken, "alarmCondition", "tmp_alarmSetParameters", "tmp_faultInstance01", alarmCondn, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - alarmCondition is not there in tmp_alarmSetParameters, exiting ...\n"); + exit(1); + } + ret = getStringTokenV2(js, tokens, numToken, "eventSeverity", "tmp_alarmSetParameters", "tmp_faultInstance01", eventSev, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - eventSeverity is not there in tmp_alarmSetParameters\n"); + printf("FAULT01::Defaulting eventSeverity to MAJOR\n"); + strcpy(eventSev, "MAJOR"); + } + eSev = get_severity(eventSev); + if(eSev == -1) + { + printf("FAULT01::Fault eventSeverity value is not matching, eventSeverity-%s \n", eventSev); + exit(1); + } + + fault = evel_new_fault(eName, event_id, alarmCondn, + specProb, priority, eSev, srcTyp,vfStat); + if (fault != NULL) + { + fault_linkstat[i].fault_raised = 1; + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; + fault_linkstat[i].last_epoch = epoch_now; + + fault_header = (EVENT_HEADER *)fault; + + evel_fault_category_set(fault, eCategory); + evel_fault_interface_set(fault, fault_linkstat[i].linkname); + + if (eType != NULL) + evel_fault_type_set(fault, eType); + + evel_start_epoch_set(&fault->header, epoch_now); + evel_last_epoch_set(&fault->header, epoch_now); + if(nfcCode != NULL) + evel_nfcnamingcode_set(&fault->header, nfcCode); + if(nfCode != NULL) + evel_nfnamingcode_set(&fault->header, nfCode); + evel_reporting_entity_name_set(&fault->header, reportEName); + if(reportEId != NULL) + evel_reporting_entity_id_set(&fault->header, reportEId); + if(srcId != NULL ) + evel_source_id_set(&fault->header, srcId); + if(srcName!= NULL ) + evel_source_name_set(&fault->header, srcName); + + evel_rc = evel_post_event(fault_header); + + if(evel_rc == EVEL_SUCCESS) + printf("FAULT01::Fault event is correctly sent to the collector!\n"); + else + printf("FAULT01::Post failed %d (%s)\n", evel_rc, evel_error_string()); + } + else + { + printf("FAULT01::New new fault failed (%s)\n", evel_error_string()); + } + } + else if (((bytes_in > lowWaterMark) && (bytes_out > lowWaterMark) && + (packets_in > lowWaterMark) && (packets_out > lowWaterMark)) && + (fault_linkstat[i].fault_raised == 1)) + { + printf("\nFAULT01:: Clearing fault\n"); + memset(event_id, 0, BUFSIZE); + sprintf(event_id2, "%09d", (i+1)); + strcat(event_id, event_id1); + strcat(event_id, event_id2); + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance01", &flt_interval); + if (ret != 0) + { + printf("FAULT01::The parameter tmp_faultCheckInterval is not defined, defaulted to 60 seconds\n"); + flt_interval = 60; + } + + ret = getIntToken(js, tokens, numToken, "tmp_lowWaterMark", "tmp_faultInstance01", &lowWaterMark); + if (ret != 0) + { + printf("FAULT01::The parameter tmp_lowWaterMark is not defined, defaulted to 100\n"); + lowWaterMark = 100; + } + ret = getStringToken(js, tokens, numToken, "reportingEntityName", "tmp_directParameters", reportEName, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - reportingEntityName is not there in tmp_directParameters\n"); + printf("FAULT01::Defaulting reportingEntityName to hostname\n"); + strcpy(reportEName, hostname); + } + ret = getStringToken(js, tokens, numToken, "sourceName", "tmp_directParameters", srcName, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - sourceName is not there in tmp_directParameters\n"); + printf("FAULT01::Defaulting sourceName to hostname\n"); + strcpy(srcName, hostname); + } + + ret = getStringToken(js, tokens, numToken, "priority", "tmp_directParameters", prio, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - priority is not there in tmp_directParameters\nDefaulting priority to Low\n"); + strcpy(prio, "Medium"); + } + priority = get_priority(prio); + if(priority == -1) + { + printf("FAULT01::Fault priority value is not matching, prioirty-%s \n", prio); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "specificProblem", "tmp_alarmClearParameters", "tmp_faultInstance01", specProb, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - specificProblem is not there in tmp_alarmClearParameters, exiting ...\n"); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "alarmCondition", "tmp_alarmClearParameters", "tmp_faultInstance01", alarmCondn, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - alarmCondition is not there in tmp_alarmClearParameters, exiting ...\n"); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "eventSeverity", "tmp_alarmClearParameters", "tmp_faultInstance01", eventSev, BUFSIZE); + if (ret != 0) + { + printf("FAULT01::Missing mandatory parameters - eventSeverity is not there in tmp_alarmClearParameters\n"); + printf("FAULT01::Defaulting eventSeverity to MAJOR\n"); + strcpy(eventSev, "NORMAL"); + } + eSev = get_severity(eventSev); + if(eSev == -1) + { + printf("FAULT01::Fault eventSeverity value is not matching, eventSeverity-%s \n", eventSev); + exit(1); + } + + fault = evel_new_fault(eName, event_id, alarmCondn, + specProb, priority, eSev, srcTyp,vfStat); + if (fault != NULL) + { + fault_linkstat[i].fault_raised = 0; + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; + + fault_header = (EVENT_HEADER *)fault; + evel_fault_category_set(fault, eCategory); + evel_fault_interface_set(fault, fault_linkstat[i].linkname); + + if (eType != NULL) + evel_fault_type_set(fault, eType); + + evel_start_epoch_set(&fault->header, fault_linkstat[i].last_epoch); + evel_last_epoch_set(&fault->header, epoch_now); + fault_linkstat[i].last_epoch = 0; + + if(nfcCode != NULL) + evel_nfcnamingcode_set(&fault->header, nfcCode); + if(nfCode != NULL) + evel_nfnamingcode_set(&fault->header, nfCode); + evel_reporting_entity_name_set(&fault->header, reportEName); + if(reportEId != NULL) + evel_reporting_entity_id_set(&fault->header, reportEId); + if(srcId != NULL ) + evel_source_id_set(&fault->header, srcId); + if(srcName!= NULL ) + evel_source_name_set(&fault->header, srcName); + + evel_rc = evel_post_event(fault_header); + + if(evel_rc == EVEL_SUCCESS) + printf("FAULT01::Fault event is correctly sent to the collector!\n"); + else + printf("FAULT01::Post failed %d (%s)\n", evel_rc, evel_error_string()); + } + else + printf("FAULT01::New fault failed (%s)\n", evel_error_string()); + } + } + + sleep(flt_interval); + } + + /***************************************************************************/ + /* Terminate */ + /***************************************************************************/ + sleep(1); +} + +void *FaultThread02(void *faultInstanceTag) +{ + EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; + EVENT_FAULT * fault = NULL; + EVENT_HEADER* fault_header = NULL; + unsigned long long epoch_now; + unsigned long long last_epoch; + + struct timeval time_val; + char event_id1[10] = "fault"; + char event_id2[15] = {0}; + char event_id[BUFSIZE] = {0}; + int fault_event_id = 0; + int i=0; + + int numToken; + jsmntok_t tokens[MAX_TOKENS]; + char js[MAX_BUFFER_SIZE]; + + jsmn_parser p; + char ch[BUFSIZE]; + int ret = 0; + char eName[BUFSIZE]; + char eType[BUFSIZE]; + char aInterface[BUFSIZE]; + char nfcCode[BUFSIZE]; + char nfCode[BUFSIZE]; + char prio[BUFSIZE]; + char reportEId[BUFSIZE]; + char reportEName[BUFSIZE]; + char srcId[BUFSIZE]; + char srcName[BUFSIZE]; + char eCategory[BUFSIZE]; + char eventSrcTyp[BUFSIZE]; + char specProb[BUFSIZE]; + char alarmCondn[BUFSIZE]; + char eventSev[BUFSIZE]; + char vfStatus[BUFSIZE]; + + int priority; + int srcTyp; + int vfStat; + int eSev; + + char hostname[BUFSIZE]; + + int flt_interval; + KEYVALRESULT keyValResultArray[32]; + KEYVALRESULT commandArray[32]; + int numInitCommands = 0; + int numCommands = 0; + int fault_raised = 0; + + memset(hostname, 0, BUFSIZE); + gethostname(hostname, BUFSIZE); + printf("FAULT02::The hostname is %s\n", hostname); + + sleep(1); + printf("FAULT02::Running Fault thread \n"); + fflush(stdout); + + memset(&keyValResultArray[0],0,(sizeof(KEYVALRESULT) * 32)); + + memset(js, 0, MAX_BUFFER_SIZE); + memset(ch, 0, BUFSIZE); + + FILE * file = fopen("flt_config.json", "r"); + + while((fgets(ch, (BUFSIZE-1), file)) !=NULL) + { + strcat(js, ch); + memset(ch, 0, BUFSIZE); + } +// printf("FAULT02::the file content is \n %s \n", js); + + jsmn_init(&p); + numToken = jsmn_parse(&p, js, strlen(js), tokens, MAX_TOKENS); + printf("FAULT02::count-%d\n", numToken); + +// printToken(js,tokens, numToken); + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance02", &flt_interval); + if (ret != 0) + { + printf("FAULT02::The parameter tmp_faultCheckInterval is not defined, defaulted to 60 seconds\n"); + flt_interval = 60; + } + + + read_keyVal_params(js, tokens, numToken, "tmp_init", "tmp_faultInstance02", keyValResultArray, &numInitCommands); + + memset(&commandArray[0],0,(sizeof(KEYVALRESULT) * 32)); + memcpy(commandArray, keyValResultArray, (sizeof(KEYVALRESULT) * 32)); + runCommands(commandArray, numInitCommands); + + gettimeofday(&time_val, NULL); + + sleep(flt_interval); + + /***************************************************************************/ + /* Collect metrics from the VNIC */ + /***************************************************************************/ + while(1) { + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance02", &flt_interval); + if (ret != 0) + { + flt_interval = 60; + } + + ret = getStringToken(js, tokens, numToken, "eventName", "tmp_faultInstance02", eName, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - eventName is not there in tmp_faultInstance02. Exiting...\n"); + exit(1); + } + + ret = getStringToken(js, tokens, numToken, "eventType", "tmp_directParameters", eType, BUFSIZE); + ret = getStringToken(js, tokens, numToken, "nfcNamingCode", "tmp_directParameters", nfcCode, BUFSIZE); + ret = getStringToken(js, tokens, numToken, "nfNamingCode", "tmp_directParameters", nfCode, BUFSIZE); + ret = getStringToken(js, tokens, numToken, "reportingEntityId", "tmp_directParameters", reportEId, BUFSIZE); + ret = getStringToken(js, tokens, numToken, "sourceId", "tmp_directParameters", srcId, BUFSIZE); + ret = getStringToken(js, tokens, numToken, "eventCategory", "tmp_faultInstance02", eCategory, BUFSIZE); + ret = getStringToken(js, tokens, numToken, "alarmInterfaceA", "tmp_faultInstance02", aInterface, BUFSIZE); + + ret = getStringToken(js, tokens, numToken, "eventSourceType", "tmp_faultInstance02", eventSrcTyp, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - eventSourceType is not there in tmp_directParameters, exiting..\n"); + exit(1); + } + srcTyp = get_source(eventSrcTyp); + if(srcTyp == -1) + { + printf("FAULT02::Fault eventSourceType value is not matching, eventSourceType-%s \n", eventSrcTyp); + exit(1); + } + + ret = getStringToken(js, tokens, numToken, "vfStatus", "tmp_directParameters", vfStatus, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - vfStatus is not there in tmp_directParameters, exiting..\n"); + exit(1); + } + vfStat = get_vf_status(vfStatus); + if(vfStat == -1) + { + printf("FAULT02::Fault vfStatus value is not matching, vfStatus-%s \n", vfStatus); + exit(1); + } + + read_keyVal_params(js, tokens, numToken, "tmp_command", "tmp_faultInstance02", keyValResultArray, &numCommands); + + memset(&commandArray[0],0,(sizeof(KEYVALRESULT) * 32)); + memcpy(commandArray, keyValResultArray, (sizeof(KEYVALRESULT) * 32)); + runCommands(commandArray, numCommands); + + /******************************************************************************** + * Put the condition to set the fault here + *******************************************************************************/ + if ((atoi(commandArray[0].resultStr) == 1) && (fault_raised == 0)) + { + printf("\nFAULT02::Raising fault\n"); + memset(event_id, 0, BUFSIZE); + fault_event_id = fault_event_id+1; + sprintf(event_id2, "%09d", fault_event_id); + strcat(event_id, event_id1); + strcat(event_id, event_id2); + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance02", &flt_interval); + if (ret != 0) + { + printf("FAULT02::The parameter tmp_faultCheckInterval is not defined, defaulted to 60 seconds\n"); + flt_interval = 60; + } + + ret = getStringToken(js, tokens, numToken, "reportingEntityName", "tmp_directParameters", reportEName, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - reportingEntityName is not there in tmp_directParameters\n"); + printf("FAULT02::Defaulting reportingEntityName to hostname\n"); + strcpy(reportEName, hostname); + } + ret = getStringToken(js, tokens, numToken, "sourceName", "tmp_directParameters", srcName, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - sourceName is not there in tmp_directParameters\n"); + printf("FAULT02::Defaulting sourceName to hostname\n"); + strcpy(srcName, hostname); + } + + ret = getStringToken(js, tokens, numToken, "priority", "tmp_directParameters", prio, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - priority is not there in tmp_directParameters\nDefaulting priority to Low\n"); + strcpy(prio, "Medium"); + } + priority = get_priority(prio); + if(priority == -1) + { + printf("FAULT02::Fault priority value is not matching, prioirty-%s \n", prio); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "specificProblem", "tmp_alarmSetParameters", "tmp_faultInstance02", specProb, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - specificProblem is not there in tmp_alarmSetParameters, exiting ...\n"); + exit(1); + } + ret = getStringTokenV2(js, tokens, numToken, "alarmCondition", "tmp_alarmSetParameters", "tmp_faultInstance02", alarmCondn, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - alarmCondition is not there in tmp_alarmSetParameters, exiting ...\n"); + exit(1); + } + ret = getStringTokenV2(js, tokens, numToken, "eventSeverity", "tmp_alarmSetParameters", "tmp_faultInstance02", eventSev, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - eventSeverity is not there in tmp_alarmSetParameters\n"); + printf("FAULT02::Defaulting eventSeverity to MAJOR\n"); + strcpy(eventSev, "MAJOR"); + } + eSev = get_severity(eventSev); + if(eSev == -1) + { + printf("FAULT02::Fault eventSeverity value is not matching, eventSeverity-%s \n", eventSev); + exit(1); + } + + fault = evel_new_fault(eName, event_id, alarmCondn, + specProb, priority, eSev, srcTyp,vfStat); + if (fault != NULL) + { + fault_raised = 1; + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; + last_epoch = epoch_now; + + fault_header = (EVENT_HEADER *)fault; + + evel_fault_category_set(fault, eCategory); + if (aInterface != NULL) + evel_fault_interface_set(fault, aInterface); + + if (eType != NULL) + evel_fault_type_set(fault, eType); + + evel_start_epoch_set(&fault->header, epoch_now); + evel_last_epoch_set(&fault->header, epoch_now); + if(nfcCode != NULL) + evel_nfcnamingcode_set(&fault->header, nfcCode); + if(nfCode != NULL) + evel_nfnamingcode_set(&fault->header, nfCode); + evel_reporting_entity_name_set(&fault->header, reportEName); + if(reportEId != NULL) + evel_reporting_entity_id_set(&fault->header, reportEId); + if(srcId != NULL ) + evel_source_id_set(&fault->header, srcId); + if(srcName!= NULL ) + evel_source_name_set(&fault->header, srcName); + + evel_rc = evel_post_event(fault_header); + + if(evel_rc == EVEL_SUCCESS) { + printf("FAULT02::Fault event is correctly sent to the collector!\n"); + } + else { + printf("FAULT02::Post failed %d (%s)\n", evel_rc, evel_error_string()); + } + } + else { + printf("FAULT02::New new fault failed (%s)\n", evel_error_string()); + } + } + /******************************************************************************** + * Put the condition to clear the fault here + *******************************************************************************/ + else if ((atoi(commandArray[0].resultStr) == 0) && (fault_raised == 1)) + { + printf("\nFAULT02:: Clearing fault\n"); + memset(event_id, 0, BUFSIZE); + sprintf(event_id2, "%09d", (i+1)); + strcat(event_id, event_id1); + strcat(event_id, event_id2); + + ret = getIntToken(js, tokens, numToken, "tmp_faultCheckInterval", "tmp_faultInstance02", &flt_interval); + if (ret != 0) + { + printf("FAULT02::The parameter tmp_faultCheckInterval is not defined, defaulted to 60 seconds\n"); + flt_interval = 60; + } + + ret = getStringToken(js, tokens, numToken, "reportingEntityName", "tmp_directParameters", reportEName, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - reportingEntityName is not there in tmp_directParameters\n"); + printf("FAULT02::Defaulting reportingEntityName to hostname\n"); + strcpy(reportEName, hostname); + } + ret = getStringToken(js, tokens, numToken, "sourceName", "tmp_directParameters", srcName, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - sourceName is not there in tmp_directParameters\n"); + printf("FAULT02::Defaulting sourceName to hostname\n"); + strcpy(srcName, hostname); + } + + ret = getStringToken(js, tokens, numToken, "priority", "tmp_directParameters", prio, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - priority is not there in tmp_directParameters\nDefaulting priority to Low\n"); + strcpy(prio, "Medium"); + } + priority = get_priority(prio); + if(priority == -1) + { + printf("FAULT02::Fault priority value is not matching, prioirty-%s \n", prio); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "specificProblem", "tmp_alarmClearParameters", "tmp_faultInstance02", specProb, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - specificProblem is not there in tmp_alarmClearParameters, exiting ...\n"); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "alarmCondition", "tmp_alarmClearParameters", "tmp_faultInstance02", alarmCondn, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - alarmCondition is not there in tmp_alarmClearParameters, exiting ...\n"); + exit(1); + } + + ret = getStringTokenV2(js, tokens, numToken, "eventSeverity", "tmp_alarmClearParameters", "tmp_faultInstance02", eventSev, BUFSIZE); + if (ret != 0) + { + printf("FAULT02::Missing mandatory parameters - eventSeverity is not there in tmp_alarmClearParameters\n"); + printf("FAULT02::Defaulting eventSeverity to MAJOR\n"); + strcpy(eventSev, "NORMAL"); + } + eSev = get_severity(eventSev); + if(eSev == -1) + { + printf("FAULT02::Fault eventSeverity value is not matching, eventSeverity-%s \n", eventSev); + exit(1); + } + + fault = evel_new_fault(eName, event_id, alarmCondn, + specProb, priority, eSev, srcTyp,vfStat); + if (fault != NULL) + { + fault_raised = 0; + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; + + fault_header = (EVENT_HEADER *)fault; + evel_fault_category_set(fault, eCategory); + if (aInterface != NULL) + evel_fault_interface_set(fault, aInterface); + + if (eType != NULL) + evel_fault_type_set(fault, eType); + + evel_start_epoch_set(&fault->header, last_epoch); + evel_last_epoch_set(&fault->header, epoch_now); + last_epoch = 0; + + if(nfcCode != NULL) + evel_nfcnamingcode_set(&fault->header, nfcCode); + if(nfCode != NULL) + evel_nfnamingcode_set(&fault->header, nfCode); + evel_reporting_entity_name_set(&fault->header, reportEName); + if(reportEId != NULL) + evel_reporting_entity_id_set(&fault->header, reportEId); + if(srcId != NULL ) + evel_source_id_set(&fault->header, srcId); + if(srcName!= NULL ) + evel_source_name_set(&fault->header, srcName); + + evel_rc = evel_post_event(fault_header); + + if(evel_rc == EVEL_SUCCESS) { + printf("FAULT02::Fault event is correctly sent to the collector!\n"); + } + else { + printf("FAULT02::Post failed %d (%s)\n", evel_rc, evel_error_string()); + } + } + else { + printf("FAULT02::New fault failed (%s)\n", evel_error_string()); + } + + } + + sleep(flt_interval); + } + + /***************************************************************************/ + /* Terminate */ + /***************************************************************************/ + sleep(1); +} + +void *FaultThread03(void *faultInstanceTag) +{ + sleep(4); + printf("FAULT03::thread created\n"); + fflush(stdout); + while (1) + { sleep (100); } +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/LICENSE.TXT b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/LICENSE.TXT new file mode 100644 index 00000000..16285cd2 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/LICENSE.TXT @@ -0,0 +1,22 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * Unless otherwise specified, all software contained herein is + * 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. + * ============LICENSE_END============================================ + * + * ECOMP is trademark and service mark of AT&T Intellectual Property. + * + */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/Makefile b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/Makefile new file mode 100644 index 00000000..4b0fd85d --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/Makefile @@ -0,0 +1,52 @@ +############################################################################# +# +# Copyright © 2019 AT&T Intellectual Property. All rights reserved. +# +# 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. +# +############################################################################# + +CC=gcc +ARCH=$(shell getconf LONG_BIT) +CODE_ROOT=$(CURDIR)/../.. +#CODE_ROOT=../code/evel-library +LIBS_DIR=$(CODE_ROOT)/libs/x86_$(ARCH) +#LIBS_DIR=/usr/lib +INCLUDE_DIR= -I $(CODE_ROOT)/code/evel_library -I . + +#****************************************************************************** +# Standard compiler flags. * +#****************************************************************************** +CPPFLAGS= +CFLAGS=-Wall -g -fPIC +FILEOBJLIST= jsmn.o ves_syslog_reporter.o + +all: ves_syslog_reporter + +clean: + rm -f *.o ves_syslog_reporter + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE_DIR) -c $< -o $@ + +jsmn.o: jsmn.c jsmn.h +ves_syslog_reporter.o: ves_syslog_reporter.c + +ves_syslog_reporter: $(FILEOBJLIST) + $(CC) $(CPPFLAGS) $(CFLAGS) -o ves_syslog_reporter \ + -L $(LIBS_DIR) \ + $(FILEOBJLIST) \ + -lpthread \ + -level \ + -lcurl + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/README.md b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/README.md new file mode 100644 index 00000000..9aecc2ac --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/README.md @@ -0,0 +1,37 @@ + +PROJECT DESCRIPTION + +--- +This project contains the source code and scripts for the generation of syslog events. The folder contains: + + - README.md: this file. + + - LICENSE.TXT: the license text. + + - ves_syslog_reporter.c and other .c files: source code that uses the ECOMP Vendor Event Listener Library (VES) to generate the syslog events. Syslog events are generated based on /var/log/syslog entries. If a specific pattern is observed in the syslog file, then it generates the syslog event. The application reads syslog_config.json file for parameter values and poppulate the syslog event. If eventName, eventSourceType, syslogTag and tmp_syslogFile parameter value is not given, the application terminates. If reportingEntityName and/or sourceName parameter values are not given, then it gets the hostname and poppulates it. + + - Makefile: makefile that compiles ves_syslog_reporter.c and generates ves_syslog_reporter binary. + + - go-client.sh/go-client_2_collectors.sh: bash script that starts up the ves_syslog_reporter. It reads input parameters like DCAE IP address and port from configuration files contained in /opt/config. Based on the collector configuration, use go-client.sh for single collector configuration, or use go-client_2_collectors.sh for 2 collectors configuration. + + +USAGE +----- + +Update the configuration files with proper parameters values so that events generated would contain those values + +To run the ves_syslog_reporter in single collector configuration, please execute the following steps: + + - Make the go-client.sh script executable + chmod +x go-client.sh + + - Run the go-client.sh script + ./go-client.sh + +For 2 collectors configuration, please execute following steps: + + - Make the go-client.sh script executable + chmod +x go-client_2_collectors.sh + + - Run the go-client_2_collectors.sh script + ./go-client_2_collectors.sh diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/dep.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/dep.xml new file mode 100644 index 00000000..fc18229f --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/dep.xml @@ -0,0 +1,25 @@ + + demo + + tar.gz + + + + + . + / + + *.sh + *.md + *.TXT + *.c + Makefile + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client.sh new file mode 100755 index 00000000..0701456a --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +./ves_syslog_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client_2_collectors.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client_2_collectors.sh new file mode 100755 index 00000000..492d76d2 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/go-client_2_collectors.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" + +#Usage for 2 collectors: +#./ves_syslog_reporter | | + +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +DCAE_COLLECTOR_IP2=$(cat /opt/config/dcae_collector_ip2.txt) +DCAE_COLLECTOR_PORT2=$(cat /opt/config/dcae_collector_port2.txt) +./ves_syslog_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT $DCAE_COLLECTOR_IP2 $DCAE_COLLECTOR_PORT2 diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.c new file mode 100644 index 00000000..1d72ba4d --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.c @@ -0,0 +1,672 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * Unless otherwise specified, all software contained herein is + * 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. + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + ****************************************************************************/ +#include +#include +#include +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +//#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +//#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { +//printf("jsmn_fill_token:: start-%d, end-%d\n", start, end); + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { +//printf("jsmn_parse_primitive:: the char is - %c\n", js[parser->pos]); + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + char d = js[parser->pos + 1]; +// char e = js[parser->pos + 2]; +//printf("jsmn_parse_string: value-%c, pos-%d\n", c,parser->pos); + + /* Quote: end of string */ +// if (c == '\"') { + if (d == '\"') { +// if ((d == '\"')&&((e == ' ')||(e == ','))) { +parser->pos++; +//printf("jsmn_parse_string: end of string\n"); + if (tokens == NULL) { +//printf("jsmn_parse_string: end tokens is NULL\n"); + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); +//printf("jsmn_parse_string: Allocated tokens \n"); + if (token == NULL) { +//printf("jsmn_parse_string: Allocated tokens is NULL\n"); + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +//printf("jsmn_parse_string: Allocated tokens is filled\n"); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; +//printf("jsmn_parse_string: value - %c, POS-%3d \n",c, js[parser->pos]); + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; +printf("jsmn_parse_string: exiting with ERROR_PART, pos-%d", parser->pos); + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; +//printf("jsmn_parse: value of c - %c\n",c); + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +//#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// if (token->type != type) { +// return JSMN_ERROR_INVAL; +// } +// parser->toksuper = -1; +// token->end = parser->pos + 1; +// break; +// } +// } +// /* Error if unmatched closing bracket */ +// if (i == -1) return JSMN_ERROR_INVAL; +// for (; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// parser->toksuper = i; +// break; +// } +// } +//#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; +//printf("jsmn_parse: value of c is :: - %c\n",c); + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +//#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { +// if (tokens[i].start != -1 && tokens[i].end == -1) { +// parser->toksuper = i; +// break; +// } +// } +// } +//#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { +//printf("index -%2d, start is %3d, end is %3d\n", i, tokens[i].start, tokens[i].end); + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +int jsoneq(const char *json, jsmntok_t *tok, const char *s) +{ + if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} + +void printToken(char * js, jsmntok_t * tokens, int numToken) +{ + for (int i = 1; i < numToken; i++) + { + printf("Token number-%2d, parent-%2d, type-%d size-%2d, parameter -", i, tokens[i].parent, tokens[i].type, tokens[i].size); + if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) { + printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start); + } else if (tokens[i].type == JSMN_ARRAY) { + printf("[%d elems]\n", tokens[i].size); + } else if (tokens[i].type == JSMN_OBJECT) { + printf("{%d elems}\n", tokens[i].size); + } else { + printf("value?? - "); + TOKEN_PRINT(tokens[i]); + } + } +} + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + + +int getStringTokenV2(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * gParam,char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], gParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Grand Parent token not seen + } + + for (i=dpToken; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 2; //Parent token not seen + } + + for (i=dpToken; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + { + continue; + } + + if(tokens[i+1].type != JSMN_PRIMITIVE) + return 3; //Wrong parameter type + +// printf("INT parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], val); + *value = atoi(val); +// printf(" - %d\n", *value); + + return 0; //success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +void parseDirectParameters(char * js, jsmntok_t * tokens, int numToken) +{ + int i = 0; + int dpToken = 0; + char param[128]; + char value[128]; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], "directParameters") == 0) + break; + } + + if (i < numToken) + { + dpToken = ++i; + } + + for (int i = 1; i < numToken; i++) + { + memset(param, 0, 128); + memset(value, 0, 128); + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + TOKEN_COPY(js, tokens[i], param); +// printf("parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], value); +// printf(" - %s\n", value); + } + } +} + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_ARRAY) + { + *numElements = tokens[i+1].size; +// printf("[%d elems]\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k], arrayValue[k].arrayString); +// printf(" - %s\n", arrayValue[k].arrayString); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING) + { + if(jsoneq(js, &tokens[i], param) == 0) + { + return 0; //Token present + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //Token Not present +} + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_OBJECT) + { + *numElements = tokens[i+1].size; +// printf("{%d elems}\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k*2], keyValueResultList[k].keyStr); +// printf("Key - %s", keyValueResultList[k].keyStr); + TOKEN_COPY(js, tokens[i+3+k*2], keyValueResultList[k].valStr); +// printf("Value - %s\n", keyValueResultList[k].valStr); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.h b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.h new file mode 100644 index 00000000..f9d838b6 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/jsmn.h @@ -0,0 +1,136 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +typedef struct arrayValues { + char arrayString[32]; +} ARRAYVAL; + +typedef struct keyValResult { + char keyStr[80]; + char valStr[250]; + char resultStr[80]; +} KEYVALRESULT; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +//#ifdef JSMN_PARENT_LINKS + int parent; +//#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +#define TOKEN_EQ(t, tok_start, tok_end, tok_type) \ + ((t).start == tok_start \ + && (t).end == tok_end \ + && (t).type == (tok_type)) + +#define TOKEN_STRING(js, t, s) \ + (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \ + && strlen(s) == (t).end - (t).start) + +#define TOKEN_COPY(js, t, s) \ + strncpy(s, js+(t).start, (t).end - (t).start) + +#define TOKEN_PRINT(t) \ + printf("start: %d, end: %d, type: %d, size: %d\n", \ + (t).start, (t).end, (t).type, (t).size) + + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +int jsoneq(const char *json, jsmntok_t *tok, const char *s); + +void printToken(char * js, jsmntok_t * tokens, int numToken); + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize); + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value); + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements); + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam); + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements); + +int getStringTokenV2(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char *gParam, char * value, int maxValueSize); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/pom.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/pom.xml new file mode 100644 index 00000000..f9f42ebb --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/pom.xml @@ -0,0 +1,83 @@ + + + + + + org.onap.demo.vnf + demo-aggregator + 1.2.0-SNAPSHOT + ../../pom.xml + + + 4.0.0 + org.onap.demo.vnf.ves5 + ves_vfw_reporting + + + + + + maven-jar-plugin + 2.3.2 + + + default-jar + never + + + + + + maven-assembly-plugin + 2.5.3 + + dep.xml + + + + create-archive + package + + single + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + none + + + + true + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/sample_syslog_event.txt b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/sample_syslog_event.txt new file mode 100644 index 00000000..3a6ec916 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/sample_syslog_event.txt @@ -0,0 +1,30 @@ +{ + "event": { + "commonEventHeader": { + "domain": "syslog", + "eventId": "syslog000000000", + "eventName": "syslog_vFirewall-AT&T_connectionReset", + "eventType": "applicationVnf", + "lastEpochMicrosec": 1548493308853168, + "nfNamingCode": "AFX", + "nfcNamingCode": "AFX", + "priority": "Normal", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sequence": 0, + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "startEpochMicrosec": 1548493278782875, + "version": 3.0 + }, + "syslogFields": { + "eventSourceType": "virtualMachine", + "syslogFacility": 16, + "syslogFieldsVersion": 3.0, + "syslogMsg": "Jan 26 14:31:46 prakash-VirtualBox prakash: peer reset qwerty", + "syslogProc": "vSyslog", + "syslogTag": "peer reset" + } + } +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/syslog_config.json b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/syslog_config.json new file mode 100644 index 00000000..6a434e43 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/syslog_config.json @@ -0,0 +1,20 @@ +{ + "tmp_directParameters": { + "eventName": "syslog_vFirewall-AT&T_connectionReset", + "eventType": "applicationVnf", + "nfcNamingCode": "AFX", + "nfNamingCode": "AFX", + "nfVendorName": "AT&T", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "timeZoneOffset": "UTC-05:30", + "eventSourceType": "virtualMachine", + "syslogProc": "vSyslog", + "syslogTag": "peer reset" + }, + "tmp_indirectParameters": { + "tmp_syslogFile": "/var/log/syslog" + } +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/ves_syslog_reporter.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/ves_syslog_reporter.c new file mode 100644 index 00000000..b7226e74 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_syslog/ves_syslog_reporter.c @@ -0,0 +1,375 @@ +/*************************************************************************//** + * + * Copyright © 2018 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "jsmn.h" +#include "evel.h" + +#define BUFSIZE 128 +#define MAX_BUFFER_SIZE 4096 +#define MAX_TOKENS 1000 + +void *SyslogThread(void *threadarg); + +unsigned long long epoch_start = 0; + +void report_syslog(char * js, jsmntok_t * tokens, int numToken, char * syslog_tag, char * eName, int srcTyp, char * syslog_msg) +{ + EVENT_HEARTBEAT_FIELD * event = NULL; + EVENT_HEADER* syslog_header = NULL; + EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; + char stringVal[BUFSIZE]; + + char event_id1[10] = "syslog"; + char event_id2[15] = {0}; + int event_id3 = 0; + char event_id[BUFSIZE] = {0}; + + int ret = 0; + /***************************************************************************/ + /* Syslog */ + /***************************************************************************/ + memset(event_id, 0, BUFSIZE); + memset(event_id2, 0, 15); + sprintf(event_id2, "%09d", event_id3++); + strcat(event_id, event_id1); + strcat(event_id, event_id2); + + + event = evel_new_syslog(eName, event_id, srcTyp, syslog_msg, syslog_tag); + + if (event != NULL) + { + syslog_header = (EVENT_HEADER *)event; + + ret = getStringToken(js, tokens, numToken, "syslogProc", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_syslog_proc_set(event, stringVal); + evel_syslog_facility_set(event, EVEL_SYSLOG_FACILITY_LOCAL0); + ret = getStringToken(js, tokens, numToken, "eventType", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_header_type_set(&event->header, stringVal); + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + unsigned long long epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; + + evel_start_epoch_set(&event->header, epoch_start); + evel_last_epoch_set(&event->header, epoch_now); + epoch_start = epoch_now; + + ret = getStringToken(js, tokens, numToken, "nfcNamingCode", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_nfcnamingcode_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "nfNamingCode", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_nfnamingcode_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "reportingEntityName", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_reporting_entity_name_set(&event->header, stringVal); + else + { + printf("Missing mandatory parameters - reportingEntityName is not there in tmp_directParameters\n"); + printf("Defaulting reportingEntityName to hostname\n"); + } + + ret = getStringToken(js, tokens, numToken, "reportingEntityId", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_reporting_entity_id_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "sourceId", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_source_id_set(&event->header, stringVal); + + ret = getStringToken(js, tokens, numToken, "sourceName", "tmp_directParameters", stringVal, BUFSIZE); + if (ret == 0) + evel_source_name_set(&event->header, stringVal); + else + { + printf("Missing mandatory parameters - sourceName is not there in tmp_directParameters\n"); + printf("Defaulting sourceName to hostname\n"); + } + + evel_rc = evel_post_event(syslog_header); + if (evel_rc != EVEL_SUCCESS) + { + EVEL_ERROR("Post failed %d (%s)", evel_rc, evel_error_string()); + } + } + else + { + EVEL_ERROR("New Syslog failed"); + } + printf(" Processed Syslog\n"); +} + +int get_source(char * inStr) +{ + int result = -1; + + if(strcmp(inStr, "other") == 0) + result = EVEL_SOURCE_OTHER; + else if(strcmp(inStr, "router") == 0) + result = EVEL_SOURCE_ROUTER; + else if(strcmp(inStr, "switch") == 0) + result = EVEL_SOURCE_SWITCH; + else if(strcmp(inStr, "host") == 0) + result = EVEL_SOURCE_HOST; + else if(strcmp(inStr, "card") == 0) + result = EVEL_SOURCE_CARD; + else if(strcmp(inStr, "port") == 0) + result = EVEL_SOURCE_PORT; + else if(strcmp(inStr, "slotThreshold") == 0) + result = EVEL_SOURCE_SLOT_THRESHOLD; + else if(strcmp(inStr, "portThreshold") == 0) + result = EVEL_SOURCE_PORT_THRESHOLD; + else if(strcmp(inStr, "virtualMachine") == 0) + result = EVEL_SOURCE_VIRTUAL_MACHINE; + else if(strcmp(inStr, "virtualNetworkFunction") == 0) + result = EVEL_SOURCE_VIRTUAL_NETWORK_FUNCTION; + + return result; +} + + +int main(int argc, char** argv) +{ + char* fqdn = argv[1]; + int port = atoi(argv[2]); + int i=0; + int rc; + pthread_attr_t attr; + pthread_t syslog_thread; + char* fqdn2 = NULL; + int port2 = 0; + + if(argc == 5) + { + fqdn2 = argv[3]; + port2 = atoi(argv[4]); + } + + if (!((argc == 3) || (argc == 5))) + { + fprintf(stderr, "Usage: %s | | \n", argv[0]); + fprintf(stderr, "OR\n"); + fprintf(stderr, "Usage: %s | \n", argv[0]); + exit(-1); + } + + /**************************************************************************/ + /* Initialize */ + /**************************************************************************/ + if(evel_initialize(fqdn, /* FQDN */ + port, /* Port */ + fqdn2, /* Backup FQDN */ + port2, /* Backup port */ + NULL, /* optional path */ + NULL, /* optional topic */ + 100, /* Ring Buffer size */ + 0, /* HTTPS? */ + NULL, /* cert file */ + NULL, /* key file */ + NULL, /* ca info */ + NULL, /* ca file */ + 0, /* verify peer */ + 0, /* verify host */ + "sample1", /* Username */ + "sample1", /* Password */ + "sample1", /* Username2 */ + "sample1", /* Password2 */ + NULL, /* Source ip */ + NULL, /* Source ip2 */ + EVEL_SOURCE_VIRTUAL_MACHINE, /* Source type */ + "vSyslog", /* Role */ + 1)) /* Verbosity */ + { + fprintf(stderr, "\nFailed to initialize the EVEL library!!!\n"); + exit(-1); + } + else + { + printf("\nInitialization completed\n"); + } + + /* Initialize and set thread detached attribute */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + printf("Main:Creating thread \n"); + rc = pthread_create(&syslog_thread, NULL, SyslogThread, &i); + if (rc) + { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + printf("Main:Created Syslog thread \n"); + + pthread_join(syslog_thread, NULL); + + evel_terminate(); + printf("Terminated\n"); + return 0; +} + +void *SyslogThread(void *threadarg) +{ + + int numToken; + jsmntok_t tokens[MAX_TOKENS]; + char js[MAX_BUFFER_SIZE]; + FILE * fp; + jsmn_parser p; + char ch[BUFSIZE]; + int ret = 0; + char eName[BUFSIZE]; + char eventSrcTyp[BUFSIZE]; + char syslog_tag[BUFSIZE]; + char syslog_msg[8*BUFSIZE]; + char str[8*BUFSIZE]; + char syslog_file[BUFSIZE]; + int srcTyp = 0; + int count = 0; + unsigned long long pos; + unsigned long long prevpos = 0; + + sleep(1); + printf("Running Syslog thread \n"); + fflush(stdout); + + while(1) + { + + FILE * file = fopen("syslog_config.json", "r"); + + memset(js, 0, MAX_BUFFER_SIZE); + memset(ch, 0, BUFSIZE); + + while((fgets(ch, (BUFSIZE-1), file)) !=NULL) + { + strcat(js, ch); + memset(ch, 0, BUFSIZE); + } +// printf("the file content is \n %s \n", js); + + jsmn_init(&p); + numToken = jsmn_parse(&p, js, strlen(js), tokens, MAX_TOKENS); +// printf("Token count-%d\n", numToken); + +// printToken(js,tokens, numToken); + + ret = getStringToken(js, tokens, numToken, "tmp_syslogFile", "tmp_indirectParameters", syslog_file, BUFSIZE); + if (ret != 0) + { + printf("Missing mandatory parameters - tmp_syslogFile is not there in tmp_indirectParameters. Exiting...\n"); + exit(1); + } + + ret = getStringToken(js, tokens, numToken, "syslogTag", "tmp_directParameters", syslog_tag, BUFSIZE); + if (ret != 0) + { + printf("Missing mandatory parameters - syslogTag is not there in tmp_directParameters, exiting..\n"); + exit(1); + } + + ret = getStringToken(js, tokens, numToken, "eventName", "tmp_directParameters", eName, BUFSIZE); + if (ret != 0) + { + printf("Missing mandatory parameters - eventName is not there in tmp_directParameters. Exiting...\n"); + exit(1); + } + + ret = getStringToken(js, tokens, numToken, "eventSourceType", "tmp_directParameters", eventSrcTyp, BUFSIZE); + if (ret != 0) + { + printf("Missing mandatory parameters - eventSourceType is not there in tmp_directParameters, exiting..\n"); + exit(1); + } + srcTyp = get_source(eventSrcTyp); + if(srcTyp == -1) + { + printf("Fault eventSourceType value is not matching, eventSourceType-%s \n", eventSrcTyp); + exit(1); + } + + // Open file in read mode + fp = fopen(syslog_file, "r"); + if (fp == NULL) + { + printf("Error while opening file"); + exit(EXIT_FAILURE); + } + + memset(str, 0, 8*BUFSIZE); + if (fseek(fp, 0, SEEK_END)) + perror("fseek() failed"); + else + { + // pos will contain no. of chars in input file. + int n = 8; + pos = ftell(fp); + + // search for '\n' characters + while (pos>prevpos) + { + // Move 'pos' away from end of file. + if (!fseek(fp, --pos, SEEK_SET)) + { + if (fgetc(fp) == '\n') + + // stop reading when n newlines is found + if (count++ == n) + break; + } + else + perror("fseek() failed"); + } + //printf("pos %d prevpos %d\n",pos,prevpos); + + // print last n lines + prevpos = pos; + + while (fgets(str, sizeof(str), fp)) + { + char * position; + // printf("str is - %s\n", str); + if ((position=strchr(str, '\n')) != NULL) + *position = '\0'; + if(strstr(str, syslog_tag) && !strstr(str,"EVEL") && !strstr(str,"commonEventHeader") && !strstr(str,"syslogMsg") && !strstr(str,"syslogTag")) + { + memset(syslog_msg, 0, 8*BUFSIZE); + memcpy(syslog_msg, str, 8*BUFSIZE); + report_syslog(js, tokens, numToken, syslog_tag, eName, srcTyp, syslog_msg); + } + + prevpos += strlen(str); + //printf("new prevpos is %d, size of str is %d\n", prevpos, strlen(str)); + } + } + sleep(3); + } +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/LICENSE.TXT b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/LICENSE.TXT new file mode 100644 index 00000000..16285cd2 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/LICENSE.TXT @@ -0,0 +1,22 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * Unless otherwise specified, all software contained herein is + * 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. + * ============LICENSE_END============================================ + * + * ECOMP is trademark and service mark of AT&T Intellectual Property. + * + */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/Makefile b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/Makefile new file mode 100644 index 00000000..9d1812fa --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/Makefile @@ -0,0 +1,52 @@ +############################################################################# +# +# Copyright © 2019 AT&T Intellectual Property. All rights reserved. +# +# 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. +# +############################################################################# + +CC=gcc +ARCH=$(shell getconf LONG_BIT) +CODE_ROOT=$(CURDIR)/../.. +#CODE_ROOT=../code/evel-library +LIBS_DIR=$(CODE_ROOT)/libs/x86_$(ARCH) +#LIBS_DIR=/usr/lib +INCLUDE_DIR= -I $(CODE_ROOT)/code/evel_library -I . + +#****************************************************************************** +# Standard compiler flags. * +#****************************************************************************** +CPPFLAGS= +CFLAGS=-Wall -g -fPIC +FILEOBJLIST= jsmn.o vpp_measurement_reporter.o + +all: vpp_measurement_reporter + +clean: + rm -f *.o vpp_measurement_reporter + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE_DIR) -c $< -o $@ + +jsmn.o: jsmn.c jsmn.h +vpp_measurement_reporter.o: vpp_measurement_reporter.c + +vpp_measurement_reporter: $(FILEOBJLIST) + $(CC) $(CPPFLAGS) $(CFLAGS) -o vpp_measurement_reporter \ + -L $(LIBS_DIR) \ + $(FILEOBJLIST) \ + -lpthread \ + -level \ + -lcurl + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/README.md b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/README.md new file mode 100644 index 00000000..6921cdc4 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/README.md @@ -0,0 +1,37 @@ + +PROJECT DESCRIPTION + +--- +This project contains the source code and scripts for the periodic generation of measurement events. The folder contains: + + - README.md: this file. + + - LICENSE.TXT: the license text. + + - vpp_measurement_reporter.c and other .c files: source code that uses the ECOMP Vendor Event Listener Library (VES) to generate the measurement events. Measurement event is generated periodically on each of the interfaces. It gives the number of bytes/packets that transmitted/received. The application reads meas_config.json file for parameter values and poppulate the measurement event. If eventName parameter value is not given, the application terminates. If reportingEntityName and sourceName parameter values are not given, then it gets the hostname and poppulates it. If measurementInterval is not given, it defaults to 60 seconds. + + - Makefile: makefile that compiles vpp_measurement_reporter.c and generates vpp_measurement_reporter binary. + + - go-client.sh/go-client_2_collectors.sh: bash script that starts up the vpp_measurement_reporter. It reads input parameters like DCAE IP address and port from configuration files contained in /opt/config. Based on the collector configuration, use go-client.sh for single collector configuration, or use go-client_2_collectors.sh for 2 collectors configuration. + + +USAGE +----- + +Update the configuration files with proper parameters values so that events generated would contain those values + +To run the vpp_measurement_reporter in single collector configuration, please execute the following steps: + + - Make the go-client.sh script executable + chmod +x go-client.sh + + - Run the go-client.sh script + ./go-client.sh + +For 2 collectors configuration, please execute following steps: + + - Make the go-client.sh script executable + chmod +x go-client_2_collectors.sh + + - Run the go-client_2_collectors.sh script + ./go-client_2_collectors.sh diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/dep.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/dep.xml new file mode 100644 index 00000000..fc18229f --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/dep.xml @@ -0,0 +1,25 @@ + + demo + + tar.gz + + + + + . + / + + *.sh + *.md + *.TXT + *.c + Makefile + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client.sh new file mode 100755 index 00000000..b2b3a08e --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +./vpp_measurement_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client_2_collectors.sh b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client_2_collectors.sh new file mode 100755 index 00000000..65f71268 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/go-client_2_collectors.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" + +#Usage for 2 collectors: +#./vpp_measurement_reporter | | + +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +DCAE_COLLECTOR_IP2=$(cat /opt/config/dcae_collector_ip2.txt) +DCAE_COLLECTOR_PORT2=$(cat /opt/config/dcae_collector_port2.txt) +./vpp_measurement_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT $DCAE_COLLECTOR_IP2 $DCAE_COLLECTOR_PORT2 diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.c new file mode 100644 index 00000000..1d72ba4d --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.c @@ -0,0 +1,672 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * Unless otherwise specified, all software contained herein is + * 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. + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + ****************************************************************************/ +#include +#include +#include +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +//#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +//#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { +//printf("jsmn_fill_token:: start-%d, end-%d\n", start, end); + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { +//printf("jsmn_parse_primitive:: the char is - %c\n", js[parser->pos]); + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + char d = js[parser->pos + 1]; +// char e = js[parser->pos + 2]; +//printf("jsmn_parse_string: value-%c, pos-%d\n", c,parser->pos); + + /* Quote: end of string */ +// if (c == '\"') { + if (d == '\"') { +// if ((d == '\"')&&((e == ' ')||(e == ','))) { +parser->pos++; +//printf("jsmn_parse_string: end of string\n"); + if (tokens == NULL) { +//printf("jsmn_parse_string: end tokens is NULL\n"); + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); +//printf("jsmn_parse_string: Allocated tokens \n"); + if (token == NULL) { +//printf("jsmn_parse_string: Allocated tokens is NULL\n"); + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +//printf("jsmn_parse_string: Allocated tokens is filled\n"); +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; +//printf("jsmn_parse_string: value - %c, POS-%3d \n",c, js[parser->pos]); + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; +printf("jsmn_parse_string: exiting with ERROR_PART, pos-%d", parser->pos); + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; +//printf("jsmn_parse: value of c - %c\n",c); + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +//#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +//#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +//#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// if (token->type != type) { +// return JSMN_ERROR_INVAL; +// } +// parser->toksuper = -1; +// token->end = parser->pos + 1; +// break; +// } +// } +// /* Error if unmatched closing bracket */ +// if (i == -1) return JSMN_ERROR_INVAL; +// for (; i >= 0; i--) { +// token = &tokens[i]; +// if (token->start != -1 && token->end == -1) { +// parser->toksuper = i; +// break; +// } +// } +//#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; +//printf("jsmn_parse: value of c is :: - %c\n",c); + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +//#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +//#else +// for (i = parser->toknext - 1; i >= 0; i--) { +// if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { +// if (tokens[i].start != -1 && tokens[i].end == -1) { +// parser->toksuper = i; +// break; +// } +// } +// } +//#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { +//printf("index -%2d, start is %3d, end is %3d\n", i, tokens[i].start, tokens[i].end); + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +int jsoneq(const char *json, jsmntok_t *tok, const char *s) +{ + if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} + +void printToken(char * js, jsmntok_t * tokens, int numToken) +{ + for (int i = 1; i < numToken; i++) + { + printf("Token number-%2d, parent-%2d, type-%d size-%2d, parameter -", i, tokens[i].parent, tokens[i].type, tokens[i].size); + if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) { + printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start); + } else if (tokens[i].type == JSMN_ARRAY) { + printf("[%d elems]\n", tokens[i].size); + } else if (tokens[i].type == JSMN_OBJECT) { + printf("{%d elems}\n", tokens[i].size); + } else { + printf("value?? - "); + TOKEN_PRINT(tokens[i]); + } + } +} + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + + +int getStringTokenV2(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * gParam,char * value, int maxValueSize) +{ + int i = 0; + int dpToken = 0; + + memset(value, 0, maxValueSize); + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], gParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Grand Parent token not seen + } + + for (i=dpToken; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 2; //Parent token not seen + } + + for (i=dpToken; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + continue; + TOKEN_COPY(js, tokens[i+1], value); + + return 0; //Success + } + } + + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + if(jsoneq(js, &tokens[i], param) != 0) + { + continue; + } + + if(tokens[i+1].type != JSMN_PRIMITIVE) + return 3; //Wrong parameter type + +// printf("INT parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], val); + *value = atoi(val); +// printf(" - %d\n", *value); + + return 0; //success + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +void parseDirectParameters(char * js, jsmntok_t * tokens, int numToken) +{ + int i = 0; + int dpToken = 0; + char param[128]; + char value[128]; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], "directParameters") == 0) + break; + } + + if (i < numToken) + { + dpToken = ++i; + } + + for (int i = 1; i < numToken; i++) + { + memset(param, 0, 128); + memset(value, 0, 128); + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { + TOKEN_COPY(js, tokens[i], param); +// printf("parameter / Value - %s", param); + TOKEN_COPY(js, tokens[i+1], value); +// printf(" - %s\n", value); + } + } +} + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_ARRAY) + { + *numElements = tokens[i+1].size; +// printf("[%d elems]\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k], arrayValue[k].arrayString); +// printf(" - %s\n", arrayValue[k].arrayString); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam) +{ + int i = 0; + int dpToken = 0; + char val[128]; + + memset(val, 0, 128); + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING) + { + if(jsoneq(js, &tokens[i], param) == 0) + { + return 0; //Token present + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //Token Not present +} + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements) +{ + int i = 0; + int dpToken = 0; + + for (i = 1; i < numToken; i++) + { + if(jsoneq(js, &tokens[i], pParam) == 0) + break; + } + if (i < numToken) + { + dpToken = ++i; + } + else + { + printf("The parameter %s is not present in JSON file\n", pParam); + return 1; //Parent token not seen + } + for (i=1; i < numToken; i++) + { + if (tokens[i].parent == dpToken && tokens[i].type == JSMN_STRING && tokens[i].size == 1) + { +// printf("value of token %d\n", i); + if(jsoneq(js, &tokens[i], param) != 0) + continue; + + if (tokens[i+1].type == JSMN_OBJECT) + { + *numElements = tokens[i+1].size; +// printf("{%d elems}\n", *numElements); + + for (int k = 0; k < *numElements; k++) + { + TOKEN_COPY(js, tokens[i+2+k*2], keyValueResultList[k].keyStr); +// printf("Key - %s", keyValueResultList[k].keyStr); + TOKEN_COPY(js, tokens[i+3+k*2], keyValueResultList[k].valStr); +// printf("Value - %s\n", keyValueResultList[k].valStr); + } + return 0; //Success + } + } + } + printf("The parameter %s is not present in JSON file\n", param); + return 2; //parameter not found +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.h b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.h new file mode 100644 index 00000000..f9d838b6 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/jsmn.h @@ -0,0 +1,136 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +typedef struct arrayValues { + char arrayString[32]; +} ARRAYVAL; + +typedef struct keyValResult { + char keyStr[80]; + char valStr[250]; + char resultStr[80]; +} KEYVALRESULT; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +//#ifdef JSMN_PARENT_LINKS + int parent; +//#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +#define TOKEN_EQ(t, tok_start, tok_end, tok_type) \ + ((t).start == tok_start \ + && (t).end == tok_end \ + && (t).type == (tok_type)) + +#define TOKEN_STRING(js, t, s) \ + (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \ + && strlen(s) == (t).end - (t).start) + +#define TOKEN_COPY(js, t, s) \ + strncpy(s, js+(t).start, (t).end - (t).start) + +#define TOKEN_PRINT(t) \ + printf("start: %d, end: %d, type: %d, size: %d\n", \ + (t).start, (t).end, (t).type, (t).size) + + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +int jsoneq(const char *json, jsmntok_t *tok, const char *s); + +void printToken(char * js, jsmntok_t * tokens, int numToken); + +int getStringToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char * value, int maxValueSize); + +int getIntToken(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, int * value); + +int getArrayTokens(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, ARRAYVAL * arrayValue, int * numElements); + +int isTokenPresent(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam); + +int read_keyVal_params(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, KEYVALRESULT * keyValueResultList, int * numElements); + +int getStringTokenV2(char * js, jsmntok_t * tokens, int numToken, char * param, char * pParam, char *gParam, char * value, int maxValueSize); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/meas_config.json b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/meas_config.json new file mode 100644 index 00000000..bbd3e453 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/meas_config.json @@ -0,0 +1,50 @@ +{ + "tmp_directParameters": { + "eventName": "Measurement_vFirewall-AT&T_nicPerformance", + "eventType": "applicationVnf", + "nfcNamingCode": "AFX", + "nfNamingCode": "AFX", + "priority": "Low", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "measurementInterval": 20, + "tmp_device": ["lo", "enp0s3", "docker0"] + + }, + "tmp_indirectParameters": { + "tmp_init":{ + "tmp_t0BytesIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f2", + "tmp_t0BytesOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f10", + "tmp_t0PacketsIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f3", + "tmp_t0PacketsOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f11" + }, + "vNicPerformance": { + "tmp_vnic_command": { + "tmp_t1BytesIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f2", + "tmp_t1BytesOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f10", + "tmp_t1PacketsIn": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f3", + "tmp_t1PacketsOut": "sudo cat /proc/net/dev | grep $tmp_device | tr -s ' ' | cut -d ':' -f2 | cut -d ' ' -f11" + }, + "receivedOctetsDelta": "$(tmp_t1BytesIn - tmp_t0BytesIn)", + "receivedTotalPacketsDelta": "$(tmp_t1PacketsIn - tmp_t0PacketsIn)", + "transmittedOctetsDelta": "$(tmp_t1BytesOut - tmp_t0BytesOut)", + "transmittedTotalPacketsDelta": "$(tmp_t1PacketsOut - tmp_t0PacketsOut)", + "valuesAreSuspect": "true", + "vNicIdentifier": "$tmp_device" + } + "cpuUsage": { + "tmp_cpuuse_command": { + "tmp_cpuUseCmd": "/usr/bin/top -bn 2 -d 0.01 | grep '^%Cpu' | tail -n 1 | cut -d ':' -f2", + "tmp_cpuIdle": "/usr/bin/top -bn 2 -d 0.01 | grep '^%Cpu' | tail -n 1 |cut -d ':' -f2 | cut -d ',' -f4 | cut -b 1-5", + "tmp_cpuUsageSystem": "/usr/bin/top -bn 2 -d 0.01 | grep '^%Cpu' | tail -n 1 |cut -d ':' -f2 | cut -d ',' -f2 | cut -b 1-5", + "tmp_cpuUsageUser": "/usr/bin/top -bn 2 -d 0.01 | grep '^%Cpu' | tail -n 1 |cut -d ':' -f2 | cut -d ',' -f1 | cut -b 1-5" + }, + "cpuIdentifier": "Cpu1", + "cpuIdle": "$tmp_cpuIdle", + "cpuUsageSystem": "$tmp_cpuUsageSystem", + "cpuUsageUser": "$tmp_cpuUsageUser" + } + } +} diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/pom.xml b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/pom.xml new file mode 100644 index 00000000..f9f42ebb --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/pom.xml @@ -0,0 +1,83 @@ + + + + + + org.onap.demo.vnf + demo-aggregator + 1.2.0-SNAPSHOT + ../../pom.xml + + + 4.0.0 + org.onap.demo.vnf.ves5 + ves_vfw_reporting + + + + + + maven-jar-plugin + 2.3.2 + + + default-jar + never + + + + + + maven-assembly-plugin + 2.5.3 + + dep.xml + + + + create-archive + package + + single + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + none + + + + true + + + + + + + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/sample_measurement_event.txt b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/sample_measurement_event.txt new file mode 100644 index 00000000..e66fa197 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/sample_measurement_event.txt @@ -0,0 +1,62 @@ +{ + "event": { + "commonEventHeader": { + "domain": "measurementsForVfScaling", + "eventId": "mvfs000000002", + "eventName": "Measurement_vFirewall-AT&T_nicPerformance", + "eventType": "applicationVnf", + "lastEpochMicrosec": 1548492802113484, + "nfNamingCode": "AFX", + "nfcNamingCode": "AFX", + "priority": "Normal", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sequence": 0, + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "scfx0001vm002cap001", + "startEpochMicrosec": 1548492780026901, + "version": 3.0 + }, + "measurementsForVfScalingFields": { + "cpuUsageArray": [ + { + "cpuIdentifier": "Cpu1", + "cpuIdle": 33.3, + "cpuUsageSystem": 0.0, + "cpuUsageUser": 66.7, + "percentUsage": 0.0 + } + ], + "measurementInterval": 20, + "measurementsForVfScalingVersion": 2.0, + "requestRate": 0, + "vNicPerformanceArray": [ + { + "receivedOctetsDelta": 2721.0, + "receivedTotalPacketsDelta": 18.0, + "transmittedOctetsDelta": 2721.0, + "transmittedTotalPacketsDelta": 18.0, + "vNicIdentifier": "lo", + "valuesAreSuspect": "true" + }, + { + "receivedOctetsDelta": 531.0, + "receivedTotalPacketsDelta": 6.0, + "transmittedOctetsDelta": 525.0, + "transmittedTotalPacketsDelta": 6.0, + "vNicIdentifier": "enp0s3", + "valuesAreSuspect": "true" + }, + { + "receivedOctetsDelta": 0.0, + "receivedTotalPacketsDelta": 0.0, + "transmittedOctetsDelta": 0.0, + "transmittedTotalPacketsDelta": 0.0, + "vNicIdentifier": "docker0", + "valuesAreSuspect": "true" + } + ] + } + } +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/vpp_measurement_reporter.c b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/vpp_measurement_reporter.c new file mode 100644 index 00000000..15b20d10 --- /dev/null +++ b/vnfs/VES5.0/evel/evel-library/code/VESreporting_vFW/vpp_measurement_reporter.c @@ -0,0 +1,685 @@ +/*************************************************************************//** + * + * Copyright © 2019 AT&T Intellectual Property. All rights reserved. + * + * 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. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "jsmn.h" +#include "evel.h" + +#define BUFSIZE 128 +#define MAX_BUFFER_SIZE 4096 +#define MAX_TOKENS 1000 +#define MAX_INTERFACES 40 + +void *MeasThread(void *threadarg); + +typedef struct dummy_vpp_metrics_struct { + int curr_bytes_in; + int curr_bytes_out; + int curr_packets_in; + int curr_packets_out; + int last_bytes_in; + int last_bytes_out; + int last_packets_in; + int last_packets_out; +} vpp_metrics_struct; + +typedef struct linkstat { + + char linkname[32]; + char linkdescr[64]; + char linkmode[64]; + int speedmbps; + +}LINKSTAT; + +vpp_metrics_struct meas_intfstat[MAX_INTERFACES]; +LINKSTAT meas_linkstat[MAX_INTERFACES]; + +unsigned long long epoch_start = 0; + +int format_val_params(KEYVALRESULT * keyValArray, int numElements, const char *replace, const char *search) +{ + char *sp; + int i =0; + int search_len; + int replace_len; + int tail_len; + + for (i=0; i| | \n", argv[0]); + fprintf(stderr, "OR\n"); + fprintf(stderr, "Usage: %s | \n", argv[0]); + exit(-1); + } + + /**************************************************************************/ + /* Initialize */ + /**************************************************************************/ + if(evel_initialize(fqdn, /* FQDN */ + port, /* Port */ + fqdn2, /* Backup FQDN */ + port2, /* Backup port */ + NULL, /* optional path */ + NULL, /* optional topic */ + 100, /* Ring Buffer size */ + 0, /* HTTPS? */ + NULL, /* cert file */ + NULL, /* key file */ + NULL, /* ca info */ + NULL, /* ca file */ + 0, /* verify peer */ + 0, /* verify host */ + "sample1", /* Username */ + "sample1", /* Password */ + "sample1", /* Username2 */ + "sample1", /* Password2 */ + NULL, /* Source ip */ + NULL, /* Source ip2 */ + EVEL_SOURCE_VIRTUAL_MACHINE, /* Source type */ + "vFirewall", /* Role */ + 1)) /* Verbosity */ + { + fprintf(stderr, "\nFailed to initialize the EVEL library!!!\n"); + exit(-1); + } + else + { + printf("\nInitialization completed\n"); + } + + /* Initialize and set thread detached attribute */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + printf("Main:Creating thread \n"); + rc = pthread_create(&meas_thread, NULL, MeasThread, &i); + if (rc) + { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + printf("Main:Created Meas thread \n"); + + pthread_join(meas_thread, NULL); + + evel_terminate(); + printf("Terminated\n"); + return 0; +} + +void *MeasThread(void *mainMeas) +{ + EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; + EVENT_MEASUREMENT * vpp_m = NULL; + MEASUREMENT_CPU_USE *cpu_use = NULL; + EVENT_HEADER* vpp_m_header = NULL; + int bytes_in; + int bytes_out; + int packets_in; + int packets_out; + unsigned long long epoch_now; + int request_rate = 0; + + struct timeval time_val; + MEASUREMENT_VNIC_PERFORMANCE * vnic_performance = NULL; + char event_id1[10] = "mvfs"; + char event_id2[15] = {0}; + char event_id[BUFSIZE] = {0}; + int meas_event_id = 0; + + int numToken; + jsmntok_t tokens[MAX_TOKENS]; + char js[MAX_BUFFER_SIZE]; + + jsmn_parser p; + char ch[BUFSIZE]; + int ret = 0; + char eName[BUFSIZE]; + char eType[BUFSIZE]; + char nfcCode[BUFSIZE]; + char nfCode[BUFSIZE]; + char prio[BUFSIZE]; + char reportEId[BUFSIZE]; + char reportEName[BUFSIZE]; + char srcId[BUFSIZE]; + char srcName[BUFSIZE]; + char cpuId[BUFSIZE]; + + int priority; + + char hostname[BUFSIZE]; + + int meas_interval; + ARRAYVAL intfArray[MAX_INTERFACES]; + KEYVALRESULT keyValResultArray[32]; + KEYVALRESULT keyValResultArray2[32]; + KEYVALRESULT vnicCommandArray[32]; + KEYVALRESULT cpuUsageCommandArray[32]; + int numInitCommands = 0; + int numVnicCommands = 0; + int numCpuUsageCommands = 0; + int linkCount = 0; + double usage=0.0; + double cpuIdle=0.0; + double cpuSystem=0.0; + double cpuUser=0.0; + double intrpt; + double nice; + double softirq; + double steal; + double wait; + + int i = 0; + + memset(hostname, 0, BUFSIZE); + gethostname(hostname, BUFSIZE); + printf("MeasThread::The hostname is %s\n", hostname); + + sleep(1); + printf("MeasThread::Running Meas thread \n"); + fflush(stdout); + + memset(&intfArray[0],0,(sizeof(ARRAYVAL) * MAX_INTERFACES)); + memset(&keyValResultArray[0],0,(sizeof(KEYVALRESULT) * 32)); + + memset(js, 0, MAX_BUFFER_SIZE); + memset(ch, 0, BUFSIZE); + memset(&meas_intfstat[0],0,(sizeof(vpp_metrics_struct)* MAX_INTERFACES)); + memset(&meas_linkstat[0],0,(sizeof(LINKSTAT) * MAX_INTERFACES)); + + FILE * file = fopen("meas_config.json", "r"); + + while((fgets(ch, (BUFSIZE-1), file)) !=NULL) + { + strcat(js, ch); + memset(ch, 0, BUFSIZE); + } +// printf("MeasThread::the file content is \n %s \n", js); + + jsmn_init(&p); + numToken = jsmn_parse(&p, js, strlen(js), tokens, MAX_TOKENS); + printf("MeasThread::count-%d\n", numToken); + +// printToken(js,tokens, numToken); + + + ret = getIntToken(js, tokens, numToken, "measurementInterval", "tmp_directParameters", &meas_interval); + if (ret != 0) + { + printf("MeasThread::The parameter measurementInterval is not defined, defaulted to 60 seconds\n"); + meas_interval = 60; + } + + ret = getArrayTokens(js, tokens, numToken, "tmp_device", "tmp_directParameters", intfArray, &linkCount); + + printf("MeasThread::Array link count is %d\n", linkCount); + + /* Copy the link information */ + for(i=0;i 0) { + bytes_in = meas_intfstat[i].curr_bytes_in - meas_intfstat[i].last_bytes_in; + } + else { + bytes_in = 0; + } + if(meas_intfstat[i].curr_bytes_out - meas_intfstat[i].last_bytes_out > 0) { + bytes_out = meas_intfstat[i].curr_bytes_out - meas_intfstat[i].last_bytes_out; + } + else { + bytes_out = 0; + } + if(meas_intfstat[i].curr_packets_in - meas_intfstat[i].last_packets_in > 0) { + packets_in = meas_intfstat[i].curr_packets_in - meas_intfstat[i].last_packets_in; + } + else { + packets_in = 0; + } + if(meas_intfstat[i].curr_packets_out - meas_intfstat[i].last_packets_out > 0) { + packets_out = meas_intfstat[i].curr_packets_out - meas_intfstat[i].last_packets_out; + } + else { + packets_out = 0; + } + vnic_performance = (MEASUREMENT_VNIC_PERFORMANCE *)evel_measurement_new_vnic_performance(meas_linkstat[i].linkname, "true"); + evel_meas_vnic_performance_add(vpp_m, vnic_performance); + evel_vnic_performance_rx_total_pkt_delta_set(vnic_performance, packets_in); + evel_vnic_performance_tx_total_pkt_delta_set(vnic_performance, packets_out); + + evel_vnic_performance_rx_octets_delta_set(vnic_performance, bytes_in); + evel_vnic_performance_tx_octets_delta_set(vnic_performance, bytes_out); + + if (strcmp(meas_linkstat[i].linkname, "docker") == 0) + { + request_rate = measure_traffic(); + } + } + + evel_measurement_request_rate_set(vpp_m, request_rate); + + //evel_get_cpu_stats(vpp_m); + memset(&keyValResultArray2[0],0,(sizeof(KEYVALRESULT) * 32)); + memset(&cpuUsageCommandArray[0],0,(sizeof(KEYVALRESULT) * 32)); + + ret = getStringToken(js, tokens, numToken, "cpuIdentifier", "cpuUsage", cpuId, BUFSIZE); + if (ret != 0) + { + printf("MeasThread::Missing parameters - cpuIdentifier is not there in cpuUsage, default to Cpu1\n"); + strcpy(cpuId, "Cpu1"); + } + read_keyVal_params(js, tokens, numToken, "tmp_cpuuse_command", "cpuUsage", keyValResultArray2, &numCpuUsageCommands); + memcpy(cpuUsageCommandArray, keyValResultArray2, (sizeof(KEYVALRESULT) * 32)); + runCommands(cpuUsageCommandArray, numCpuUsageCommands); + + cpu_use = evel_measurement_new_cpu_use_add(vpp_m, cpuId, usage); + if( cpu_use != NULL ) + { +/**************************** + for(i=0; iheader, epoch_start); + evel_last_epoch_set(&vpp_m->header, epoch_now); + epoch_start= epoch_now; + + if(nfcCode != NULL) + evel_nfcnamingcode_set(&vpp_m->header, nfcCode); + if(nfCode != NULL) + evel_nfnamingcode_set(&vpp_m->header, nfCode); + evel_reporting_entity_name_set(&vpp_m->header, reportEName); + if(reportEId != NULL) + evel_reporting_entity_id_set(&vpp_m->header, reportEId); + if(srcId != NULL ) + evel_source_id_set(&vpp_m->header, srcId); + if(srcName!= NULL ) + evel_source_name_set(&vpp_m->header, srcName); + + evel_rc = evel_post_event(vpp_m_header); + + if(evel_rc == EVEL_SUCCESS) + printf("MeasThread::Meas event is correctly sent to the collector!\n"); + else + printf("MeasThread::Post failed %d (%s)\n", evel_rc, evel_error_string()); + } + else + { + printf("MeasThread::Measurement event creation failed (%s)\n", evel_error_string()); + } + + sleep(meas_interval); + } + + /***************************************************************************/ + /* Terminate */ + /***************************************************************************/ + sleep(1); +} + diff --git a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel.h b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel.h index d62911de..8cdc4b57 100644 --- a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel.h +++ b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel.h @@ -1622,6 +1622,22 @@ EVENT_HEADER * evel_new_heartbeat(void); *****************************************************************************/ EVENT_HEADER * evel_new_heartbeat_nameid(const char* ev_name, const char *ev_id); +/**************************************************************************//** + * Create a new Heartbeat fields event. + * + * @note The mandatory fields on the Heartbeat fields must be supplied to + * this factory function and are immutable once set. Optional fields + * have explicit setter functions, but again values may only be set + * once so that the event has immutable properties. + * @param ev_name Unique Event Name confirming Domain AsdcModel Description + * @param ev_id A universal identifier of the event for: troubleshooting correlation, analysis, etc + * @param interval heartbeat interval + * @returns pointer to the newly manufactured ::EVENT_HEARTBEAT_FIELD. If the event + * is not used (i.e. posted) it must be released using + * ::evel_free_hrtbt_field. + * @retval NULL Failed to create the event. + *****************************************************************************/ +EVENT_HEARTBEAT_FIELD * evel_new_heartbeat_field(int interval,const char* ev_name, const char *ev_id); /**************************************************************************//** * Free an event header. @@ -1705,6 +1721,17 @@ void evel_last_epoch_set(EVENT_HEADER * const header, void evel_reporting_entity_name_set(EVENT_HEADER * const header, const char * const entity_name); +/**************************************************************************//** + * Set the Source Name property of the event header. + * + * @note The Source Name defaults to the OpenStack VM Name. + * + * @param header Pointer to the ::EVENT_HEADER. + * @param entity_name The source name to set. + *****************************************************************************/ +void evel_source_name_set(EVENT_HEADER * const header, + const char * const source_name); + /**************************************************************************//** * Set the Reporting Entity Id property of the event header. * @@ -1716,6 +1743,17 @@ void evel_reporting_entity_name_set(EVENT_HEADER * const header, void evel_reporting_entity_id_set(EVENT_HEADER * const header, const char * const entity_id); +/**************************************************************************//** + * Set the Source Id property of the event header. + * + * @note The Source Id defaults to the OpenStack VM UUID. + * + * @param header Pointer to the ::EVENT_HEADER. + * @param entity_id The Source id to set. + *****************************************************************************/ +void evel_source_id_set(EVENT_HEADER * const header, + const char * const source_id); + /**************************************************************************//** * Set the NFC Naming code property of the event header. * diff --git a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event.c b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event.c index 1656fa75..89c5c15e 100644 --- a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event.c +++ b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event.c @@ -410,6 +410,35 @@ void evel_reporting_entity_name_set(EVENT_HEADER * const header, EVEL_EXIT(); } +/**************************************************************************//** + * Set the Source Name property of the event header. + * + * @note The Source Name defaults to the OpenStack VM Name. + * + * @param header Pointer to the ::EVENT_HEADER. + * @param entity_name The source name to set. + *****************************************************************************/ +void evel_source_name_set(EVENT_HEADER * const header, + const char * const source_name) +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions and assign the new value. */ + /***************************************************************************/ + assert(header != NULL); + assert(source_name != NULL); + + /***************************************************************************/ + /* Free the previously allocated memory and replace it with a copy of the */ + /* provided one. */ + /***************************************************************************/ + free(header->source_name); + header->source_name = strdup(source_name); + + EVEL_EXIT(); +} + /**************************************************************************//** * Set the Reporting Entity Id property of the event header. * @@ -439,6 +468,35 @@ void evel_reporting_entity_id_set(EVENT_HEADER * const header, EVEL_EXIT(); } +/**************************************************************************//** + * Set the Source Id property of the event header. + * + * @note The Source Id defaults to the OpenStack VM UUID. + * + * @param header Pointer to the ::EVENT_HEADER. + * @param entity_id The Source id to set. + *****************************************************************************/ +void evel_source_id_set(EVENT_HEADER * const header, + const char * const source_id) +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions and assign the new value. */ + /***************************************************************************/ + assert(header != NULL); + assert(source_id != NULL); + + /***************************************************************************/ + /* Free the previously allocated memory and replace it with a copy of the */ + /* provided one. Note that evel_force_option_string strdups entity_id. */ + /***************************************************************************/ + evel_free_option_string(&header->source_id); + evel_force_option_string(&header->source_id, source_id); + + EVEL_EXIT(); +} + /**************************************************************************//** * Encode the event as a JSON event object according to AT&T's schema. * diff --git a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event_mgr.c b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event_mgr.c index 347f0284..cc676a6f 100644 --- a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event_mgr.c +++ b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_event_mgr.c @@ -64,6 +64,7 @@ static bool evel_token_equals_string(const MEMORY_CHUNK * const chunk, const jsmntok_t * const json_token, const char * check_string); static EVEL_ERR_CODES evel_setup_curl(); +static EVEL_ERR_CODES evel_send_to_another_collector(const EVEL_EVENT_DOMAINS evel_domain, char * json_body, size_t json_size); /**************************************************************************//** * Buffers for error strings from libcurl. @@ -135,8 +136,8 @@ static char * evel_password = NULL; static char * evel_username2 = NULL; static char * evel_password2 = NULL; -static int http_response_code = 0; -static int evel_collector_id = 0; +static long http_response_code = 0; +static int evel_collector_id = 1; /**************************************************************************//** * Initialize the event handler. * @@ -224,7 +225,7 @@ EVEL_ERR_CODES event_handler_initialize(const char * const event_api_url, } /***************************************************************************/ - /* Store other parameters + /* Store other parameters */ /***************************************************************************/ evel_secure = secure; evel_verbosity = verbosity; @@ -349,10 +350,10 @@ static EVEL_ERR_CODES evel_setup_curl() EVEL_ENTER(); - if (evel_collector_id > 4) + if (evel_collector_id > 2) { rc = EVEL_CURL_LIBRARY_FAIL; - log_error_state("Wrong evel_collector- value > 4"); + log_error_state("Wrong evel_collector- value > 2"); goto exit_label; } @@ -374,21 +375,6 @@ static EVEL_ERR_CODES evel_setup_curl() username = evel_username2; password = evel_password2; } - else if (evel_collector_id == 3) - { - api_url = evel_batch_api_url; - source_ip = evel_source_ip; - username = evel_username; - password = evel_password; - } - else if (evel_collector_id == 4) - { - api_url = evel_bbatch_api_url; - source_ip = evel_source_ip_bakup; - username = evel_username2; - password = evel_password2; - } - /***************************************************************************/ /* Clean-up the cURL library. */ /***************************************************************************/ @@ -1020,6 +1006,57 @@ exit_label: return(rc); } +/**************************************************************************//** + * Send event to another collector + * + * Identify the next collector and try sending the event to that collector + ****************************************************************************/ +static EVEL_ERR_CODES evel_send_to_another_collector( + const EVEL_EVENT_DOMAINS evel_domain, + char * json_body, + size_t json_size) +{ + int rc = EVEL_SUCCESS; + CURLcode curl_rc; + + EVEL_ENTER(); + + if ((evel_collector_id == 1) && (curr_global_handles == 2)) + { + evel_collector_id =2; + } + else if (evel_collector_id == 2) + { + evel_collector_id =1; + } + + rc = evel_setup_curl(); + + if ( rc == EVEL_SUCCESS) + { + if (evel_collector_id == 1) + { + if (evel_domain == EVEL_DOMAIN_BATCH) + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url); + else + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url); + } + else if (evel_collector_id == 2) + { + if (evel_domain == EVEL_DOMAIN_BATCH) + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bbatch_api_url); + else + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bevent_api_url); + } + + rc = evel_post_api(json_body, json_size); + } + + EVEL_EXIT(); + + return rc; +} + /**************************************************************************//** * Callback function to provide data to send. * @@ -1135,6 +1172,50 @@ static void * event_handler(void * arg __attribute__ ((unused))) EVEL_ERROR("Event Handler State was not INACTIVE at start-up - " "Handler will exit immediately!"); } + /***************************************************************************/ + /* Set the connection to collector */ + /***************************************************************************/ + while (true) + { + evel_collector_id = 1; + rc = evel_setup_curl(); + + if ( rc != EVEL_SUCCESS) + { + EVEL_ERROR("Failed to setup the first collector. Error code=%d", rc); + if (curr_global_handles == 2) + { + EVEL_DEBUG("Switching to other collector"); + + evel_collector_id = 2; + + rc = evel_setup_curl(); + if ( rc != EVEL_SUCCESS) + { + EVEL_ERROR("Failed to setup the connection to second collector also, Error code%d", rc); + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collectors setup issue- retry count=%d", collector_down_count); + } + else + { + collector_down_count = 0; + break; + } + } + else + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + } + else + { + collector_down_count = 0; + break; + } + } while (evt_handler_state == EVT_HANDLER_ACTIVE) { @@ -1157,55 +1238,10 @@ static void * event_handler(void * arg __attribute__ ((unused))) /***********************************************************************/ json_size = evel_json_encode_batch_event(json_body, EVEL_MAX_JSON_BODY, msg); - /***************************************************************************/ - /* Set the connection to collector */ - /***************************************************************************/ - while (true) - { - evel_collector_id =3; - rc = evel_setup_curl(); - - if ( rc != EVEL_SUCCESS) - { - EVEL_ERROR("Failed to setup the first collector. Error code=%d", rc); - if (curr_global_handles == 2) - { - EVEL_DEBUG("Switching to other collector"); - - evel_collector_id = 4; - - rc = evel_setup_curl(); - if ( rc != EVEL_SUCCESS) - { - EVEL_ERROR("Failed to setup the connection to second collector also, Error code%d", rc); - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); - collector_down_count = collector_down_count + 1; - EVEL_ERROR("Collectors setup issue- retry count=%d", collector_down_count); - } - else - { - break; - collector_down_count = 0; - } - } - else - { - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); - collector_down_count = collector_down_count + 1; - EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); - } - } - else - { - break; - collector_down_count = 0; - } - } - /***************************************************************************/ /* Set the URL for the API. */ /***************************************************************************/ - if (evel_collector_id == 3) + if (evel_collector_id == 1) { curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url); } @@ -1242,45 +1278,51 @@ static void * event_handler(void * arg __attribute__ ((unused))) while (true) { - if ((evel_collector_id == 3) && (curr_global_handles == 2)) + if (curr_global_handles == 2) { - evel_collector_id =4; + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + else + { + break; + } } - else if (evel_collector_id == 4) + else { - evel_collector_id =3; + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); } - rc = evel_setup_curl(); - - if ( rc != EVEL_SUCCESS) - { - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); - collector_down_count = collector_down_count + 1; - EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); - continue; - } + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); - if (evel_collector_id == 3) - { - curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url); - } - else if (evel_collector_id == 4) + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) { - curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bbatch_api_url); + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; } - - rc = evel_post_api(json_body, json_size); - if ( rc != EVEL_SUCCESS) + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) { - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); collector_down_count = collector_down_count + 1; EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); - continue; } else { - EVEL_DEBUG("Successfully sent msg after retry=%d", collector_down_count); break; } } @@ -1295,50 +1337,6 @@ static void * event_handler(void * arg __attribute__ ((unused))) /***********************************************************************/ json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg); - /***************************************************************************/ - /* Set the connection to collector */ - /***************************************************************************/ - while (true) - { - evel_collector_id = 1; - rc = evel_setup_curl(); - - if ( rc != EVEL_SUCCESS) - { - EVEL_ERROR("Failed to setup the first collector. Error code=%d", rc); - if (curr_global_handles == 2) - { - EVEL_DEBUG("Switching to other collector"); - - evel_collector_id = 2; - - rc = evel_setup_curl(); - if ( rc != EVEL_SUCCESS) - { - EVEL_ERROR("Failed to setup the connection to second collector also, Error code%d", rc); - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); - collector_down_count = collector_down_count + 1; - EVEL_ERROR("Collectors setup issue- retry count=%d", collector_down_count); - } - else - { - break; - collector_down_count = 0; - } - } - else - { - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); - collector_down_count = collector_down_count + 1; - EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); - } - } - else - { - break; - collector_down_count = 0; - } - } /***************************************************************************/ /* Set the URL for the API. */ /***************************************************************************/ @@ -1346,7 +1344,6 @@ static void * event_handler(void * arg __attribute__ ((unused))) curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url); else curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bevent_api_url); - if (curl_rc != CURLE_OK) { rc = EVEL_CURL_LIBRARY_FAIL; @@ -1375,45 +1372,51 @@ static void * event_handler(void * arg __attribute__ ((unused))) while (true) { - if ((evel_collector_id == 1) && (curr_global_handles == 2)) + if (curr_global_handles == 2) { - evel_collector_id =2; + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + else + { + break; + } } - else if (evel_collector_id == 2) + else { - evel_collector_id =1; + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); } - rc = evel_setup_curl(); - - if ( rc != EVEL_SUCCESS) - { - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); - collector_down_count = collector_down_count + 1; - EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); - continue; - } + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); - if (evel_collector_id == 1) - { - curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url); - } - else if (evel_collector_id == 2) + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) { - curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bevent_api_url); + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; } - - rc = evel_post_api(json_body, json_size); - if ( rc != EVEL_SUCCESS) + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) { - sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); collector_down_count = collector_down_count + 1; EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); - continue; } else { - EVEL_DEBUG("Successfully sent msg after retry=%d", collector_down_count); break; } } diff --git a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_heartbeat_fields.c b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_heartbeat_fields.c index 031632a3..be8527c4 100644 --- a/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_heartbeat_fields.c +++ b/vnfs/VES5.0/evel/evel-library/code/evel_library/evel_heartbeat_fields.c @@ -36,10 +36,9 @@ * this factory function and are immutable once set. Optional fields * have explicit setter functions, but again values may only be set * once so that the event has immutable properties. - * @param event_name Unique Event Name confirming Domain AsdcModel Description - * @param event_id A universal identifier of the event for: troubleshooting correlation, analysis, etc - * @param vendor_id The vendor id to encode in the event instance id. - * @param event_id The vendor event id to encode in the event instance id. + * @param ev_name Unique Event Name confirming Domain AsdcModel Description + * @param ev_id A universal identifier of the event for: troubleshooting correlation, analysis, etc + * @param interval heartbeat interval * @returns pointer to the newly manufactured ::EVENT_HEARTBEAT_FIELD. If the event * is not used (i.e. posted) it must be released using * ::evel_free_hrtbt_field. diff --git a/vnfs/VES5.0/evel/evel-library/libs/x86_64/libevel.a b/vnfs/VES5.0/evel/evel-library/libs/x86_64/libevel.a new file mode 100755 index 0000000000000000000000000000000000000000..b35d58e0c40f5f2db14e4482348c26a7e4ea4e59 GIT binary patch literal 562325 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

TEF(B-Ve?+tL`6 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%0Dav>x zwH%tx>1aKpG?gYCshY}LmEBf%l5fOfqc~vn@zdW-YVCNj_HfxtXajqgLL>vkH>)ic z58GeZ1Wrd}9J&U+qL{+$lq9YnEv_H)0t|b9@gTP)zO$*NK%|VF<%-CrBew=X@j$wZ z4dSPM5}QTZB8z4$T(GE;oV@1En6kKP(TsVH$3%>65?T2j#M|McwQ4vUq>Ex_rxbfF zj+MLxr;Nf+tK=Q8LkMIk$0}IF62XLC(3S}uw#ZJb+#H35>jRHl^3btPS_i2`WTdnO zoqGh{iMjkBc-6Tkd8oP12w|gQo20amX55sG4WjfQ#rKJuq=l^P3rAoMc?g2GmJ5tf zpylqeeGs`0Gy6h1dEe6TWGoDPyUBLwPgVr0KJ|f!F_N$`jN)aVnYk$bl)bZjXu|*o zcvzgd6crYG*i@(e;)_HP4mO*^<^pUS`!|w(!TL=tB{ivp*mNbhVLn^b#8(>BqK^_mXM9WZx;MwG#6kd~(CLC*(U z+ir9WVY0`!fi|uE4sNCPhb=&^zOlLzbFlq{Z6>x_4{0RYrv;ZdO+K5KVdV4q;o@24xP*aG?T0q^_?D3R80<3Mvb3z61#=OcsV z!Ezlv6YxuF-SNgzA{rkg*pbEd{fC5V0oVF$5UD~N-9`?lrU&Cq%HdFle&|T~?o&Im zv$u&|hts#9CC=W692(FTu?w&kt_e88@&TEohB<$T7$Dpk8sl`yMbzH61a-Lmp5rtJF2XKO<%-)fLXz=_O>{SF zFsNv!6`%bq$YL`&pP0h|7JWlt?wCCOv-6^3(U8%x)*(YoA??tzcBcDQnHS}f{rx?{u8hyWSU410xF`}g0S5k|xuM*&42-C$*QR|FP zJ8vXscWh$H@m|7E^TTt227alc?qnXVTe~vg%t$=j*@p+bY}$bhvub$|l3s-4Jc-X4 z(*@s;7R;J^=&TVph7W7N#IVTwvF`mNSyktV1%aKk{$~gG7PV^^j|%WzwLho@cV#88~N1N*H_n9}XQ6Zlu=G(I zi=p7Z_75~if#zV){P18ju_;eyCiGtgP--?xWl|}5FPBFuan9<9q?`6!CMI5nquBap zvl77VU)%dCo%zN}AV zDg|u^f-^adw}da*k4<-6+E-YRp>~Mju|XciMVDZIm};LI*&p`s`NvPXSy(xxa>nFo z6;tMy&-kI6g-K&j4SdXjx~OqX*4Py{Ew}95V!CdH+g5bXsw*ZwI+$(q^&#GuJ?O3# zUv|Q7z`tW}gH9?gw})THB;qw}v5+^Y=$)EBv}FfO9XIef!Q*^Ru=c0i#;T-e4Ak*J zVv==`RVl&qy9OV z>7pg}gkUyGvckd9GAA0zK@mjp9l;ULm5O0pCP&quZ%fkc?7Ea~`G)_Od!(Xdu1Ucj&h zs}GOnL|G1O2*T~0#Fh}{=qQ(Rz_5!q%OV$X3Y^XRjy~GFACIx`$3H4-s~c4tUf6J> zk}sA_#v3sUDl73u+CiR-al9NcaxBIP?X(K%6V)bn+*X_qILRUEJU#QkBGs{<(REri zgliys|BylM4F+DbU_?#G|E$^V9J5ndU@u@mfO`rcl5iWoXiu>lNH#K!3ohu4il;cn zidN}$5Qs8No%H5|yci_zW2jNyIqIPx+8ig(kP!s74(_#N1RCO33e5D)lvW|ZNqu|# zeb-#P>zy^Cpia~CtOVI+`e!B_I(HT6^1{#7~TP4am>Y} z?jziOG=MoE^Db)K!?sK^u9&6ndA$n0q;ku`S=l)Obflf)iYhDuYwKTvVc8_s82Nr< zF!`nFESFm+8hIVZ8-x5yCbpK=1efZS=Ax24c-3H~xuj%|xJm-M5_(eUe#TM$s=Phs z?aM2<;%mL;9!+GwzwQH$f)S@{w+J|1J~VDXHdUlD7+eBao8q}5E>5i=aG(r*iqFT` z?XL}^N?|tS;mg+DmH@$V0Y?H3cGfG}f5OFw;z{tR+tMH!je+T+u5O~JtF^x>?iw#s z>L%gEq&Z?z6+K|1Gu9cCYHxQ%%-=45e8dP0U?-aS+0CUG=DMfV*VKmDCvA|sePelQ z0_+4LU)vY|SeLafYg zYiw?i|7#nY(SOO!A$D`lEv%hjZ2Nwrd!PY}ZNSGDeR)XX?gG5nR*Zi4o~Wxo{Y+d} z84Y>XkM1k#!lU~dNBhk=`6EX3Xb|D84@wWWwxjGDE~fuL(p8pE8-Y6@0hY*Xc2iTE z8spNrOSq<^s7jG483WRc-f5&o(H%?5kd$waI8R1c-RkS<1tJ!~2R2bhzlbQVUNwRh z<7DOsKFJ9)tMDcp$5`9eBB-8&gk@Q06r@v;3j8&?vS+ASIm#|;-A2iH(G4Rlxtqcx zLe>+MBvVd6plVIxMxte$++kk>{<{2>i~;_AOUxxk0UlS~XcT0MAq3EGg?JCi7gBO! zWi+Wl%!5-kr>l}9>rPNfGmcKj-6J=Z9T29CE&){1kV~8oaNI;4EV5Up4aZsyuJ!xj z{+S2UENklKAI`#YD#!logB?aCj0Cpq$`xxc=MyiRSU8>Nb54_CUf9n_ z9lqz-^i9eLfm39}nUT63?FG=49`YP}9#SMD1f9QyKL}J|Ck+X_P(HaG3FU!SO~(J4 z_kCM`(l^@7B)|Mt-uIPFHF;h+y;cahyA912FBe9h{K9R9$UC443ihbi{U~U3y92Y@ zBT%8qwywx0M5CWXVx74+gd4#c<<}rh2@NYlFW2lSJT3>?c?9$=k)MLeURnM};Wc*j!tO z4N)?(8frz_($gQ)9#J+v^(2X@2hQ$KT$x>n0`w0>Js?+D$EP^z<)* zOl$C_Q~4c~i%}JhHC5XUeim{5n8p|?RGM%Rqm{`@y^=Xn^o2Vi61Jn?0NF)%~6@Q3sa-+p={lAqx& ztZim98&7f*rgQOjUQfR}SN2iL#g$K=co%=f^9O^e5-&~!Hy~JV4D?H-`F9gUEoHNd zRJ8kD%@6KJbN5fXN$eFn)`^!m_+qnp;L?reI^*41n#~G!AHZX*rTlo@%J*WIbT9_U z=hf&MXs*B2$jw|Q-djfx4Wrtu%w{s)CoFGumF;NYn{cqd0j*81Z;ApNmi?w@Ot=;Q z#Is}M1s3Gf?I=I=p#q}?t+=FlF_||l8-GIcf*#UcT(My8^vTl}Em-`6J=15uqo$SL zsW-D^k8!NvLkD)*2NLYQ{yOhxVo#Ia#cJNRr-h#vO!>6Y@V;pM6qtP`PW&E!vnCOC zE-FWSZjQXGJPyUs<6D?gY=^ddjja^B6lYybZWjEn&|PO58%8*_MRBQtWT*13;Sb59 z4cmj2`@TH)TL{tIP)T>LyVdQ74r@gnRS4_+LtPMn3hcmOwFAfDJFs+j9v*7bsm^9x zRUsN|tp4pMzY4Hq!K`UB&aZ-pa^*!o^mH%%fa+g3-P_ZO&#GI}j(RwOolP+E$2MbJ zDnGDu9dNuhpR!+C*-T(V#lJMFuJpCTWv+*WEF_Z8cXh5ph*0$sNUlBI@lrH?++eLyk}A4I{hkb)u1#VzgTLne!#`l& z?)n@%yZvkP$aCg-qazNiLfq7~VEM;Cyps34rcSBExlvo*&Hvc1gj?u-7=w&ee)YCN zxnQ(Eh+$)QQ|FzDLmC&o)9v^3?0N8GsDq*XG^S>om70&8Xsr6VtcmQsd!AyLA^1o$?2UHO`q?v*$9tug0Sn$xp6S8-$hKi=L1Jd&#F|F2;N z7?33(2&iaSR1|?sNM=#cWRlKIm@G}w8DHfKSsuSFswa_Ty>BD(-Hp zR-g8wPt)%eGGe$N5LtQT8TVjy^V5@`Hzbe8-_F4Ukx37N1yR`qWrh!WD0P@=U zl~+EK3((yzNL_j0`*|y8Px7_<&8)s>W$sjZnDLC2hn=yq@r;#6$^Tw^Y0_Y?Ua@*^ z=3bE{;#v6l5IuEf)2nFyEQw#GI8z=jgySQ@cq!afC|77lv0(b5aHKNLVKE4Kxh@S4jWq$%UI+d)m7)ePL%$ zYg?{!YbF?&$g#`l&hR7if%~E=-0qmHb#i)yVY?Wbm)HY}j^ApT=@oVGr}e zthrN#&Z*I2c``3l+M8?9oyoPfx8_;{831&}mo9%%I7y$Dn=Vu?C{$p(emVzsle5om z>kQj^`@@0mTz|N+wQb9$zTSbJc5TLT+>G?eyWx6rew?|1fx4ADv=IrX+*w@;(J?v=h>~g&#n;+guPTEh^4aO_n zQTaR)!sqpG?(J$1+xmL@`-7>%1!09Bqjb+!50{gvS&HQ-43s;lm5XI+yqz=_ylc50ngpq5%%X=a|8XNKJ{*?kofYJ%=WOSH%C6r%}7Ss z>G~R4W^-?Uj_ho1Sdw~m(TYp9Kv9Zn$L>rY8RQ^TwC=*)&fF!pJxe{0I!ss`4Y|vQ zyUw2sD>IXmdFcYf#i`NpF;aLW6yM-(L7q?PU|1>_hXdK~D;%Af68EN#SuRc#0{(Vc zTan_2m_zEs7tQqb_4bjMw$mje*xJ+E-P*N1;STHoQ)7->A%g6+|L&NcE)}VJOpQ+E zsa?3Uj!?Vt{jE?Y%V+CkIq-dowh0;Dq46u^9Ya*N9QEOj&Yoa0e_<5B*pg-T2GPDZ zEAgqbC&y#Kbg^RLL5{XlwK{vk{_Xu;y_@s|^03a_c4_ep1@y2#)3>#=4ULZSeVJ@; zUyihy8mpK8%uMkc|Amp*-w`smhh4qrh>f^Y6lW+dIe`x@9>w9wVx{f;LV(@v*&Rzf zZgBU~C{R&RGgDKA32N<$b)oj?$n@+=9L3NRBS+pfk)Ps-F;WGG?rk=I4zfj&qiy)$Sq^}!+ zDAu{YAWFquE=#r!bD2I;sx{|C?jGt8;x!fq*>|;h;I|0A+(Bu;>8kRm$#Guxq(M{c zJ5q4Wrq0HrqA6N6EJp3SxpUKIwIrN_TrJbrLl=~qAO?_sIf2Vm$HzKl{ME1!BEag& z(M3>&cIP?sJl?4pa?C)fW>R>`j)Ctyyc7mI&v+@32I~yZ3BH>{b(pI+1b<4i)KK!xP#-8 z`V{$Ntt62G>KN% zKy9YckPmfcy4w4LN`A5=<9~i)tXLkO-Z2@B%#`$NTdw{83v0{N|j*&f7==w_{(DjG)7pSM(~LHDG%k_9r^hZ1uuFCvp8Iw&^Y0G zYHx0HrZ3>Z0Lig6h4VHJ=$l?>|E~DpzE|x75+-GHQQumEUsOK22c(VBjsYLL_TYsT6`kWq;0)4 z{`Odo9>s79|Gj+@+oD8C2l-u75k3Q3g;6{+I;XKz|lz+GRRh`ydZgZN1&yG-ssTm5c&oSOP;y+=auG zqfy#VX*yq&n!h{K+CR`IVN}&-z!_(&HUzj`c-hB*t^k{eMNp(ZPH z+Et-wMuT}J*xcH`IoGtZOB2OcaWC1j(Lz>|DDuQ@>hUPGdKp_*ey^J1Y_FWu? zIA7If5B))!O&jG&QC=q(7w76|rOBR?k5dau4TAQrPMT*}J)&uKlw79A#tyQTdKAp< znoq7OjYI#!MAoyTBq3bm5sE965K!Q$&`LvTT-Hcv8KgoHNaR>1bWUfkO$K6WtF671 zT8C%Typ?ip#4KC9DWU&4-+D@pLec$e$X6Y=)2xt|D%vOkLsd5AvSF47R`Lj*y!Ums z^#_yVBW*O3>Z4Ty&Exd}D-$I-e`Mx#(>XCOirPr4;*e{rPhtZ44^j9 zr*bpObg8R#6EbA9I1Md*xs91tn#Ri2X%%2GWVSjeqMz7x$% z(Sngbbhn;GLVOH(MU>u`VsQ35fw6-~Q&c1*Nf72`%^1ecOn^ohF#d2WB_CYsn_ zde{}MPjJq&MlEcEA*04>bh;a#sv@y8n=2tLj|$3p`lLEda)&t!pQiPisUod(U?g$R zUSEBEBwQH7+j|FSw5#Eb;>g&FS#GA$;sMP~$}}+WoI8-1moJy|bQ@3eE(>~84YE~7 z>p)k|8;>dT8G@qlsg1214B;5l`7xX@PTfh|!5-wKiPA-dV7N4c0oYw0ainqwAL!ep zV?o%N%XF&^aJ6K(-PAKs-?nB=eeMXPNDe*XNk^45Z)(k+tJ$9pRw9j zG})8`QKk`g(n7l1B5nc-UyOona{h-ps@BKGT(dd1HO7m>(=`7Xl3Y`!T<+W-p@!C>Y1N{J1YI;D z37SKzx2j{Q6`lJ7GR4rvf1bwc8M<~|-5uAZ=f8Z*Jj#E0(u*uUIpIa-he;VnJH1$x z7U*Mb=$vHuvLt5Ggl;IMMFUQ(dRlvEl9E22%FIWW;}SF4({>am3#XOJ`JMU7j?*ZL z?)wDpHt{Qe>q>?ecI5($a%$~%4w&b zdKxc<%E8bMa&fxB%rghO#e|sy?(I-Msdrn{wz335%b~h7Om|~wb%kY+=g~bGx}88V zM2&`bIXZU5eY`e#%b zWH@mK<8B*S;T7WKnnix(Ji2j4H#~ZHMMy4WBaBSuu}plGC~LKo8xAaaG`y37ja`$(ZX!AT=EBi|vj=)V#s>Gl5BK_$0*+^7)b1s*~s+(Qo z9GY2XsuUf_cJ;QlN9R$6w$c3~y=@fotS&mF_U)(I@?B7qn#**NLYkBE4fe??8r;h? zUPUF^JNtNY-`hjC;Owln%>zAKXim@`b$+p~PW`MN)XM!`)YmA~cDA$q^hRb-rp3@5 zGgCVwX=1CswyYXhPs?`p^kmwz0~_fsC*9^YYHnn87 zx2K<$kZ663!bWtke{*YJY=|6}-PpCIzw<>gY^62aYz`-e-Tl$InQhrjTP|GF&|qTS zqiq9ZnQltO2FM%b9GXT`)00zn_H@P?NV^PByLe4PF2PPx?G$EdvKl=p(ACLZwyi&N zHm;$~j!Q+lQdl4_>mhw({adqHYNz0={_dWzwXd%=Zp4n>E?Si6>TQz|Boflro88X- zj9US$h&D;4XA|AqmwIUxqf^+RRJkydMJM4CHV2esXe{JsBBH{r?d{yIMvlo_K`dK(fJ%Dm{fHcu5lN~l|ES` zG)wcJ{d{{Q(?d6)d7i14=-m>jZs8))_o=25fMh*X?anH3%ZejF46UD1NTD2n6pruO z+r>SDv+iV6Ssqp`#A$na`v<7&ksC!Snbx_?w%FTPCF(j|ooyj?pU5Jxb0%e?%aaW0|nv>E{5M8l%V?U*1xlB_-gyQz^--_zCZq&fNk(R`0@r0i@iHgHVt(Vf% z3A7lCn83lXlHXNGM8l}XVi6L6Hg2ME4Nn2#x*C}r%S&}gm*`Xtk!Ckqv2Bl|0EgX8 zbYI7sh5v4BXrKVrN`W)N)X&(c96-XZ);?Njwi6OvJR_Zgz1Yzw(U-^(^yv+4WPZS> zol;T`=^V`lobbJ^m)oN;q_wg>r}-q6q3n2T_0S-Pc*Ry?bL0hV5MGSz&t@n}b#-<- zVXD2SpN>(3g`L@^s8Za#6dySo6h;A7Mvag}@$*-7`zS~EJ|+9%DS5by9yghxbiu5` zVmUr+>gwG{vZ+sRj!njHKtlxGbx|Y4y_x)MYes!B5*3XLF~b5W@2q2Z@(OSDc$OBHc8!QBs+mySm1Z#U)tym&{q5@@W8T1NDBc$NXs!h z`N=xI5H!|NiiRgIr{ji~*0!-Tx}?*EBBlvXBDhjarbJburo5&cmWS<<9K3Nk4jS@^ zWaI)AXsMpupCsVQ-HHGBjG0!W^L!7JX3f?JVi@FHw7e8?nx}2$M@p<5YuiSn6^#(x zos^;V&_fZOef{)I<*MF9F zf>A2!O}tKME939hYCNkxInxiIiBbF(_e@iE=g|zl>r7>QvE$LR`(ljINYl~IS zXdt!6ttsK5#01u3sq*+%!pC$rmCU5MBqyfaXPxXc>~3$4>cM@8%8_rzO>jUmY%C;5jaU?F+)z7_yuNa3rt{U5t^Tep6)QkIZ_MA9W z_VjM-ZKsrQ*L0qin&@uDC_lGt?PJpo`@1hglRYPsrdIPi}GyBFlC9#TVCNZNYiLms?sP^ zrYAOOazGE$>ok;&&_=<6o*RsWx6+w3QmRHW;iN<~wuXdJndU^(NS49_Y->05SzZ?C z596w8TT&5`D33;~Xj!Y{v4L!JPA9D;C^nyLjOT&mdK)Pj@C(FFOYw*58%03YN8DV) ztbg7H0aA5Y8x$K_^_{dM(R(q;)nyQ;6S0_VXEt+At25u>mZby9MkChW9B%6GZ_Ren z!i=WLaUX1s>nm|GQc{y6ekh7=xm6+zLGAP)>ZasMi<1wa>X_vxCWU6_=5>)sx{|Dh z#(jx>Gi>b-J7dHXE;4p>l4a{`2#&)voG0t;FB)b-$u%7t#zr{3F_KGhj~t_It4$~q z^I@5HN%u)K66-+Mmwq05EVo8%c@Aa#?7KC1W>dR%#E4LLK#YyiCVEs)mS1F=#6g@o zEiGG@<3NBOK*4R(=A6bsdVzvgo+;rIK~zo3Bc!cl-a@zPwNtR#Jk}>1n8#GsgC6eL z+-m|DM$xmM?8DL1YJ&BTFj`V|m#}own;4_EQJ~_kpD-b(jWoT9M+v80y@Ihajg9N< z6)5e*

    xZvJ0<8nS$;zz}g_>XFQ!?8he4W1QfT0vm(NQlBcY^BFPH{ty?LI@Ql>V zY)eUNBil&G%x#OOCi0*Y?NB~KH_uH!4&@}aogTGvma_R&xyerhbF>c0b+>sCW%c}o zb9DKE9+^ZSE%Y{QMHFwsk)rC+j!YW)Dk1noprnSp^eLRMbQow8Eirfb6SvX`d*G>IB1TsoN*~8-XDKmC*Y>F&nX$39A zMLANgmmaxl?b%Lt*X%zIe3FhPx&T{I7X}lJ$oAnV$@ekkVwnB0%4kYiHmBJtEt1)_ zwV&~9$gvsw9uh{HZ9B_-)ElCuCG`hyeutKEZz^C^s;WY~UhSr5n({dzhL-2JW}RI; z;KcF21C#!Q31sFHt%XLbO4cAex#AAv%&WwfG8{+k!}E?PHNnzDD=kvdN~&c0j%^z$ z$<)^VcXr$2z3Pl77`ib>>R(DGug&cYcFjiPr=lQcgxcElS!uY!l z1o3Eyp(Abe)&iQ@j2&54al@G@dfbL4IJ}I(w^gH=E#JDL){tkIZK;EuacvW=>xBA1 z7&buUWk#OpgmOnSum|Br>Cq?l2r4~wL7pB2pjA-k7*C-10U&x;J9-jHo@q2XNpI+0 zC>K+shao){Gd4x%T_8TknmqLi!=Z99KQhb@iEH_t6n?e5$Y*K! zorRea4p&O!^n87+nkqrtfBdH(AaX22E21I214^||RLprYh@Yx}D~jR8%nU11eR}bi zB*=4!WCvk)qkBm2{}v`o^j6M)++yxNo5O)VZo*GA@EZZV{4K9cOw*fJ^4c*TazZ)BmU?1| zZ$9veu^f4V)cIQ+3Z-W%JKW+?rJciiy6M4BNkNu+GrtNg%I#p!oTfLd!X5N%8vmw4 zrTloA|4H@v&l{4jD~ka902|*frngEW##+#-IzNZ}A8%2)`6JKrkS6j+{B#dJWG?R> z>N|&WP!`)>r%G8M&%M{!@lhKqfhOZ-mWHa7vdctIo#*Zfuc|-U;ERsnBzO zBBrI&xH1X%Kp_us4^Z zfsqoQHi}Udu04f2dIX)GFr|%v@ZPF4s9a2>%0sDWHEJgsz4Yn{i|KR8qIY2zzfK?( za$8znjSsProTI-hhF%-z$D194JNIowxTB{&LPxJ1u2<2k7}r)qb!2uVHsE+hZbL7QQLIQdlY1oCp&#+k z1VYZ#H_l|^6}_hON*DY$a$k8VkY1P3SJFgtdWw#pnx!{-dg(?7$F#o`3N(5-@94Wow#Ma{=20E0U7T&CJl9PRZWyUSkB zjyo@cuMJi;wUn(mhIh$^Cj6>ttgCY&*ANs%V?#VX&gj^|asmfwZ`3fiH*eyaPScZ= z_NKPf)Lg8leds-}qV8r>AyL|S7-6$Y&_rH6Qy$T#)dPN~Fpi2& zRTDB@52ej8^sNBj!QDdKa{5ShRU?ajyoCWxug2n|j=yn<#O39bc&i%Oq()6TLw>~5 z2&sY__?()^-V-7s;r(t2RyPXHBFiF&uAwD+p2&(`9~VXVWwUCPd8dPrgJ<+)0*4xg6TO6(NYH+809@1y98^X^4m`^K$Jdv~O35WQC^ zFIC26Xa_XP$ML!ZznCJ|;8f9lg~Eq3v0>^qi;+0cmdBCMS37LzwM{LA3M zm+C9wT|qs-yAR-Ei4z=76@wDhvT%+{&|0~QxtDUednw0U-j&=l*t(C;2&i?j!Oztp zYNABLc}hFgacd77r;CVR>!C$lz7@p}jri@!7T^f4yARQ-*>s*}gOy-sVK>iU^%w1^ zBj-_JeyMLFB+UQU7bAJZqCaF3flZ9)LoJheej{5>i{*#(YTnEczkEGZ7@@MsiHlRt zl_Nca-q|}(rAY>cqO7k{9_Sp%m0+c$0CV_8JlEH!iD+U#ch2}l#6l_Bcw#s0X36K! zrfJYtUKOW#VkuP%dJ{Kr6%JV0=*rmGi6%E{+gM-MoV6{5Yl{^gA76zXsaoc*j&-@R zWLUiC=$M@2G3rXk>Al(tO|;thSwJd8jmJgf_KL3|=h;DNw^Uz~-7eG>NDwZ>W@nlH z)*(^D#csw*i@#P`+HE@Crj-3znNIETZ#rK#hw0K?HSsi>IFzih)4O$ie?Q=*XE>#} zYIQ3o_c@X4P030WT|#Q3sjy6h#df;X;m?d4gLsyFy4K1W4#lk8zeePpd07gSE(*(| z0sS*@)D;)yTI18SQwpz`NdviOa(Ifb6|XUg0ikWs zW(GPnx&$*>*iWj!)}mt3XyBY0ji|K0K$Tk3a*j&Qd2tRPdg_WADKhR>c6fre^?Q69 z%F)J}OAwiWHoKe5^INWY+MP)@ll7ZH>s!Of?(pig&Cq!q_@dUP>%^vkz1;Le&Q!a^ zJ%-ESO<>tr2iY-B=hh9wv8fpondx3)f8O z5k(Z|t!dp1H`?}!2HVG{%WI8v-`{+NcHH8Z=*g#?I)uDyaF%XHRi=xR?hEXRN>v{g z8%H*urfu#ns@@keIxZVk&{CXh?j6n!UXPDC;>BMXg!m&x;uWfijAb+u%b4ZxXzhmg zpr&molHTRXRDryItM0d)+?_HOEshu!^UrejA&XCmN@KHL*^wdbux{ML8-1i5wVg3J z6OdZQa{V2DWoK0i8#Vyo@uC`TV3m!wTKA`Q$P-M}$CI>y^D>l&ZiZ98Z; zT?tEKRreIy$c8tLrk7i2v72wZ^VYLbYs(gg^c=m69(-?%wz9<5HxyM%w1Pu>$W7^7UvxC^@xQs>q`j^fzV8!@EYp8F|8?BySMYZasVv zdVfbtk%n?#k+tPBs_Ntf66t<)VxnwXt)O*Wohqr3rL<_3t=xL@2*JBLID?COSa;+u zPyn4A&eIGrHn>|Vz997Ho0irOQwvh=G(z#4>T8x|D~oAeqw&9~vB#vnQ!N->Iw~gj zu=wh<=_PFwZD+HnA=y@eDrs&6hRcW3ER_P0F8P`f=u z8LOmNDE312oT_tELQR@e=tf-Z=H$)kwwKWCpG#9{n)VmUG(5y+@e75te*gd3>!g}d zTS&dqiN;x)yQ6Oc%Yu*weP1-kaiACgP4s zH|BW{5GRiCH7m614)t~{jkq4cVP7{K(+*TAj!uu4&yNL~dqsX4ck%RQatkJ0rOGv; zlCfKvKe{Tyv8gA-&JLBU)f9KbOg6B%m{VH%g;XNyyH)n3rqaip z+@_+OrA)8#Q$}o0WH?eeuIoq;N0Fo+RTm)#uI?deA`#S~5rR4o5K(xNF20L)#-!nk zHjJ@n6<8QOm!uzl7@Cp&BzQ}DigVJ=X@f=Tz+>LT&+W;Xu9|+uYPdh&Z;RaD8z|Ug z9g(#qRJTg(XJUjZhqMsNgN1x;A>G6MO327ZZUB`NBWZfvpu!k-6XUuFwkJcJI>rt| zolo7d)&NywLJ-%SdKeGyGD_1wlO7mQnjMXMRn;V|V<+@lCCzMV^1+b+}_nL z?KV|%mXW3g)sgq`_tIEuGa0jyp}NNL8ZMsNxz}~Nt4`Y}k>S9`sWGm|sdGYkI)~Sj zDi{6RhNvF9s%l2piNln&g{$Ex?dipNN#v`M2kc!|>{)whE=m5;kEOjPNuOjkpBkWJ zhqGXUxj`HYSWON1$(Y9SlvmrAk2w<3W((RaHBIHgIWC^aljGoAWhLr=u}jBRO4C)2 zvMV5FIb&gMqb4*`qqJfL&fCoV*MRlTAZ5=EW*~CIhrdc8$;9^TS z-@}rHch$wljB2OnCh=u!DW7r@UG44Z%EgZ8B=iK6X^FN_Z9~M3LH-yAWw(RHz8v=k zcyq!}qbR^$sw0VH-&!c?A^%P-jGpAOqLWNOb($7sit%vEj$ONkQqJwdvFz%zEZlK2 zPcL7P>J$0#NqGaCM^JYRq8)W)4Z>w@*#Til>XQ99vQLf5+h{z)-&mu7rcW-(?XgOp z@4E40pL7SyZG{?cL~amw5OlFgj^DM*r*--GQf%Tx$E#6?$XDXlNpvySuM)O~rD-f- zJ6Co6)F4a36*PgEfoD9 z+}`?-HeXsq)hD~@=8p`cZhzril^@U+$E&HBGfp|7=>MQvP1i_qqh!A3wp&yRW0IesAVN6HLrF*5RxvXTIZ;K(a=Qsfg4+7e-OSGQ>9WQ zEb@A}sB5F-q!V2s3Z>2f7I*Yyz>Yd#+K`nt&*C`+_v7m1rndf31KL=`VOXPZGL%-O zoOGdNR`&1WlT&58Se0HDkg;APMz#4?qG|XWe{KI}K^mh@)XZ|Vy&IEkF4xp%O%GaL z+gn}89&|*%0ve4w+=h-C)sDo8N+d9KWyegy%ckj`Z6)N6QhA1MTXPrD738#Ox0R0Q%^v`v)5$dB=zVZqaF7{L)qT?_5i`h4n=Tz1bJvXLd`( z&rd#1!=z&cyG^8XQRhIw8yL~cw(_YY_imj$$I}{p<0-@^^x{pX=|x_ClQ#O;jeeci zIW6&(8Hek0q8_mAiqQ)C`DC8FKTdbO^;w1J?r!|fjOfX)KdUXYQrHCEF^_!t(5X|R zTztAycWiA!I|j68d+C8G+S-R+*2kuIdH$l7d`FX>x~mdP#=^2XJvcZ`pT?2z#Z?#M zm*Lf*6gBDHwvpl_zb!5W(=U%NF*y~BUYAD8=*Kj9=}ex!pqp|P{ysp4w-Dhs^Yyz$ zQKjf3u5Eg_+F!QQDQB5jdJcp?dMDvswJnwL**#_Y&0*J8js@F??D^zeVbuhi}ZJ9y@(6GMn~t3}2l|m5HzTr5;l0cxtx6ChfK9B6vRp zc%Oc%Sic7&BVp<_`2z*51GM2x;w*YJ-bs07_z`jG7o~3(@m?d+tF(l4n?!T4qsqH? z_Q^@VvA57h4z!a+s*(xABowXC=KXP=U_1I#=i-hRAJ8E}s~|luG2+#%TFu@yFwmAw zSCw|}qP=+4sTvciY5>67qrB5%tRD2nV`Ei3TJKFYj(f$#1(62u*WyKF>e=>GD}UIX z3MK@M;;Wri3WJTQW+vW#=4VapJBnwu_0tx4v}vvG zbtjjVH_7qU82#K4wCCp%dAkOF%|(`LbVm~1NI)*8TR!O%j8syd9F+H)<-L9tqu&OS zZA`S9tb@EGZLe#qSSf*f+G?NS30er5DN%+uFojBli5kQu&CBTLo?V zy*5Pjr5<_?U7tP=5-RC_I8sA(Dt9jcc`g^`ax#8?=9W?bnlNaKNrhAwIH=Z9eGlsC zW8+#BjV@2?l2Rpl6QLl_V)OeG6>Tc{Kzn3H3Xm#OUJ@~N?W=;~U1E6G#BK*5xsOv4 zX%1q87JH&R`z9aZK(8~eq`A1Y9zKdLyM#L~qLt9z<(?&90^-~|psr1ym*Ec_O4HHK z1=JD6LR@Jvcv{PEQ2acmk=KC=BOCQ8ho!z z-GHNg{5eDJ*={U_MB0#p8V%E|$owfIx3wS-&`uz=eSYG|K>ZjyBE@RxhZ015r0*yh zO1`dUJppHl7Z!IuP61E`2Yw%pl#_?XMM=(ylSkBCl6IsWkh7yt_=*f<0(vAGH+bVO z-pNAtwAMX-q78a^3uJ!DkG`$ZN85ky7@<$s%NK{-kJ<4wowgI?4s_R-eMQg%2`Ll`~nQ_9o!J(UWjKlp+jx4KG`J$Ou5q=jy4C%YEgf(Gvl>@+6! z{FF~}@#i62N25C60})~%3UB&xD_%K|ZLdBmJ3NajvX~khojjBtotu_qj#h!p(Y^3G zM5oeGs{nlgvO+sUp_;PsnC;A?0+1W>^l@}IJ=IAQqNviSQgRzp^K_b_(zdN?v;LvWbxLM(s-_2tbNFFql@;?*Gm${qkX&S^INn5KRFq$$Cl?R z>eaO(oEu9TN{Y~4h@2|Kj)0FMIUiZ&jc{dQa*ChV3CK?4^nr1)JKucfp|deoiZ?;D zZ%RomB6V%g(x_L_CsKLQFLtx2DBDZ6xuq}O(JqW4-K^O`lPx9UA?`Ot1bF=tF++BO zi0aLw*5fonsFO{Ol_^J#>c`uoI@>xY^p5sdMfK=I7`)`8+X$=KBd6h=tKvUKXg5EM zf_hbRd}nNrRFHUWpIzccy(n`1s7$oQsWf&eU#VO`FVH%TEgPvK7)cM+g6wmmL0n_$ zAp9ve*)o_sjzTeg*|1B#V8JZjFPZl$cYMSy8_VLIXFD4MnQ2Q>#&}nv;tvn4^jW7i+IXy|cR)A%lvd#O@;e(vOB+k+sb)k4Ub;a?*A;!* zU`Reyt&)qsc25jPqt^+|DeTLKe0)w9&8FDc75$cc7))b7&~x z^XN1aAXRDCP`dBtnSgc>CQ{_3p|} zNVjq11F2V&z(Q-u1x_RMLR4;Qx}QH@kfXP_Xm&DLqHk}LC$I~lxoK}I=O9(Ilj1=q zFZ{|?5}P-MW!^SO+>%}7@wu5J8esQGY9Ol8NU_|*3t6HdJ5NH%G`+odEmLa7$)s)U z#yw`&+1;{3AB_tsrIQY^wU@p#L0{dWY?1FOMSCRj)TK~vh7*px2D=m|t@EZ~(& z8mdPpr)dbKms`0mn5gsLatlC?IH?_O6!AHakB^fkV>H7i_m=mUv=S10WuB>jv#0By z@?YBITWpG%C@s{}pO4!fOmbDycX-G61a11sTR_r?O<#W|ucDipGAb*O?-e=!qz%Ew zCuxOj7jI@mC$Qc1D=PB!3))497ODR8cIRu8{-OfkLUB|kA93}CW67r+RK=cNdJ@^y znZMQ+?EPnDup+p5MRl2#!9KxqDqk549+FmmRdD%+G`??e{CR17zhH4Pjqe}SO{MYG zK`@iX4+tK4eHuS7sDDEme?l;OSsFhmm`mjc2VKXntU6(3a7Zwi$`1|ptXq{*zAh+j zPUD9KtKWQLO8LWsx>Wwe;OJC-L{Oi~j|{F#zp96li$2Wt2-s5B7*LwW*;MaTnYVeyq{yp&Td;Is{w|o4M zeN_Kndi=TIcYFLS@ZWiS2l$^n{$JpK^Y}->gUi|KK@iy=e1DJs7W@#8KY_vy+y6+9 zp9KC?k9UBd;PD~w=XiV;e2vFHNPLA8-&Scgf^UG2AnqCeIrtJQMn82Z|7Y-JkMBn( zvi#uL$?{JpzBhmV)W=kCWKa5&?ehhXn?GOYar5UJJZ}Dci^t8M_jug=`3{epKi}zb z^XGd!ZvOnB$IYJ~_PF`;qaHVZUU5~@KIYG>J#PMdsK?EppX71#=VLv76a4l>kDEWA z>~ZtwMvt36Z}7PJbBo8#pEr5j{JF>D=FfHTH}hNb=OaA+9P!VigPSqlt%g6h!f&%) z`Cq?4TS_&Pl; z_$2rbzM#CF{^p-=Le4DYSUrSMXPqpGSEc=?~af`}Ly7k0+ki zudQ8{P@crfkG0F)9&dpB+0(V6N1^;i@Oh8F_}^T25#{~s-stghyKa+S_b^=dV!N)# z-wFO@@Iz7lQ{a2N^4|hq2Df(k74bB?{jLT#dj`#_r(dsC9)E}U?a`FaoG2Pv`A48n z!}C;*WmE_5r|p{sw|0Ljc**0<;B(;S&zry(z|Ef9!S{H)489D0H0=3m@Ve)#p2q(h zyaD`3l>ZEP*5fyWmpnN?1E2Hw!{7^^oPGCK`|R=f)4-R(kAOZ6;B{xH{>D4O8^F!a zUksl0_%wJ4{7I1iTJSlKUkSbdZteS7@I4;?Huy5Q+4C3Rb!V#n#{UA|0B#xl{uD@g z+|PQv9=rr@e$og&=kYDz3*gr7F9F}<@m=7{;O1|y2d{g9>Tmo5;0@q5W4RVQ3vTUl zD|pG{_khoVn;rfNzTok?1K>ZNoa4clJ^p;~x)-YcCchiJ!Q-RgS#X;{T?k(C_}jqe zJUJf%U-0->!1s7^z6ZYS@%xCU#W#y%buC&ii%T{x9O3alLw>eR%UitN??AQZpvRvM zUIOR&9{;QXpY!-x;0xg9Z|8#V@%Rk*vM1+t;C1b)r(Jg*yaC+ez~{iT9{&z_3Ecef zm*8_A|6lM0a2sD%KSAxY=<(x-@2y>yP~LBsyFK0{@{bOdFurV7GY4mZx4c`&o!5hh z&}Vr-?PKxeeDJ!hN&ZIg7Vvt=xdMF9<6i`y1-J3*Ch&QWFM%(DTYvcz_>#w;Knn^o zuQ^BcJOTO~3*O@KwcxYh$D{ma@I{a3!I#0!e=6X0+w{7|-vr(OZu-9;JnQlQ0WX1@ zeQp7t^Y~rhi{Mt@KZ6I`lXf`xV6{&RxYhUR;IkfI555R~9PD!zc<>^XZ`}Gt1GwpL z{UYn}VaO?Ydu0{|2f-ITejWI-C+EB1 zbuUTU=U(szaP#L!z_T7dj0OU+k0<8@@Hvm40lolkc~KAe9*>WKFN53oyBoajrK-R2 zw}Ur${KMc`k6YeS0=K-*@|HP|e-&~T!L46>A3O+EALI9fw}3wz{_rUHtjC{70|(o4 z(c{km4+d4f$$0^I3-}3;p9P=wxaAv*;O2*xZv=UjZ`|^Y7LQxLG3)Vh(;xg<(C1>) ze@NvUf2Zm1@sFDR9{;+@2ed;CaBP{sb>r=a|^z=IK$ zZ@dM(#p8Y8vmW1R@;!cu$uA`3UuN>bP5-M*zQ?aO`QYX!KQj5FD&P2nCLjE{sPDf_ zzQ>O`9QFY}8Ref19*n7c;~T+Sz)k-g_^ih#O+NUkkbkMk-=Xr2UvBcjt=&Ii@;&|y z;(N=x7Es>LyT0J@A3%QHPSwZq)!%?Oc>EvWS&tt=6ETUO9)A}29JuA58^9Ml-Uq%1 z+{Td+@MVu*0$w+v`dc3OR`3RoUj?28w>Wt%c**0I*Uf=jzHNEkg2#UZIeWm(p7($+ zd;DMEb(5;U)whl=#P)3PxaDf7btR8K8*=8r%|6cuU+{Pid=I$UXB2$dTmko z2j1Xun;&Jt%|13iDtSC0gL6C1fm?eW4!+>=lfn0ZTYH@ezU=X>#P=2l>PuQLKMtJe z@g0y~UrDy(Mc^$Se+T#=xcTizz-K-FHSl?Gv)dljU5tf4kA+ ze}??4E>is|jg+7Ls3P3Hi{R7^<>zSdC6BKG4=z?Y*1m1vEgrXZfLZW*$hUQXd5@oK z`hZ)0ZhpAr@hQ{iWvY+aa~8bba-P0we8mpr}^d=A{~kON=v_$2rqPtK*_%O1ZRyzUjrdVK=C!Qy8i?5ra~?kid;y%&b@@3Ne2>T5!IwS06};}1S}&7R1aAPhb%0lZXFdL2 z@De!vDL+?(&w2ct;0xeZ-#ftfczhXr8QlC~A4)u=f4xfeH~tjx7VvuLa|-yN$2-7h z!7X3f20ric^T8LvtzW+ie97ZifCsNuJ=_>#vr6W?1r3|^!4^5fwuk6Ye8_y#R+{lfD0lE*D?p9QyiS>8VH zamyyUlsr^4JA%v$N%~dp!Q~ ze`n|as$pl3o1N?4tmP@Z$dB2%!Q+=f|3UD2l(+nC*5hX9d2q9X*?H09S3&*~xa9-a zf(LI=J&peeydK>8*FE4Z9{(5dz4_-L%KQE~;&HprRrhw)$NHV!=W6h{-RH`JoBnp6 ztK@OJ&ou{bcC-6j3m!NBTmrW|&HS_e9jd2s%VV?P7Pl>rEqVNKQdr^xxaI99f-iae zOz^sQ>UAv-?*(u0_zv(va9f|b2z=h-?*QKePGL=cJ_5e%@vni`zf1Ko`Fp?zJ#NoA z%z>N!_MF3l$L%?XJ>X_fd(L6m;}77vb(g6=rvKl;8$5ml31s_c!7X2UCV0u?FC@M< zKc7c=-_Jknaf=UozN~s$T($VH>~V|3b=PQl8jj`1;&6k z2W@`0mqUgXE_b1it>?sF}Hn;+VJ zuKMd#Pvdr{CZ?)u=zro~x zRpncJxXt8)p8)%qKhJ|(T)G$Km%#11_FPT<*OK;m#N>lpy$+;?mU!jy6Tp|ip9y`M z!Rx=S^6k3zJj@`t)oYW<2e-^}Pgq5ZvtlR+I1Xt4uz)t*cyX z@~>C{;B{>@4I+kKota4Wyf3O@!2RFOjZ}L6$@d4Icj}_#n8&u`h$qd;ACBOW@|8zX7lRu3p#p zKftr#ln%+yAt$JPN*;d}_&m79^9|rj9`6IM|K3WGYw>&pe9+^UfX{)OK5qqI@c32W zdpv$Ec<}v|a$VE&R`3>&-vd4iZsXBk!52MVM-9RL6YNp>=7-0Fw|M;d;IrTsm%70h zJw9skf1vWM{Dmgp<8L$h;HPOb1|Kr{9{-BT|6x-9@0om$-)Hi{O`pG+e2*Va4K4Qh zQBwYi;4L0M6MPoj7M@+uQzh?4(oYa4h$@lmJ zCLi4N|GUZe_z~ogVxQZS@}CLb;_(-P&w`u$v%wcVexAv{BPsu7Cg0=lGWp;p|6?ZK z-vCmJF`kw^e;_+7SS#Z<8AAHf{6DI#> zN%=1~`5u3_$p<(2A2<0Pzrp1HT;-d8-e&SW{(F-TZvOe0$@lotBv9=0i=_U~0dMhm z8~7}^=|2Fz=EmVdz;q>zfwI{K_5tZ9=GQlg1c0{t#8KkK1}q){_&hLwY<~hXl8ECu^4lkK4N99&oddt@|x| z+}8b8KcMxp`_#9fUPpV});Ai!%?`FMm-V=0BFg)Ia-+ws ze+~X#>t*d?{j210>tD0rw$5h#Yu@9wZng++>rS?Aw&ZbJHw*rt`cRrJKelex;&EFy zD}md60b4hl^SG^>Er6T;wr;k^;|I`y!f|OC+}5|B0$%q=)zf$rc!S3~z_T6?!Asy4 zug(Xb^Y|OV7r;$_^TRzJH$PkkH~ZK+TivqiZ~O|#ZveM?eHuLLaa)fnfm=Vi$@KU5 z67jwIEy`E7{$lV3 z@YUcI@bw;l2Y8FeKM%ec-1PY_c-AYwBs|`iV>7P%5cn+iBjBHV zQGN;KZ5(+7Ja|m)VEiyz0A%~rgPZ+N0B`a58N~UzKYy)W_h!9pa5nhlyOo!r&k*>B zUZ?z6bbvDWbLN!43gur1{;9Vq|GNGP-VZ+UP6fY4`7eMk@<^z8yB7jUhnaK$hj4Ypt}Z>DBpterq64^ z2R(i{_$>J0u-hlV=RN)n@I`R*&mR+K`+WT)TCWZ8|382~!W*8@&mrIkosRwmITlx+ z1|F7LUIed=T8^PZDQ6hd-hE91neFQGOQXp9nj=9em#7p9Eh7 zH$A@!zU1-S!Gl%F_Pw7t>$B}Ms?R~t=P{J8_sSpLq;_iox9dI!d=T91a|ZY<_;ILL z5BQ?T$H14tt>5hiuiH1-AKy-VZ~1Bi%IjG{wBJ#a$FG9?LEgxSeoX&s!Dl`GBk*}} zYu|go7d`$j;%RzbP1j=kk9|(vYL9=A zxa0}&H{Z|i_V}UATK;~I*MmRk@ebif2ZNWNsGf=VuvPdm!G+%ZXb61P;}hWX;3fF^ zZt%MQR{3?{uLG|KUk(0V@RPu;zE^`cc;&wizTV?M1aI;9J>Z)?{x|Tf#}8i1_B${!BDItS(V{7}ok;R$-6fbxSVZ{xxR;IrO!-vB=6@ee}&LEl#S z|4aRre-==F9_0^F9NYlD;PIbB&OYB$IbTJ+9!2>@l(+otp!J9ko}8zFFM}U}>o$O| zeu~=P^yvhz2e*9n#o#R-pC-=n@O4Y7=PA&04&?_?-qssG2tMoa>mcU~zg9W7L(iX~ z{5;BAeIG>mH{PY?mr#D+4Qhu)lsA7nig?<%9K}_XS8K%i!`&XYb=SK3q~F@QYlFv6 zg+5tu%cEMrOCGo9ndiZeL%r;I<|U8Yz8v+(sXiy5yzR>|=y7{4cMjb0c6%;&!Q-~x zz64IoJMv@C+14Mg*EMd>*$#r+`j|auJMVFO&UOi$ZWqdrJ!e~gg332;&)E)w)AFPI z*n0lF$89}-3EcFt=WOdwRQbkjKe0jZ6CvOB6Px$A?I*Se+}hXn6I=GUJ;zx8485+k zuRWJL=<)6U#&73QUZur;`(cmU^XJ)9wd7H#mpy-8^0@6kGY@Wh+Ws?39=H8x>Q7Vo zCqlmMKQriYdrx`ZVlx4u!YYuujq9t5|3ZO?npd))SK*#mC+*#0fc z9=GRX>rU6}9u0fizH<#8pRD1BHm|v^P6s-FUUP%T@2BhVcyWuzZC@dDP=JuUT zInm=buQ}P{Hm_;)xXo)ec--bSEgrXd%_fiAyr#$FHm@1*xXo+M^|+0{S@@fJV;skF z9)FpPn@0!p$TJ^<-@Xas>5e9qVC$&1o-hk;dE-Y=ehwUIX7IHder|fsqr9)@hdplZ zQ?Fi|)W_bZt_MF7`rG@|EgrY`sRzMLe|w+0iW3VO78g7<(wx}<{t2Kfu%SD&kbcR|ke;CJ1v!0PpT@P|ICd>G|dlEbilj(AAr z*!_i*!4Jhc#@5w(;dbFr!uhoo<@fo43Vt=}dja?>hZX!B=e!C0w%uBO3i^Bq{JGn- z{BKeIi{SsrXu$>W?}Fb72VW04zXNaFPl4U9S#_q?_eDQeeg)(-2*)4JuT3cb>J_Tb z;gCO4qx|j~{4J34v zpMmmE+OBf`fkR#3uee7ATnWA#{Fn7w-t2QZ_`yF={taCBYVeo+Re`n3cfr4Zz4B+E z{2#y{gMcT1AJmfGj!z@b?f5^x(}Gt*&&|T|hx02Geo*k*A8C2>|JM*t^Q(6gKa5t{ zXT5cy4?)iMJGH*2>aXBy;IF?=!7%jy75JUs4CLnq{T18`zVmNd@KMCQLt3?7KY2_G zTKk>^{#H)#=*RTm0-nEF%ipEHf|r7Ce2VI0@&CWT4}c=JzVu%3hrgxe|FS{_eGdHd zyOiGr`QHKm+8zZT2mclLTfV2@*WiB!-~P%KLMr+z*mtAW_fU)@=b-$t#M9d4G?X9e zQ~{qy`3(5{r<9xB#=#FfN4d#=v&cU>xbnTpar2!he|3kJ{}SYX3jDMmDR`0|4sI2W zLe8(>p?vNtEtuIy5B>{$g0dR^IT~`F)uwh>@g@bW;Aes#TBqf|tzv@nz{h@|z~bsF zz(4+T6<~hyaq!#j((=ECoSTH>59im*bs4U2_0NgI4+=i{4?Sl7)gtHDE~O>dl~qpco1nBDtrk11ve=;0p-6c9EF@;KSlX1KU8=(j{On*y9+A7^gpCS z^?dWATG04&!Qb?Cud7g4E~-*)yML~FN5!d zBkVwZZv`LTqrl?-AB00W=htDIv|e{%9(OQ~odCY;X-WUdg5Ui)m2cx+XG^+2>_qt! zctL=EtX{7HfBr*)1B9pGUErs!Qb#y;Cr|lpkEFax9MBS)=^lQ2vQeQUg2<@(=0MdL8sc<>;=#(cnk#quknS9r(~D6=3nC z2mDj_sy=473F18Nk9gz$#VG#(5_4r+60)uYzib~Z|84MZfC2Q`KN3tPkt+S>GxW%2T-r; zz^{6-@>P)YeefIW6qtYh0sQk@m0P}aXt&nu5?s*Ap9Frex6aTH{?Wtqy5^skfdAz| zm2ds!NS1wZXrjSqi>zdZ~5z%QvBvs;EZ zkK;SMb&stm-}4Nu*FMMT1uEc&c>VFi;6vAGIfTpLi^8Ft^XnFrKl~h(|72AwxC4CZ znF{X4Igc7YP`S0s(|gqp|Le{7dyTJ1mOo#(qX_=_8kD~V^Ha-Dt^og5kIMNB%6|_0 zl3yw}KmQr{$;(iNw#YWXwtS5ObWf0qKw^UnZ({sYR*e+uAl!Gv-N`n(1F zXI}aL0dK_v*WX3?d%?&5tnx9;1gp+gJ&(u4{dnm4MDRELDXGs3!ABOfzLw9N1Ag+u z$?~rxUJo7IUvESCpCbP^J+A?O{#KQL2TuAn_%9Im7QlZFzVTwc?wR^4_=9lwiu&(; z`n0~ESx)NtT<}v`lv}&EgP;6;<<{;yz%T!->iKJ`GPulr7v?>I%}Jd7gOgSQrx zTYhpo_^*GFyzaliKl5qj)-Rsguk||QQsoHW!5Z+JF){5?b%S>BFW#AyUjo1D_p0D_ z4J*Oxz*h&!Jmf0yGtO4QrvEMA7ayYKTX5Z9gI_+S;EUjY1HWZL0n8m7p3{2mn^%4b z${!E@lS@?pwfZY)1RuRif%T)8g1`9<%57co<=`VPRDKKEtFrzOyLIwt6rn>Ek3k>Z+x@zzvI+%z~6qma`VHB!G8h2oz}y_+rdAF z1!l_wuLkdVNDG>MZU=wldcELbxbD5+&snAQwfuACR&AHR{8S5CzdHi_uotWRcIfjQ z@caQP=egi#gSUsue~ar53&$VMuh*db*}qZ&W`_mvpEf98!Z|+$fBx%~_vvMV2f>f_ z*4d7t#2~G`P8NPpkbg|&SbowAo<#zM>IH+~AO4M&H#@%>{CXJX`Fc3`0Qix6RPm2P z|1W{R5(6WqZNY8e5C2*V;#cqxc+*!@FkTA@_S=@e?sOD!UT>Tn)AD`$tGrXeGhX~T z2mG$Xwfx`r)$-He{ZCK>TmJb0;&qg7EMolx)e1g}@<$+XvwpNF<*iiw`}-)rqFJw7 zK0uHC6Z~3?r)_#TIBL7*IbZhHZB8W4_5HRtPW6I6?Rbq>C*#uF!4H3t*6T9J-wFQS zOO-!wKRw~q;DtN&?{~r9J^=ppDHZSq$oVXI-?J20{&PF{({EL7e*Osf!{1HvV_&58 zI`S;#W`{N4ANsm-i_cl`PoJoK3H99#e(@p6cKj&#nxoV{8}zi`OW>a=Yx%cB&TZfq zd{_%!1Uozge#~jf>ptzps(+m~E_Z^z^gbLEfUmz?FZe0+ms`N^J1E({kAVN}v}C=Ge~H%X z{y(XlFRaqv>%hNxwU+-m>eUPWJ0u#r^l&f=UWfZEmhZg-{7ER7L-~&hhfwF&S5f|5 z{9p$CSRDQ__%P;`m*Ske!SDT(mhXoAeO{{d`tpA(*rJDnClas2x$duel%F3@UUxnC z`F~V7=AT30t@|st_PV%+{5PWfZC}x=+C2RV@ZWpm^-bVAKA`f=pYN+7|L-XO3vWI7 z8KK%?{aaPe$583#f!ANJ;3wc`fnU2*?O^o^!7m<7+UG)(!HwXbJy`2yn)$;|i1Rwb zAl4ZWHi8FH{tZhjy%DDz|uYmvBcB{PVBG)8hZ?g4V0;Zk1zx+XQ~)0`^*XpfWkf!1Ca|ZZ(KB3&~vmN}Ip>p&8 zN$|oymH$J31#bfX{Sn$w&jf!T_$fE&^`4CDegeGZwOa7+;9n=6=2t&J`R#vDJ6Qkv zn{b@${5p7t);Byr<(nOz4SoeKXuJ!&ZiVV^{!j-0+HET53S8se;P1LX^)Wf0Ad7>tmRMC!@*0z zGlwd-@$?PC9fRSYSD^e%uL}4)_~*e_&MU_TC39~>t0+yjC~UY|VpYRFke;%v`dychgSH>+T? z=U2chhpV0zKkoy7|Akt9E%bR5d>RR+m4DVwt#1|%W%X?VKkot+&;&VI@Vu9IT?k&k zQvK89TmgQ}ohrcm^Cs}yzoB+`9nA4V;Yr)@Uw=aRgS)l95ErbTQ2p1Qr1I~^si%N{ z91U*u-3DI7LhnaWz65^dH3|*}e$7`N_L3w<-5Dw=jCWi9EJ8MWN7xE+F@KBATtSDrHmAYX2xnCu2e|H=?iNY7%oojs(rLlDD13#ba*Hn$xr8N*oWG$ zSPqBC{R-t8MLIgWGBpmBik0cn@xsIiIcBPB2;4C}%?0;Xq%uK{UZX(UKwnq?X7}J& zVS0ROv?$J3?Lh8P-!+J0b!&BEbZD^naE@^IH zl2WvJ&H7-nFrDZ2qu$q>&Ghu=THCgeGP&0F)?8~yrb)xO&hAX_KrZNR-4>V4^=_f_ zy=$*)=5Ud6?R9I{2Ip0ZQz4lwgps4asq2Rm924wFvO??Dudg9={RRsfP89wGM0u zcH}ENrt{=zq=8tB{xemj4!Hnd)u~x?Lk*MA0oEf?w9#{^=7z@fO3;A~{>i7WP1U}+ z;dHi8;yO*~6>V;4I=!lBEd`pY6sX~X%?-^}6sI+!n5Mu6-%}cDj8zoCs$6wVjrM3*AKPZuL&@)R<{-nqgA?|UQ1W;PR9Z?u%GhNUdr$RaL9GR<&d(PAJe^s|jQ%P86*bsvD|8bwjOC-B1;(8)zSzv|iBMP!*~h zBnGB6fegh7T_pyl74@@o8iQ*ou)(+M8nJYW)HPK}&l>5&X+_0~uC8mO52qCsD<+H9 z8d$}OiJ~=Pe)F2DnBQ0{<~LTw{KkeF#%ip}ZyVRtaKc7Od|h1|*VJ&r#;U}(v9U(q zYHal5K;!8(`c`9AZqq2ELrN1gp6>fVW34o(u__H}6t75=+U$pn##$LuV^zk~SYy~| zZuEzZ=EfR{2OaRk+v$>Er|5e6T8z-Nj51!Fs+wnQs4=uRZJ?pu8}Ouws;@ys*{d9n zpy4{*dFX(rIgxbdp>f=^G7&yGO##UT5(-EYr4^N2AW@VolrB|TDp{1>DNU*j@QI@9 zS^Ko2GHNG_rbh+J1gq>SF)dB%2DYLjl?K*yV__&x6kU_90EXg3QEBRG1!#Qrds>4u zby`sxUuCMz>lMYJ^9sW$Ik$LU^~vOG>{*UVaH+JSGL9#T))=nIvugQ3jUj{b!CD?( zYkgub(+JOWm@TAL$sZ{(TSzNfJyO=1MK;hZQW~TXr zNkmVvXp_tbTqka-kw!H&k#ofA@1zy!;^ZVPEGK#kxq5nBBJ+Disr#oFrOsYew1)L5 zJ@bp!2sTYLx%Z3KNbj4P{q(-6xkj=;2dZ?HoFK*2%`y_Xrf!z0WLi;4<`YG0#e-%) zXK!k*HQ+Q?4LHrUCXUTj6UXLS6UXMNiDPrEv8lOgY-+AGacr)dIO?6PG-FkH*4i2w zEA=gQIoGahWqz0YiYj?n(}ftHMF^xX{` zJX_FZ(u>joq>h#GQe}K>DnCJr``4yYwUP(FfheBvwbN=Ua}B30G1pHm>ID?c_0x*V zTq7Y>hNrZm5g~BGtytHp7WHRo_6pr2O>1=Lg4ANQ21;2{c8b!HMXeU| zgI(H90)NemYUBmpv^Dq|A%j-StIV<98`x8p$=CS(ym?L4I?x*F&ThTe)abR%Yy4i@ z{QpY3b{I*{yxtKU0v!edETKay>m-nX(aiMp>}+OxZ~Z`P`(%7Vkd$k>Yi8Qp-PP{u z9`6#M69F;@))}2-p#$j%kBsypHYd4&5JUii5RyZP0Q395zVH7%YIh|@+O6)Y@A-ef zzOIv;IdLcB?AZxBDaizdyLiH4J!z8%0%s?=*v6e=t|C=Y3uR|^7JU8kPCoJ}ez-po znX{TNU-T;Ab6UXXw17{|oi0W$Aa+_n?6iQ-X%Q_t%_HWs(_H1uP7C;)7D1nB0iS6R z>zEeuXIfCybWC ztfe|!#n{ zisTCz3Ch5u%CQBkjBKlj?2A8E+g4G|FdLJ&v)t96&Bi3|tdO{~F^M}XB<^fX;?4?* zI~$X@vqIv|#w6~nkhteXRQ`NSGMpEZ;qF+h|1L%qyLjJH0pU%%xu*J?g#I))S|SP$ zG)yC9PZf74H-o{5Bg}L>64T~%oviW6u6Ty%{NN{-M#h?l1vq}nW+{gZ9iV9bNwua4@TCrd=t?YdoVeE-^WnQ`B(x3(Ca z?4xwPtOX6d(8lYM<p>vUv=%~ptp?mr~tD; zh4i-H49XBT@r&$hvVuo~jvRPstt(my~uQ4oEz@V6?HO55koiuh(dbb|+E!#Uuc zj}=|vLD6)?{yL{fkwBFRN!KS~#Abuz1^JvK8P@Y-WLPT$@euC*aM*6C)V^ChV-7YH zO2D5V=aF*6e(UBk9%NA`+^K7LJWx@^EZA-4eN;uo_D5U^#G0Qmh$zVnc_Y+yer(j` zmt%ATY*gSP)t{a;PI9~c{A9#s#e>YOMlf8neZ*->%B+)vs;37v^_vk(5o5#9nN~YL z8KcaTJZgfbn!Z|ej3ZIC`J5x}?)gdX?&40}C%}Wu)j2-_5B8?3vmS4WzBB+Yj(Hye?Ntuq-OsPNz^ zn)NlMhWRYTYu|jku`>*OL?hyJe39Mo$_(oYLLy8~6!mkK|f>x&TCb@Ol?&mkztxC_!TGvY7p+D^AU9&W0YhRno_EAu`-HtLiD3jf` zdsXvL?fI>F3;X)2mzCp7*5fdAyC`{qGv9=t0+3TI2Y{b9@Jit?_|B693291Tk}8xYUXK4Sf&YDFiz?U!?&3XBfv!V7@AeJz3gGC{fHM0z@D2}8Zh*~J{`R`cMkM!LO)Up#D@58kWJ^*h+2kTbi-&0$V@YWry4EV|XcC2o|)UUM&& z!=naL0qT5r-SK(Nb^Ey9cD_Au)5FkScO*$-v)gXByn52y&?!O22^lX^xz)6bMMGbg z8^~a+O&VmBL9JFS*65S2Q;rN=Lo(qI-EIjN+ztoK`vdeqXz5L*kcgH+*tvoRTtI|z za$Ih!IHs6_P8QScO#lkco_70zB-I+Rg8)Esdz%|%+%?}WBjh`xLw`|NSZ^%bv=l9E zDLyfc`0A@~K79N2`_1Xm>5uq|Ix2lItJmug7E_;gyh%93S z#-oGrq`Cq<^1_<}kQ-NF>sK71 zi~9{q_}RqQ&hzbQ1c@C^OAb(7pInRf@M z?EsnD`o*w;G2j-@l3hs>!^narFq$^Ya=rlKXg3;|p>0;(rUFPW6~GKIo%?+Q2I8e& z9X4#4fb4fY)Gg!M+jW_Yk_Lf^Ev{&B8F@^W5Vj&;61l=8&n~U#xcSYa=Biz;;<7d1 z!xU0bTcCdk(shp%B~@)kkmoz))D8j@6 zz&{XDOz&-saMm`noCzpVr09aYc2gNmvVbQr3Bl<**h;{5pV?)+ZuwmjJO%7z*cu(~ z`#lg|@lhgxJ&*Fp9$w4!;FJqfFdxy@8Oe7H78JSh=0^lgMf+-UR_BV2`%H!XDa?gkXQQXlr<) zwP)5gw9h9nX~bG@8U&CyE|qXckd7Maf{({U3hVwFRDoa1siC02C3wQLpjS}&W>c1c z$hhnW=q;Io5f1x!-(B}R{#_%v@39iZ6;MYCj&*#Hke;IEFnj0$%pSVH$igK`g|Kfj zGNSYek?|Ul3o8(jC-Syv3ay8A&fDFBIU;H7H^bvOlfXao8!e4xFHad=(dZ^dNQm(H zKs15Rga;@!>Qoa&mc40$usY%n(B}qNtH8FpA;)_@K*2mOa?t8kS zOhe)*lcvFX^^>7Lsc~;5<_v`3=dgqq3*Fl9=Pd<%h`M>#Ob8Um;$XTW&iVNJ!)AkE zuJQ$Ui7-(+PK?kvrst@gj}g>}J_f21lCL#YyX8d!T9ICiPOreTng40pjO|^~IguWN z#p{6`NLZGVcyIzq3-9a>8aU*bpujb%wRykWb)XJ}r2-}(*eHs&?eJ4x0nQ*%wshSh zpaj!wH4VajcDhVBUO9BC;=F*Es2hN4%v9Eae$en|nT;xHu2 zJ!d5j>vbzWn3L)ymabA0x#Sp&No-ypP)}jj$>0)D}VTXP!wlFiBpvXhOpBMaD4TG|lx|!h0~n zpAiwSc|CIw`;xp>ki1yuIr*qKR{I3Rx9^>s5E#e9CcvDw%i&|MvOGPGDG>AL0A$qb zM9ik)H-H`94XF?x8^zBr2wj!r5<`tb)^T8ET{=D%>M_v!}YY?&CE2n*~Bq7kG3=^8cb13DY90lpawN-^ zC@zW?hv1MKTcX0~*)4|tYQNll92+_KRKU}M5z9P$lWKxNJ zhj&lCHGS{rBc9ITNp(siu2KaABtyBBs)*4z&bwV9IACF>0Y~?dY1-Aqx$85i6WuhI zu+7u<*{ z6!X=2$r?Q}ef?3t)KUwb0!AWdXdw|4ePc3_w3-DjSnPR3uQhLD^XQ=n?*y}L_MADm zO)`mN^V~XinM~d)xI%`nAw@l{3?mFcq$}r-wg61XhTup8j4G4j3aMe?15SDq@$My8 zLp&RPPF$qIrC4gBF%wreaRGqoBwBMtUVepRb1&cWJ+wn-MiLyv%564R!{Ks(6Usln z=C_9jvBw2nfv}^{v zN1>$z0-PkGFA)6B3TnjW7-TfBGJ-aXo?A+CBt*0v(VCcC z$V|{lsB$e9Gk`zwJlz|e0CFHZWB~*g_TppMQ3Z% zJysY=TBo8GoN^~N5bvL82QoVyR^+CWu<2-Co_oJ7kN9yl446)R%o(fu4mc;x)Eg!u~v^koJj(uxKijz6=Jmpk;lpEm{gfifPqxDCS+n^ zo*p!itR@do04}kj5;m_u2bzk0ojVvXj^uakVu?C=7$xOGfr$a-5$p%9vRKj z{$Wuf5LIDiFaJ=WFf3f{uzwbcVAAcS#8lzO0tglw!E% zr-=-s2ac<_X5Fk0iNFynSMaV@DpN5|3jpSk2NYz_s+#mx0Wc_WUbYGbIX69jOox5s z71zx1H@;v%SnU;&vnfLR3q@>%Z`*(1SLg0{CS z722iOz3B3kX$@p!0O#qMU8>NY=VZ&EV_yDPh~-o@;ph9^vx_8mWLJt^#xN@ z0;7)QZk2$KRF^sn;hZe(RGgx_{9kP&lQPByPhjuk2<(k{2nx%5}br@(p6r8 zCOz!)M{_izt~4P=;l-h#bdCk}dzwogPmU02nH*7=k>t>k#53C+{S*oATX+-a@a)m$ z<*vK6Z}>UC&OQTF_O7`9$Msjx_owks-kvv^=<5W-_3PQ#_FhQ4eS{DH_2rlLTGxrb z-aTBmKPN5at#^~jSFEF6FL3=?{ImH#{Hi>#>&0)#cbb>lSpS#h_T*JuX>9BN7x(0W zUB7cI-|c^WU7>xQyoFC+y7j+;_aV}sUB9mjxAogRcKsIG{;%u*bj{r-cKxP&ozVJe zZdQo z+jn|Dq@VwD?Qho~{i$I}dgSAMmi2%5ia^?~KVuW=HQ)dDw7<>rq4u}y)?N^<{qN}2 zE`Ny+w0`UVXua6bRvWc{~qNejEK0%6FEhJX1snb3PuiRA))efD*J^p($K zoV1f&idcc{&%$hr+DuF?)WeG{7?8t zv~TnOQ2YN-`#+_{;m_c1`!}@w5uW+=-+Wi#ck^B8@onnt{;a=U{|(Q4|H|Bhbm@)LZZv8>r&>-&Fy@O|lj#~x^hJ9@Rt z|KJ0y|JZKm)%wq}`wC9(Lyzcn>UPgJ{Rf@@C4En-Jb@Sf4lwyo@FTF z*5CV%P{@62sAFVb8TKZ7+%%J4|8uemCX-M9Ozysd%S!;M(VgiZl8?3d|MqLr@?V~_ G|NjHzg;9$D literal 0 HcmV?d00001 diff --git a/vnfs/VESreporting_vFW5.0/Makefile b/vnfs/VESreporting_vFW5.0/Makefile index 8fa70237..c9776e0e 100644 --- a/vnfs/VESreporting_vFW5.0/Makefile +++ b/vnfs/VESreporting_vFW5.0/Makefile @@ -16,6 +16,7 @@ ############################################################################# CC=gcc + ARCH=$(shell getconf LONG_BIT) CODE_ROOT=$(CURDIR)/../.. LIBS_DIR=$(CODE_ROOT)/libs/x86_$(ARCH) diff --git a/vnfs/VESreporting_vFW5.0/README.md b/vnfs/VESreporting_vFW5.0/README.md index 598b207d..e9bbc2d6 100644 --- a/vnfs/VESreporting_vFW5.0/README.md +++ b/vnfs/VESreporting_vFW5.0/README.md @@ -1,6 +1,5 @@ PROJECT DESCRIPTION - --- This project contains the source code and scripts for the periodic generation of network measurement reports. The folder contains: @@ -22,5 +21,6 @@ To run the vpp_measurement_reporter, please execute the following steps: - Make the go-client.sh script executable chmod +x go-client.sh - - Run the go-client.sh script + - Run one of the scripts based on one collector or 2 collector ./go-client.sh + ./go-client_2_collector.sh diff --git a/vnfs/VESreporting_vFW5.0/go-client.sh b/vnfs/VESreporting_vFW5.0/go-client.sh index 3b4b49d3..3d1b159a 100755 --- a/vnfs/VESreporting_vFW5.0/go-client.sh +++ b/vnfs/VESreporting_vFW5.0/go-client.sh @@ -1,7 +1,6 @@ #!/bin/bash -export LD_LIBRARY_PATH="/opt/VES/evel/evel-library/libs/x86_64/" +export LD_LIBRARY_PATH="/opt/VES/libs/x86_64/" DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) ./vpp_measurement_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT eth1 -#./vpp_measurement_reporter 127.0.0.1 30000 127.0.0.1 31000 eth1 diff --git a/vnfs/VESreporting_vFW5.0/go-client_2_collector.sh b/vnfs/VESreporting_vFW5.0/go-client_2_collector.sh new file mode 100755 index 00000000..3fe872ea --- /dev/null +++ b/vnfs/VESreporting_vFW5.0/go-client_2_collector.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/libs/x86_64/" +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +DCAE_COLLECTOR_IP2=$(cat /opt/config/dcae_collector_ip2.txt) +DCAE_COLLECTOR_PORT2=$(cat /opt/config/dcae_collector_port2.txt) +./vpp_measurement_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT $DCAE_COLLECTOR_IP2 $DCAE_COLLECTOR_PORT2 eth1 diff --git a/vnfs/VESreporting_vFW5.0/vpp_measurement_reporter.c b/vnfs/VESreporting_vFW5.0/vpp_measurement_reporter.c index 7cf0414d..371f3d9f 100644 --- a/vnfs/VESreporting_vFW5.0/vpp_measurement_reporter.c +++ b/vnfs/VESreporting_vFW5.0/vpp_measurement_reporter.c @@ -1,7 +1,7 @@ /*************************************************************************//** * - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2019 AT&T Intellectual Property. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,128 +36,13 @@ typedef struct dummy_vpp_metrics_struct { void read_vpp_metrics(vpp_metrics_struct *, char *); -unsigned long long epoch_start = 0; - -#ifdef DOCKER -int measure_traffic() -{ - - EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; - FILE *fp; - int status; - char count[10]; - time_t rawtime; - struct tm * timeinfo; - char period [21]; - char cmd [100]; - int concurrent_sessions = 0; - int configured_entities = 0; - double mean_request_latency = 0; - double measurement_interval = 1; - double memory_configured = 0; - double memory_used = 0; - int request_rate=0; - char secs [3]; - int sec; - double loadavg; - - printf("Checking app traffic\n"); - time (&rawtime); - timeinfo = localtime (&rawtime); - strftime(period,21,"%d/%b/%Y:%H:%M:",timeinfo); - strftime(secs,3,"%S",timeinfo); - sec = atoi(secs); - if (sec == 0) sec = 59; - sprintf(secs, "%02d", sec); - strncat(period, secs, 21); - // ....x....1....x....2. - // 15/Oct/2016:17:51:19 - strcpy(cmd, "sudo docker logs vHello | grep -c "); - strncat(cmd, period, 100); - - fp = popen(cmd, "r"); - if (fp == NULL) { - EVEL_ERROR("popen failed to execute command"); - } - - if (fgets(count, 10, fp) != NULL) { - request_rate = atoi(count); - printf("Reporting request rate for second: %s as %d\n", period, request_rate); - - } - else { - EVEL_ERROR("New Measurement failed"); - } - printf("Processed measurement\n"); - - status = pclose(fp); - if (status == -1) { - EVEL_ERROR("pclose returned an error"); - } - return request_rate; -} - -#endif - - - -/**************************************************************************//** - * tap live cpu stats - *****************************************************************************/ -void evel_get_cpu_stats(EVENT_MEASUREMENT * measurement) -{ - FILE *fp; - char path[1024]; - double usage=0.0; - double idle; - double intrpt; - double nice; - double softirq; - double steal; - double sys; - double user; - double wait; - MEASUREMENT_CPU_USE *cpu_use = NULL; - - /* Open the command for reading. */ - //fp = popen("/bin/ls /etc/", "r"); - fp = popen("/usr/bin/top -bn 2 -d 0.01 | grep '^%Cpu' | tail -n 1 ", "r"); - if (fp == NULL) { - printf("Failed to run command\n" ); - exit(1); - } - - /* Read the output a line at a time - output it. */ - while (fgets(path, sizeof(path)-1, fp) != NULL) { - printf("%s", path+10); - sscanf(path+10," %lf us, %lf sy, %lf ni, %lf id, %lf wa, %lf hi, %lf si, %lf st", - &user,&sys,&nice,&idle,&wait,&intrpt,&softirq,&steal); - } - - /* close */ - pclose(fp); - - cpu_use = evel_measurement_new_cpu_use_add(measurement, "cpu1", usage); - if( cpu_use != NULL ){ - evel_measurement_cpu_use_idle_set(cpu_use,idle); - //evel_measurement_cpu_use_interrupt_set(cpu_use,intrpt); - //evel_measurement_cpu_use_nice_set(cpu_use,nice); - //evel_measurement_cpu_use_softirq_set(cpu_use,softirq); - //evel_measurement_cpu_use_steal_set(cpu_use,steal); - evel_measurement_cpu_use_system_set(cpu_use,sys); - evel_measurement_cpu_use_usageuser_set(cpu_use,user); - //evel_measurement_cpu_use_wait_set(cpu_use,wait); - //evel_measurement_cpu_use_add(measurement, "cpu2", usage,idle,intrpt,nice,softirq,steal,sys,user,wait); - } -} - - - int main(int argc, char** argv) { EVEL_ERR_CODES evel_rc = EVEL_SUCCESS; EVENT_MEASUREMENT* vpp_m = NULL; EVENT_HEADER* vpp_m_header = NULL; + EVENT_HEADER* batch_header = NULL; + MEASUREMENT_VNIC_PERFORMANCE * vnic_performance = NULL; int bytes_in_this_round; int bytes_out_this_round; int packets_in_this_round; @@ -165,64 +50,70 @@ int main(int argc, char** argv) vpp_metrics_struct* last_vpp_metrics = malloc(sizeof(vpp_metrics_struct)); vpp_metrics_struct* curr_vpp_metrics = malloc(sizeof(vpp_metrics_struct)); struct timeval time_val; - //time_t start_epoch; - //time_t last_epoch; + time_t start_epoch; + time_t last_epoch; char hostname[BUFSIZE]; - char* fqdn = argv[1]; - int port = atoi(argv[2]); - char* vnic = argv[3]; + char eventName[BUFSIZE]; + char eventId[BUFSIZE]; char* fqdn2 = NULL; int port2 = 0; + char * vnic = NULL; + memset(eventName, 0, BUFSIZE); + memset(eventId, 0, BUFSIZE); + memset(hostname, 0, BUFSIZE); - MEASUREMENT_VNIC_PERFORMANCE * vnic_performance = NULL; - //struct timeval tv_start; + strcpy(eventName, "measurement_vFirewall-Att-Linkdownerr"); + strcpy(eventId, "mvfs00000001"); + char* fqdn = argv[1]; + int port = atoi(argv[2]); if(argc == 6) { fqdn2 = argv[3]; port2 = atoi(argv[4]); vnic = argv[5]; } + else + vnic = argv[3]; printf("\nVector Packet Processing (VPP) measurement collection\n"); fflush(stdout); - if (!((argc == 6) || (argc == 4))) + if (!((argc == 4) || (argc == 6))) { fprintf(stderr, "Usage: %s | | \n", argv[0]); fprintf(stderr, "OR\n"); - fprintf(stderr, "Usage: %s | \n", argv[0]); + fprintf(stderr, "Usage: %s | \n", argv[0]); exit(-1); } - srand(time(NULL)); - /**************************************************************************/ /* Initialize */ /**************************************************************************/ if(evel_initialize(fqdn, /* FQDN */ - port, /* Port */ - fqdn2, /* Backup FQDN */ - port2, /* Backup port */ - NULL, /* optional path */ - NULL, /* optional topic */ - 100, /* Ring Buffer size */ - 0, /* HTTPS? */ - NULL, /* cert file */ - NULL, /* key file */ - NULL, /* ca info */ - NULL, /* ca file */ - 0, /* verify peer */ - 0, /* verify host */ + port, /* Port */ + fqdn2, /* Backup FQDN */ + port2, /* Backup port */ + NULL, /* optional path */ + NULL, /* optional topic */ + 100, /* Ring Buffer size */ + 0, /* HTTPS? */ + NULL, /* cert file */ + NULL, /* key file */ + NULL, /* ca info */ + NULL, /* ca file */ + 0, /* verify peer */ + 0, /* verify host */ "sample1", /* Username */ "sample1", /* Password */ "sample1", /* Username2 */ "sample1", /* Password2 */ - NULL, /* Source ip */ - NULL, /* Backup Source IP */ - EVEL_SOURCE_VIRTUAL_MACHINE, /* Source type */ - "vFirewall", /* Role */ - 1)) /* Verbosity */ + NULL, /* Source ip */ + NULL, /* Source ip2 */ + EVEL_SOURCE_VIRTUAL_MACHINE, /* Source type */ + "vFirewall", /* Role */ + 1)) /* Verbosity */ + { fprintf(stderr, "\nFailed to initialize the EVEL library!!!\n"); exit(-1); @@ -236,7 +127,7 @@ int main(int argc, char** argv) memset(last_vpp_metrics, 0, sizeof(vpp_metrics_struct)); read_vpp_metrics(last_vpp_metrics, vnic); gettimeofday(&time_val, NULL); - epoch_start = time_val.tv_sec * 1000000 + time_val.tv_usec; + start_epoch = time_val.tv_sec * 1000000 + time_val.tv_usec; sleep(READ_INTERVAL); /***************************************************************************/ @@ -271,45 +162,33 @@ int main(int argc, char** argv) packets_out_this_round = 0; } - vpp_m = evel_new_measurement(READ_INTERVAL,"vFirewallBroadcastPackets","TrafficStats_1.2.3.4"); - vnic_performance = (MEASUREMENT_VNIC_PERFORMANCE *)evel_measurement_new_vnic_performance("eth0", "true"); - evel_meas_vnic_performance_add(vpp_m, vnic_performance); + vpp_m = evel_new_measurement(READ_INTERVAL, eventName, eventId); if(vpp_m != NULL) { printf("New measurement report created...\n"); - - evel_measurement_type_set(vpp_m, "HTTP request rate"); - evel_measurement_request_rate_set(vpp_m, rand()%10000); - + vnic_performance = (MEASUREMENT_VNIC_PERFORMANCE *)evel_measurement_new_vnic_performance(vnic, "true"); + evel_meas_vnic_performance_add(vpp_m, vnic_performance); evel_vnic_performance_rx_total_pkt_delta_set(vnic_performance, packets_in_this_round); evel_vnic_performance_tx_total_pkt_delta_set(vnic_performance, packets_out_this_round); evel_vnic_performance_rx_octets_delta_set(vnic_performance, bytes_in_this_round); evel_vnic_performance_tx_octets_delta_set(vnic_performance, bytes_out_this_round); - evel_get_cpu_stats(vpp_m); /***************************************************************************/ /* Set parameters in the MEASUREMENT header packet */ /***************************************************************************/ - struct timeval tv_now; - gettimeofday(&tv_now, NULL); - unsigned long long epoch_now = tv_now.tv_usec + 1000000 * tv_now.tv_sec; - - //last_epoch = start_epoch + READ_INTERVAL * 1000000; + last_epoch = start_epoch + READ_INTERVAL * 1000000; vpp_m_header = (EVENT_HEADER *)vpp_m; - //vpp_m_header->start_epoch_microsec = start_epoch; - //vpp_m_header->last_epoch_microsec = last_epoch; - evel_start_epoch_set(&vpp_m->header, epoch_start); - evel_last_epoch_set(&vpp_m->header, epoch_now); - epoch_start = epoch_now; - - evel_nfcnamingcode_set(&vpp_m->header, "vVNF"); - evel_nfnamingcode_set(&vpp_m->header, "vVNF"); - //strcpy(vpp_m_header->reporting_entity_id.value, "No UUID available"); - //strcpy(vpp_m_header->reporting_entity_name, hostname); - evel_reporting_entity_name_set(&vpp_m->header, "fwll"); - evel_reporting_entity_id_set(&vpp_m->header, "No UUID available"); - evel_rc = evel_post_event(vpp_m_header); + vpp_m_header->start_epoch_microsec = start_epoch; + vpp_m_header->last_epoch_microsec = last_epoch; + evel_reporting_entity_id_set(vpp_m_header, "No UUID available"); +printf("1111\n"); + evel_reporting_entity_name_set(vpp_m_header, hostname); +printf("1111\n"); + // evel_rc = evel_post_event(vpp_m_header); + batch_header = evel_new_batch("batch_event_name", "bevent_id"); + evel_batch_add_event(batch_header, vpp_m_header); + evel_rc = evel_post_event(batch_header); if(evel_rc == EVEL_SUCCESS) { printf("Measurement report correctly sent to the collector!\n"); @@ -326,8 +205,8 @@ int main(int argc, char** argv) last_vpp_metrics->bytes_out = curr_vpp_metrics->bytes_out; last_vpp_metrics->packets_in = curr_vpp_metrics->packets_in; last_vpp_metrics->packets_out = curr_vpp_metrics->packets_out; - //gettimeofday(&time_val, NULL); - //start_epoch = time_val.tv_sec * 1000000 + time_val.tv_usec; + gettimeofday(&time_val, NULL); + start_epoch = time_val.tv_sec * 1000000 + time_val.tv_usec; sleep(READ_INTERVAL); } diff --git a/vnfs/VESreporting_vLB5.0/Makefile b/vnfs/VESreporting_vLB5.0/Makefile index f5a4da9e..a9eb05e1 100644 --- a/vnfs/VESreporting_vLB5.0/Makefile +++ b/vnfs/VESreporting_vLB5.0/Makefile @@ -1,4 +1,3 @@ - ############################################################################# # # Copyright © 2017 AT&T Intellectual Property. All rights reserved. @@ -17,6 +16,7 @@ ############################################################################# CC=gcc + ARCH=$(shell getconf LONG_BIT) CODE_ROOT=$(CURDIR)/../.. LIBS_DIR=$(CODE_ROOT)/libs/x86_$(ARCH) @@ -37,4 +37,10 @@ vpp_measurement_reporter: vpp_measurement_reporter.c $(CC) $(CPPFLAGS) $(CFLAGS) -o vpp_measurement_reporter \ -L $(LIBS_DIR) \ -I $(INCLUDE_DIR) \ - vpp_measurement_reporter.c -lm -lpthread -level -lcurl + vpp_measurement_reporter.c \ + -lpthread \ + -level \ + -lm \ + -lcurl + + diff --git a/vnfs/VESreporting_vLB5.0/README.md b/vnfs/VESreporting_vLB5.0/README.md index 598b207d..1b3dd481 100644 --- a/vnfs/VESreporting_vLB5.0/README.md +++ b/vnfs/VESreporting_vLB5.0/README.md @@ -22,5 +22,6 @@ To run the vpp_measurement_reporter, please execute the following steps: - Make the go-client.sh script executable chmod +x go-client.sh - - Run the go-client.sh script + - Run one of the scripts based on one collector or 2 collectors ./go-client.sh + ./go-client_2_collector.sh diff --git a/vnfs/VESreporting_vLB5.0/go-client_2_collector.sh b/vnfs/VESreporting_vLB5.0/go-client_2_collector.sh new file mode 100755 index 00000000..3fe872ea --- /dev/null +++ b/vnfs/VESreporting_vLB5.0/go-client_2_collector.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +export LD_LIBRARY_PATH="/opt/VES/libs/x86_64/" +DCAE_COLLECTOR_IP=$(cat /opt/config/dcae_collector_ip.txt) +DCAE_COLLECTOR_PORT=$(cat /opt/config/dcae_collector_port.txt) +DCAE_COLLECTOR_IP2=$(cat /opt/config/dcae_collector_ip2.txt) +DCAE_COLLECTOR_PORT2=$(cat /opt/config/dcae_collector_port2.txt) +./vpp_measurement_reporter $DCAE_COLLECTOR_IP $DCAE_COLLECTOR_PORT $DCAE_COLLECTOR_IP2 $DCAE_COLLECTOR_PORT2 eth1 diff --git a/vnfs/VESreporting_vLB5.0/vpp_measurement_reporter.c b/vnfs/VESreporting_vLB5.0/vpp_measurement_reporter.c index 78050498..d641f95f 100644 --- a/vnfs/VESreporting_vLB5.0/vpp_measurement_reporter.c +++ b/vnfs/VESreporting_vLB5.0/vpp_measurement_reporter.c @@ -129,6 +129,8 @@ int main(int argc, char** argv) port2 = atoi(argv[4]); vnic = argv[5]; } + else + vnic = argv[3]; MEASUREMENT_VNIC_PERFORMANCE * vnic_performance = NULL; @@ -233,7 +235,7 @@ int main(int argc, char** argv) } vpp_m = evel_new_measurement(READ_INTERVAL,"vLoadBalancer","TrafficStats_1.2.3.4"); - vnic_performance = (MEASUREMENT_VNIC_PERFORMANCE *)evel_measurement_new_vnic_performance("eth0", "true"); + vnic_performance = (MEASUREMENT_VNIC_PERFORMANCE *)evel_measurement_new_vnic_performance(vnic, "true"); evel_meas_vnic_performance_add(vpp_m, vnic_performance); if(vpp_m != NULL) { -- 2.16.6