# squished to one.
release = Cherry
+2020 July 22; version 2.1.0
+ Added metrics support (RIC-381).
+
2020 July 17; version 2.0.0
Add alarm support.
cmake_minimum_required( VERSION 3.5 )
set( major_version "2" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this
-set( minor_version "0" )
+set( minor_version "1" )
set( patch_level "0" )
set( install_root "${CMAKE_INSTALL_PREFIX}" )
# but Cmake insists on having these exist when we add them to include directories to
# enable code to find them after we build them.
#
-include_directories( "${srcd}/src/messaging;${srcd}/src/json;${srcd}/src/alarm;${srcd}/ext/jsmn" )
+include_directories( "${srcd}/src/messaging;${srcd}/src/json;${srcd}/src/alarm;${srcd}/src/metrics;${srcd}/ext/jsmn" )
# Compiler flags
#
add_subdirectory( src/messaging )
add_subdirectory( src/xapp )
add_subdirectory( src/alarm )
+add_subdirectory( src/metrics )
#add_subdirectory( doc ) # this will auto skip if {X}fm is not available
# shared and static libraries are built from the same object files.
#
add_library( ricxfcpp_shared SHARED
- "$<TARGET_OBJECTS:message_objects>;$<TARGET_OBJECTS:json_objects>;$<TARGET_OBJECTS:alarm_objects>;$<TARGET_OBJECTS:xapp_objects>"
+ "$<TARGET_OBJECTS:message_objects>;$<TARGET_OBJECTS:json_objects>;$<TARGET_OBJECTS:alarm_objects>;$<TARGET_OBJECTS:metrics_objects>;$<TARGET_OBJECTS:xapp_objects>"
)
set_target_properties( ricxfcpp_shared
PROPERTIES
SOVERSION ${major_version}
VERSION ${major_version}.${minor_version}.${patch_level}
)
-target_include_directories( ricxfcpp_shared PUBLIC "src/messenger" "src/xapp" )
+#target_include_directories( ricxfcpp_shared PUBLIC "src/alarm" "src/metrics" "src/messenger" "src/xapp" )
# we only build/export the static archive (.a) if generating a dev package
if( DEV_PKG )
add_library( ricxfcpp_static STATIC
- "$<TARGET_OBJECTS:message_objects>;$<TARGET_OBJECTS:json_objects>;$<TARGET_OBJECTS:xapp_objects>"
+ "$<TARGET_OBJECTS:message_objects>;$<TARGET_OBJECTS:json_objects>;$<TARGET_OBJECTS:alarm_objects>;$<TARGET_OBJECTS:metrics_objects>;$<TARGET_OBJECTS:xapp_objects>"
)
set_target_properties( ricxfcpp_static
PROPERTIES
SOVERSION ${major_version}
VERSION ${major_version}.${minor_version}.${patch_level}
)
- target_include_directories( ricxfcpp_static PUBLIC "src/messenger" "src/xapp" )
+# target_include_directories( ricxfcpp_static PUBLIC "src/alarm" "src/metrics" "src/messenger" "src/xapp" )
endif()
# -------- unit testing -------------------------------------------------------
enable_testing()
add_test(
NAME drive_unit_tests
- COMMAND bash ../test/unit_test.sh -q
+ COMMAND bash ../test/unit_test.sh -q CMBUILD=${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ../test
)
--- /dev/null
+# don't stash generated things
+*.md
+*.rst
+*.ps
+*.pdf
+
+# don't stash intermediates
+*.sp
+*.ca
+*.bncfile
+++ /dev/null
-=============
-RELEASE NOTES
-=============
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-.. SPDX-License-Identifier: CC-BY-4.0
-..
-.. CAUTION: this document is generated from source in doc/src/*
-.. To make changes edit the source and recompile the document.
-.. Do NOT make changes directly to .rst or .md files.
-
-
-
-C++ Framework Release Notes
-===========================
-The following is a list of release highlights for the C++
-xAPP Framework.
-
-Cherry Release
-==============
-
-2020 July 17; version 2.0.0
----------------------------
-Add alarm support.
-
-Add xapp namespace for Message, Msg_component, Alarm and Jhah
-objects. This is a BREAKING change and thus the major version
-number was bumped to 2.
-
-
-2020 June 29; version 1.2.0
----------------------------
-Add support for json parsing
-
-
-2020 June 26; version 1.1.0
----------------------------
-Change the port type in constructors to indicate "const"
-
-Version bump to 1.1.0 to allow patches to bronze code to
-continue to be done on the 1.0.* level.
-
-
-
-Bronze Release
-==============
-
-2020 April 28; version 1.0.0
-----------------------------
-Bump version to force package build (old CI version added
-incorrect install prefix). Bump to 1.0.0 for release.
-
-
-2020 March 27; version 0.1.2
-----------------------------
-Changes identified by sonar (missing copy/move builders)
-rmr_dump example programme Improvements to code for better
-test coverage
docs = user_guide
src = user_guide.xfm
-imbed_src = cpp_frame.im example1.im example2.im example3.im jhash.im example4.im
+imbed_src = cpp_frame.im example1.im example2.im example3.im jhash.im example4.im metrics.im
desired_out = rst ps md
include ../master.mk
this example as simple as possible, the counters
are not locked when incremented.
+&h2(Metrics Generation)
+The example also illustrates how a metrics object instance can be created
+and used to send appliction metrics to the collector.
+In this example the primary callback function will genereate metrics with
+the receipt of each 1000th message.
+
&space
&indent
.** pull in the code from the example directory
--- /dev/null
+.** vim: sw=4 ts=4 et :
+.if false
+==================================================================================
+ Copyright (c) 2020 AT&T Intellectual Property.
+ Copyright (c) 2020 Nokia
+
+ 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.
+==================================================================================
+.fi
+
+.if false
+ This imbed file contains the description of the metrics API.
+ It should be included by the main user.xfm source.
+.fi
+
+
+&h1(Metrics Support)
+The C++ xAPP framework provides a lightweight interface to the metrics
+gateway allowing the xAPP to create and send metrics updates without
+needing to understand the underlying message format.
+From the xAPP's perspective, the metrics object is created with one or
+more key/value measurement pairs and then is sent to the process
+responsible for forwarding them to the various collection processes.
+The following sections describe the Metrics object and the API associated
+with it.
+
+
+&h2(Creating The Metrics Object)
+The &cw(xapp:Metrics) object can be created directly, or via the xapp framework.
+When creating directly the xAPP must supply an RMR message for the object to use;
+when the framework is used to create the object, the message is created as
+as part of the process.
+
+The framework provides three constructors for the metrics instance allowing the
+xAPP to supply the measurement source, the source and reporter, or to default to
+using the xAPP name as both the source and reporter (see section &ital( &snr_section )
+for a more detailed description of these).
+The framework constructors are illustrated in figure &frame_builders.
+&space
+
+.ca start frame_builder.ca
+&ex_start
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( );
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string source );
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string reporter, std::string source );
+&ex_end
+&export_fig( frame_builders )
+&fig_cen(The framework constructors for creating an instance of the metrics object.)
+&space
+.ca end
+.gv remain
+&ifroom( 2 : frame_builder.ca )
+
+.ca start create1.ca
+&ex_start
+
+ #include <ricxfcpp/Metrics>
+
+ char* port = (char *) "4560";
+
+ auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
+ auto reading = std::shared_ptr<xapp::Metrics>( x->Alloc_metric( ) );
+&ex_end
+&export_fig( metric_create_1 )
+&fig_cen(Metrics instance creation using the framework. )
+&space
+.ca end
+.gv remain
+&ifroom( 2 : create1.ca )
+
+Figures &metric_create_1 illustrates how the framework constructor can be used to
+create a metrics instance.
+While it is unlikely that an xAPP will create a metrics instance directly, there
+are three similar constructors available.
+These are prototypes are shown in figure &metrics_builders and their use is illustrated
+in figure &metrics_create_2.
+
+.ca start met_builder.ca
+&ex_start
+ Metrics( std::shared_ptr<xapp::Message> msg );
+ Metrics( std::shared_ptr<xapp::Message> msg, std::string msource );
+ Metrics( std::shared_ptr<xapp::Message> msg, std::string reporter, std::string msource );
+&ex_end
+&export_fig( metrics_builders )
+&fig_cen(Metrics object constructors.)
+&space
+.ca end
+.gv remain
+&ifroom( 1 : met_builder.ca )
+
+.ca start create2.ca
+&ex_start
+ #include <ricxfcpp/Metrics>
+
+ char* port = (char *) "4560";
+
+ auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
+ auto msg = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
+ auto reading = std::shared_ptr<xapp::Metrics>( new Metrics( msg ) );
+&ex_end
+&export_fig( metrics_create_2 )
+&fig_cen(Direct creation of a metrics instance.)
+&space
+.ca end
+.gv remain
+&ifroom( 2 : create2.ca )
+
+&h2(Adding Values)
+Once an instance of the metrics object is created, the xAPP may push values in
+preparation to sending the measurement(s) to the collector.
+The &cw(Push_data()) function is used to push each key/value pair and is illustrated
+in figure &push_fig.
+
+.ca start push.ca
+&ex_start
+ reading->Push_data( "normal_count", (double) norm_count );
+ reading->Push_data( "high_count", (double) hi_count );
+ reading->Push_data( "excessive_count", (double) ex_count );
+&ex_end
+&export_fig( push_fig )
+&fig_cen(Pushing key/value pairs into a metrics instance.)
+&space
+.ca end
+.gv remain
+&ifroom( 1 : push.ca )
+
+&h2(Sending A Measurement Set)
+After all of the measurement key/value pairs have been added to the instance, the
+&cw(Send()) function can be invoked to create the necessary RMR message and send that
+to the collection application.
+Following the send, the key/value pairs are cleared from the instance and the
+xAPP is free to start pushing values into the instance again.
+The send function has the following prototype and returns &cw(true) on success and
+&cw(false) if the measurements could not be sent.
+
+&h2(Source and Reporter)
+&export_var( snr_section : Source and Reporter )
+The alarm collector has the understanding that a measurement might be &ital(sourced) from one
+piece of equipment, or software component, but reported by another.
+For auditing purposes it makes sense to distinguish these, and as such the metrics object
+allows the xAPP to identify the case when the source and reporter are something other than
+the xAPP which is generating the metrics message(s).
+
+&space
+The &ital(source) is the component to which the measurement applies.
+This could be a network interface card counting packets, a temperature sensor, or the xAPP
+itself reporting xAPP related metrics.
+The &ital(reporter) is the application that is reporting the measurement(s) to the collector.
+
+&space
+By default, both reporter and source are assumed to be the xAPP, and the name is automatically
+determined using the run-time supplied programme name.
+Should the xAPP need to report measurements for more than one source it has the option to
+create an instance for every reporter source combination, or to set the reporter and/or
+source with the generation of each measurement set.
+To facilitate the ability to change the source and/or the reporter without the need to create
+a new metrics instance, two &ital(setter) functions are provided.
+The prototypes for these are shown in figure &setter_fig.
+&space
+
+.ca start setter.ca
+&ex_start
+ void Set_source( std::string new_source );
+ void Set_reporter( std::string new_reporter );
+&ex_end
+&export_fig( setter_fig )
+&fig_cen( Setter functions allowing the reporter and/or source to be set after construction. )
+&space
+.ca end
+.gv remain
+&ifroom( 1 : setter.ca )
.im cpp_frame.im
.im jhash.im
.im alarm.im
+.im metrics.im
&h1(Example Programmes)
The following sections contain several example programmes which are written on
Cherry Release
==============
+2020 July 22; version 2.1.0
+---------------------------
+Added metrics support (RIC-381).
+
+
2020 July 17; version 2.0.0
---------------------------
Add alarm support.
+METRICS SUPPORT
+===============
+
+The C++ xAPP framework provides a lightweight interface to
+the metrics gateway allowing the xAPP to create and send
+metrics updates without needing to understand the underlying
+message format. From the xAPP's perspective, the metrics
+object is created with one or more key/value measurement
+pairs and then is sent to the process responsible for
+forwarding them to the various collection processes. The
+following sections describe the Metrics object and the API
+associated with it.
+
+
+Creating The Metrics Object
+---------------------------
+
+The ``xapp`` object can be created directly, or via the xapp
+framework. When creating directly the xAPP must supply an RMR
+message for the object to use; when the framework is used to
+create the object, the message is created as as part of the
+process. The framework provides three constructors for the
+metrics instance allowing the xAPP to supply the measurement
+source, the source and reporter, or to default to using the
+xAPP name as both the source and reporter (see section
+*Source and Reporter* for a more detailed description of
+these). The framework constructors are illustrated in figure
+17.
+
+
+::
+
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( );
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string source );
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string reporter, std::string source );
+
+Figure 17: The framework constructors for creating an
+instance of the metrics object.
+
+
+::
+
+
+ #include <ricxfcpp/Metrics>
+
+ char* port = (char *) "4560";
+
+ auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
+ auto reading = std::shared_ptr<xapp::Metrics>( x->Alloc_metric( ) );
+
+Figure 18: Metrics instance creation using the framework.
+
+Figures 18 illustrates how the framework constructor can be
+used to create a metrics instance. While it is unlikely that
+an xAPP will create a metrics instance directly, there are
+three similar constructors available. These are prototypes
+are shown in figure 19 and their use is illustrated in figure
+20.
+
+::
+
+ Metrics( std::shared_ptr<xapp::Message> msg );
+ Metrics( std::shared_ptr<xapp::Message> msg, std::string msource );
+ Metrics( std::shared_ptr<xapp::Message> msg, std::string reporter, std::string msource );
+
+Figure 19: Metrics object constructors.
+
+
+::
+
+ #include <ricxfcpp/Metrics>
+
+ char* port = (char *) "4560";
+
+ auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
+ auto msg = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
+ auto reading = std::shared_ptr<xapp::Metrics>( new Metrics( msg ) );
+
+Figure 20: Direct creation of a metrics instance.
+
+
+
+Adding Values
+-------------
+
+Once an instance of the metrics object is created, the xAPP
+may push values in preparation to sending the measurement(s)
+to the collector. The ``Push_data()`` function is used to
+push each key/value pair and is illustrated in figure 21.
+
+::
+
+ reading->Push_data( "normal_count", (double) norm_count );
+ reading->Push_data( "high_count", (double) hi_count );
+ reading->Push_data( "excessive_count", (double) ex_count );
+
+Figure 21: Pushing key/value pairs into a metrics instance.
+
+
+
+Sending A Measurement Set
+-------------------------
+
+After all of the measurement key/value pairs have been added
+to the instance, the ``Send()`` function can be invoked to
+create the necessary RMR message and send that to the
+collection application. Following the send, the key/value
+pairs are cleared from the instance and the xAPP is free to
+start pushing values into the instance again. The send
+function has the following prototype and returns ``true`` on
+success and ``false`` if the measurements could not be sent.
+
+
+Source and Reporter
+-------------------
+
+The alarm collector has the understanding that a measurement
+might be *sourced* from one piece of equipment, or software
+component, but reported by another. For auditing purposes it
+makes sense to distinguish these, and as such the metrics
+object allows the xAPP to identify the case when the source
+and reporter are something other than the xAPP which is
+generating the metrics message(s).
+
+The *source* is the component to which the measurement
+applies. This could be a network interface card counting
+packets, a temperature sensor, or the xAPP itself reporting
+xAPP related metrics. The *reporter* is the application that
+is reporting the measurement(s) to the collector.
+
+By default, both reporter and source are assumed to be the
+xAPP, and the name is automatically determined using the
+run-time supplied programme name. Should the xAPP need to
+report measurements for more than one source it has the
+option to create an instance for every reporter source
+combination, or to set the reporter and/or source with the
+generation of each measurement set. To facilitate the ability
+to change the source and/or the reporter without the need to
+create a new metrics instance, two *setter* functions are
+provided. The prototypes for these are shown in figure 22.
+
+
+::
+
+ void Set_source( std::string new_source );
+ void Set_reporter( std::string new_reporter );
+
+Figure 22: Setter functions allowing the reporter and/or
+source to be set after construction.
+
+
+
EXAMPLE PROGRAMMES
==================
return 0;
}
- Figure 17: Simple callback application.
+ Figure 23: Simple callback application.
Callback Receiver
locked when incremented.
+Metrics Generation
+------------------
+
+The example also illustrates how a metrics object instance
+can be created and used to send appliction metrics to the
+collector. In this example the primary callback function will
+genereate metrics with the receipt of each 1000th message.
+
+
::
#include <stdio.h>
#include "ricxfcpp/message.hpp"
#include "ricxfcpp/msg_component.hpp"
+ #include <ricxfcpp/metrics.hpp>
#include "ricxfcpp/xapp.hpp"
// counts; not thread safe
long cb1_lastts = 0;
long cb1_lastc = 0;
- // respond with 2 messages for each type 1 received
+ /*
+ Respond with 2 messages for each type 1 received
+ Send metrics every 1000 messages.
+ */
void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
xapp::Msg_component payload, void* data ) {
long now;
mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
cb1_count++;
+
+ if( cb1_count % 1000 == 0 && data != NULL ) { // send metrics every 1000 messages
+ auto x = (Xapp *) data;
+ auto msgm = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
+
+ auto m = std::unique_ptr<xapp::Metrics>( new xapp::Metrics( msgm ) );
+ m->Push_data( "tst_cb1", (double) cb1_count );
+ m->Push_data( "tst_cb2", (double) cb2_count );
+ m->Send();
+ }
}
// just count messages
fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
x = new Xapp( port, true );
- x->Add_msg_cb( 1, cb1, NULL ); // register callbacks
+ x->Add_msg_cb( 1, cb1, x ); // register callbacks
x->Add_msg_cb( 2, cb2, NULL );
x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
// control should not return
}
- Figure 18: Simple callback application.
+ Figure 24: Simple callback application.
}
}
- Figure 19: Simple looping sender application.
+ Figure 25: Simple looping sender application.
}
}
- Figure 20: Simple looping sender application with alarm
+ Figure 26: Simple looping sender application with alarm
generation.
# variables are set to reference the needed files.
%.o:: %.cpp %.hpp
- g++ -g ${prereq%% *} -c
+ g++ -g ${prereq%% *} -c
% :: %.cpp
+ #C_INCLUDE_PATH=/tmp/usr/include:/usr/local/include g++ $< -g -o $@ -lricxfcpp -lrmr_si -lpthread -lm
g++ $< -g -o $@ -lricxfcpp -lrmr_si -lpthread -lm
all:: xapp_t1 xapp_t2 rmr_dump
#include "ricxfcpp/message.hpp"
#include "ricxfcpp/msg_component.hpp"
+#include <ricxfcpp/metrics.hpp>
#include "ricxfcpp/xapp.hpp"
// counts; not thread safe
long cb1_lastts = 0;
long cb1_lastc = 0;
-// respond with 2 messages for each type 1 received
+/*
+ Respond with 2 messages for each type 1 received
+ Send metrics every 1000 messages.
+*/
void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
xapp::Msg_component payload, void* data ) {
long now;
mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\n" );
cb1_count++;
+
+ if( cb1_count % 1000 == 0 && data != NULL ) { // send metrics every 1000 messages
+ auto x = (Xapp *) data;
+ auto msgm = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
+
+ auto m = std::unique_ptr<xapp::Metrics>( new xapp::Metrics( msgm ) );
+ m->Push_data( "tst_cb1", (double) cb1_count );
+ m->Push_data( "tst_cb2", (double) cb2_count );
+ m->Send();
+ }
}
// just count messages
fprintf( stderr, "<XAPP> starting %d threads\n", nthreads );
x = new Xapp( port, true );
- x->Add_msg_cb( 1, cb1, NULL ); // register callbacks
+ x->Add_msg_cb( 1, cb1, x ); // register callbacks
x->Add_msg_cb( 2, cb2, NULL );
x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
char* djson; // dup so we can save it
void* rp = NULL; // return value
- if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS )) != NULL ) {
+ if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) {
djson = strdup( json ); // allows user to free/overlay their buffer as needed
rp = parse_jobject( st, djson, "" ); // empty prefix for the root object; parse_jo will clean up and free st
if( rp == NULL ) {
# For clarity: this generates object, not a lib as the CM command implies.
#
add_library( message_objects OBJECT
- callback.cpp
+ callback.cpp
default_cb.cpp
message.cpp
messenger.cpp
# header files should go into .../include/xfcpp/
if( DEV_PKG )
install( FILES
- callback.hpp
- default_cb.hpp
- message.hpp
+ callback.hpp
+ default_cb.hpp
+ message.hpp
messenger.hpp
msg_component.hpp
DESTINATION ${install_inc}
- )
+ )
endif()
*/
//char* Message::Copy_payload( ){
std::unique_ptr<unsigned char> xapp::Message::Copy_payload( ){
- unsigned char* new_payload = NULL;
if( mbuf != NULL ) {
- new_payload = (unsigned char *) malloc( sizeof( unsigned char ) * mbuf->len );
+ unsigned char* new_payload = new unsigned char[mbuf->len];
memcpy( new_payload, mbuf->payload, mbuf->len );
+ return std::unique_ptr<unsigned char>( new_payload );
}
- return std::unique_ptr<unsigned char>( new_payload );
+ return NULL;
}
/*
Makes a copy of the source field and returns a smart pointer to it.
*/
std::unique_ptr<unsigned char> xapp::Message::Get_src(){
- unsigned char* m = NULL;
-
- m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_SRC );
- memset( m, 0, sizeof( unsigned char ) * RMR_MAX_SRC );
+ unsigned char* m = new unsigned char[RMR_MAX_SRC];
if( m != NULL ) {
rmr_get_src( mbuf, m );
#include "message.hpp"
#include "messenger.hpp"
#include "alarm.hpp"
+#include "metrics.hpp"
namespace xapp {
}
+// ------------------ metrics support --------------------------------------------------
+std::unique_ptr<xapp::Metrics> xapp::Messenger::Alloc_metrics( ) {
+ std::shared_ptr<Message> m;
+
+ m = Alloc_msg( 4096 );
+ return std::unique_ptr<xapp::Metrics>( new xapp::Metrics( m ) );
+}
+
+std::unique_ptr<xapp::Metrics> xapp::Messenger::Alloc_metrics( std::string source ) {
+ std::shared_ptr<Message> m;
+
+ m = Alloc_msg( 4096 );
+ return std::unique_ptr<xapp::Metrics>( new xapp::Metrics( m, source ) );
+}
+
+std::unique_ptr<xapp::Metrics> xapp::Messenger::Alloc_metrics( std::string reporter, std::string source ) {
+ std::shared_ptr<Message> m;
+
+ m = Alloc_msg( 4096 );
+ return std::unique_ptr<xapp::Metrics>( new xapp::Metrics( m, reporter, source ) );
+}
+
// ------------------- listening support -----------------------------------------------
/*
#include "message.hpp"
#include "alarm.hpp"
+#include "metrics.hpp"
#ifndef RMR_FALSE
#define RMR_FALSE 0
std::unique_ptr<xapp::Alarm> Alloc_alarm( std::string meid );
std::unique_ptr<xapp::Alarm> Alloc_alarm( int prob_id, std::string meid );
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( ); // metrics allocation
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string source );
+ std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string reporter, std::string source );
+
void Listen( ); // lisen driver
std::unique_ptr<Message> Receive( int timeout ); // receive 1 message
void Stop( ); // force to stop
--- /dev/null
+# vim: sw=4 ts=4 noet:
+#
+#==================================================================================
+# Copyright (c) 2020 Nokia
+# Copyright (c) 2020 AT&T Intellectual Property.
+#
+# 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.
+#==================================================================================
+#
+
+
+# For clarity: this generates object, not a lib as the CM command implies.
+#
+add_library( metrics_objects OBJECT
+ metrics.cpp
+)
+
+target_include_directories (metrics_objects PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:include>
+ PRIVATE src)
+
+# header files should go into .../include/xfcpp/
+if( DEV_PKG )
+ install( FILES
+ metrics.hpp
+ DESTINATION ${install_inc}
+ )
+endif()
+
--- /dev/null
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+ Copyright (c) 2020 Nokia
+ Copyright (c) 2020 AT&T Intellectual Property.
+
+ 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.
+==================================================================================
+*/
+
+/*
+ Mnemonic: metrics.cpp
+ Abstract: Impementation of the metrics functions. (see metrics.hpp)
+
+
+ void Set_source( std::string new_source );
+ void Set_reporter( std::string new_reporter );
+ void Push_data( std::string key, double value );
+
+ Date: 20 July 2020
+ Author: E. Scott Daniels
+*/
+
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <rmr/RIC_message_types.h>
+#ifndef RIC_METRICS
+ #define RIC_METRICS 120
+#endif
+
+#include <iostream>
+
+#include "msg_component.hpp"
+#include "message.hpp"
+#include "metrics.hpp"
+
+extern char* __progname; // runtime lib supplied since we don't get argv[0]
+
+
+
+namespace xapp {
+
+
+
+// ------ private ----------------------------------------------
+
+
+/*
+ Return the current time in milliseconds past the UNIX epoch.
+*/
+static long long now( void ) {
+ struct timespec ts;
+ long long now = 0;
+
+ clock_gettime( CLOCK_REALTIME, &ts );
+ now = (ts.tv_sec * 1000000) + (ts.tv_nsec/1000000); // convert nano to milli and bang onto seconds
+
+ return now;
+}
+
+
+/*
+ Build the payload that we'll send.
+ Accepts a pointer to the payload message component in the RMR message that we are
+ to fill in, along with the maxmimum length of the payload. Returns the length of
+ the payload that was acutally used.
+
+ Currently, the munchkin expects:
+ {
+ "reporter": "<string>",
+ "generator" "<string>",
+ "timestamp": double,
+ "data": [
+ {
+ "id": "<string>",
+ "type": "<string>",
+ "value": double
+ },...
+ ]
+ }
+*/
+int xapp::Metrics::build_payload( xapp::Msg_component payload, int max_len ) {
+ int used; // actual size of payload created
+ std::string generator;
+
+
+ if( data.compare( "" ) == 0 ) { // xAPP never pushed any data
+ return 0; // don't build, just bail
+ }
+
+ generator = source.compare( "" ) == 0 ? reporter : source;
+
+ used = snprintf( (char *) payload.get(), max_len,
+ "{ "
+ "\"reporter\": \"%s\", "
+ "\"generator\": \"%s\", "
+ "\"timestamp\": %lld, "
+ "\"data\": [ %s ] "
+ " }",
+ reporter.c_str(),
+ generator.c_str(),
+ now(),
+ data.c_str()
+ );
+
+ return used;
+}
+
+
+// --------------- builders/operators -------------------------------------
+
+/*
+ Create a new metrics "message" with a provided (empty) RMR message.
+*/
+xapp::Metrics::Metrics( std::shared_ptr<Message> msg ) :
+ msg( msg ),
+ reporter( __progname ),
+ source( "" ),
+ data( "" )
+{ /* empty body */ }
+
+/*
+ Build a metrics for a value source other than the calling xAPP
+*/
+xapp::Metrics::Metrics( std::shared_ptr<Message> msg, std::string msource ) :
+ msg( msg ),
+ reporter( __progname ),
+ source( msource ),
+ data( "" )
+{ /* empty body */ }
+
+/*
+ Build a metrics object that allows the xAPP to set it's reporter name
+ rather than assuming the programme name and the source.
+
+ The xAPP can pass "" as the msource if the intent is just to supply
+ an alternate programme name which is also the source.
+*/
+xapp::Metrics::Metrics( std::shared_ptr<Message> msg, std::string reporter, std::string msource ) :
+ msg( msg ),
+ reporter( reporter ),
+ source( msource ),
+ data( "" )
+{ /* empty body */ }
+
+// ------------------ copy and move support ---------------------------------
+
+/*
+ Copy builder. Given a source object instance (soi), create a copy.
+ Creating a copy should be avoided as it can be SLOW!
+*/
+xapp::Metrics::Metrics( const Metrics& soi ) {
+ msg = soi.msg;
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+}
+
+/*
+ Assignment operator. Simiolar to the copycat, but "this" object exists and
+ may have data that needs to be released prior to making a copy of the soi.
+*/
+Metrics& xapp::Metrics::operator=( const Metrics& soi ) {
+ if( this != &soi ) { // cannot do self assignment
+ // anything that must be freed from 'this' must be done here
+
+ msg = soi.msg;
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+ }
+
+ return *this;
+}
+
+/*
+ Move builder. Given a source object instance (soi), move the information from
+ the soi ensuring that the destriction of the soi doesn't trash things from
+ under us.
+*/
+xapp::Metrics::Metrics( Metrics&& soi ) {
+ msg = soi.msg; // capture pointers and copy data before setting soruce things to nil
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+
+ soi.msg = NULL; // prevent closing of RMR stuff on soi destroy
+}
+
+/*
+ Move Assignment operator. Move the message data to the existing object
+ ensure the object reference is cleaned up, and ensuring that the source
+ object references are removed.
+*/
+Metrics& xapp::Metrics::operator=( Metrics&& soi ) {
+ if( this != &soi ) { // cannot do self assignment
+ // anything that needs to be freed/delted from 'this', must be done here
+
+ msg = soi.msg; // move pointers and values
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+
+ soi.msg = NULL; // prevent bad things when source is destroyed
+ }
+
+ return *this;
+}
+
+/*
+ Destroyer.
+*/
+xapp::Metrics::~Metrics() {
+ msg = NULL;
+}
+
+
+// ---- setters -------------------------------------------------
+
+void xapp::Metrics::Set_source( std::string new_source ) {
+ source = new_source;
+}
+
+void xapp::Metrics::Set_reporter( std::string new_reporter ) {
+ reporter = new_reporter;
+}
+
+/*
+ Pushes the key/value pair onto the current data list.
+ This could be more efficent, but for now it is simple.
+*/
+void xapp::Metrics::Push_data( std::string key, double value ) {
+ char wbuf[1024];
+ char* sep = (char *) " ";
+
+ if( data.compare( "" ) != 0 ) { // first on the list, no need for preceeding comma
+ sep = (char *) ", ";
+ }
+
+ snprintf( wbuf, sizeof( wbuf ), "%s{ \"id\": \"%s\", \"value\": %.5f }", sep, key.c_str(), value );
+
+ data += std::string( wbuf );
+}
+
+// ------------------- getters ------------------------------------
+
+
+// ------- message sending ---------------------------------------
+
+/*
+ Send the message by building the payload and passing to RMR.
+ Returns the state of the send (true == good). If the xapp did not
+ push any data, no send is actually invoked, and true is reported.
+*/
+bool xapp::Metrics::Send( ) {
+ int used = 0; // actual num bytes used in the payload
+ bool state = true;
+
+ used = build_payload( msg->Get_payload(), msg->Get_available_size() - 1 );
+ if( used > 0 ) {
+ //fprintf( stderr, ">>>> sending payload: %s\n", (char *) msg->Get_payload().get() );
+ state = msg->Send_msg( RIC_METRICS, RMR_VOID_SUBID, used+1, NULL );
+ }
+
+ data = ""; // clear data after the send
+
+ return state;
+}
+
+
+
+} // namespace
--- /dev/null
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+ Copyright (c) 2020 Nokia
+ Copyright (c) 2020 AT&T Intellectual Property.
+
+ 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.
+==================================================================================
+*/
+
+/*
+ Mnemonic: metrics.hpp
+ Abstract: Headers for the metrics class.
+ This class provides a simple interface for an xAPP to compose and
+ send metrics. The class allows the xAPP to name the metric and to
+ provide one or more key/value pairs which are sent to the metrics
+ gateway (munchkin) when Send() is invoked. The class hides the
+ true message structure that the munchkin desires from the xAPP.
+
+ The munchkin endpoint is assumed to be a routable target and thus
+ normal RMR messages (with appropriate type) are sent.
+
+ Date: 20 July 2020
+ Author: E. Scott Daniels
+*/
+
+#ifndef _XAPP_METRICS_HPP
+#define _XAPP_METRICS_HPP
+
+
+#include <iostream>
+#include <string>
+
+#include "msg_component.hpp"
+
+namespace xapp {
+
+// ------------------------------------------------------------------------
+
+/*
+This class is loosely tied to the json expected by the munchkin. See the munchkin
+documentation for explicit information, but this will help to understand a bit:
+ {
+ "reporter": "<string>",
+ "generator" "<string>",
+ "timestamp": double,
+ "data": [
+ {
+ "id": "<string>",
+ "type": "<string>",
+ "value": double
+ },...
+ ]
+ }
+*/
+
+class Metrics {
+ private:
+ std::shared_ptr<Message> msg; // message to send
+
+ // data for the payload
+ std::string reporter; // this application as it's reporting the metrics
+ std::string source; // source of the measurement if not the reporter
+ std::string data; // key/value/type tuples "pushed" waiting for send
+
+
+ int build_payload( xapp::Msg_component payload, int payload_len );
+
+ public:
+
+ Metrics( std::shared_ptr<Message> msg ); // builders
+ Metrics( std::shared_ptr<Message> msg, std::string msource ); // allow xapp to supply metric source
+ Metrics( std::shared_ptr<Message> msg, std::string reporter, std::string msource ); // allow xapp to supply all
+
+ Metrics( const Metrics& soi ); // copy to newly created instance
+ Metrics& operator=( const Metrics& soi ); // copy operator
+ Metrics( Metrics&& soi ); // mover
+ Metrics& operator=( Metrics&& soi ); // move operator
+ ~Metrics(); // destroyer
+
+
+ void Set_source( std::string new_source );
+ void Set_reporter( std::string new_reporter );
+ void Push_data( std::string key, double value );
+
+
+ bool Send( );
+};
+
+} // namespace
+
+#endif
binaries = unit_test
-tests:: unit_test jhash_test
+tests:: unit_test jhash_test metrics_test
+
+include_list = -I ../src/metrics -I ../src/alarm -I ../src/messaging
# RMR emulation
rmr_em.o:: rmr_em.c
cc -g rmr_em.c -c
+# include the emulated rmr stuff, but we still need the rmr lib for symtab things which we cannot hope to
+# emulate (and don't need to)
+#
unit_test:: unit_test.cpp rmr_em.o
- # do NOT link the xapp lib; we include all modules in the test programme
- g++ -g $(coverage_opts) -I ../src/alarm -I ../src/messaging unit_test.cpp -o unit_test rmr_em.o -lpthread
+ g++ -g $(coverage_opts) $(include_list) unit_test.cpp -o unit_test -L../.build -lricxfcpp rmr_em.o -lrmr_si -lpthread
# build a special jwrapper object with coverage settings
jwrapper_test.o:: ../src/json/jwrapper.c ../src/json/jwrapper.h
# do NOT link the xapp lib; we include all modules in the test programme
g++ -g $(coverage_opts) -I ../src/json -I ../ext/jsmn jhash_test.cpp -o jhash_test jwrapper_test.o -lrmr_si -lpthread
+metrics_test:: metrics_test.cpp rmr_em.o
+ # do NOT link the xapp lib; we include all modules in the test programme
+ g++ -g $(coverage_opts) $(include_list) metrics_test.cpp -o metrics_test -L../.build -lricxfcpp rmr_em.o -l rmr_si -lpthread
+
# prune gcov files generated by system include files
clean::
rm -f *.h.gcov *.c.gcov
--- /dev/null
+// vim: ts=4 sw=4 noet :
+/*
+==================================================================================
+ Copyright (c) 2020 Nokia
+ Copyright (c) 2020 AT&T Intellectual Property.
+
+ 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.
+==================================================================================
+*/
+
+/*
+ Mnemonic: metric_test.cpp
+ Abstract: This is the unit test driver for the metrics class.
+
+ Date: 20 July 2020
+ Author: E. Scott Daniels
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <string>
+#include <memory>
+
+#include "../src/messaging/callback.hpp"
+#include "../src/messaging/default_cb.hpp"
+#include "../src/messaging/message.hpp"
+#include "../src/messaging/messenger.hpp"
+#include "../src/messaging/msg_component.hpp"
+#include "../src/alarm/alarm.hpp"
+#include "../src/xapp/xapp.hpp"
+
+#include "../src/metrics/metrics.hpp" // overtly pull the code under test to get coverage opts
+#include "../src/metrics/metrics.cpp"
+
+#include "ut_support.cpp"
+
+int main( int argc, char** argv ) {
+ int errors = 0;
+ std::shared_ptr<Xapp> x;
+ std::shared_ptr<xapp::Metrics> m;
+
+ set_test_name( "jhash_test" );
+
+ x = std::shared_ptr<Xapp>( new Xapp( "4560", true ) );
+ if( x == NULL ) {
+ fprintf( stderr, "<FAIL> unable to allocate xapp object\n" );
+ announce_results( 1 );
+ return 1;
+ }
+
+ m = x->Alloc_metrics( );
+ m->Push_data( "barney_balance", 216.49 );
+ m->Push_data( "fred_balance", 760.88 );
+ m->Send( );
+
+ // ensure data is cleared after first send
+ m->Push_data( "barney_balance", 216.49 );
+ m->Push_data( "fred_balance", 760.88 );
+ m->Push_data( "wilma_balance", 1986.0430 );
+ m->Send();
+
+ m->Send(); // shouldn't really send
+
+
+ // drive alternate builders
+ m = x->Alloc_metrics( "different-source" );
+ m->Push_data( "wilma_balance", 1986.0430 );
+ m->Send();
+
+ m = x->Alloc_metrics( "different-app", "different-source" );
+ m->Push_data( "wilma_balance", 1986.0430 );
+ m->Push_data( "pebbles_balance", 1982.0614 );
+ m->Send();
+
+
+ m->Set_reporter( "set-reporter" );
+ m->Set_source( "set-source" );
+
+
+ // drive move/copy adjunct functions
+
+ xapp::Metrics b = *m.get(); // force the move/copy operator functions to trigger
+ xapp::Metrics c( NULL ); // a useless metric without a message
+ xapp::Metrics f( NULL, "source" ); // a useless metric to drive direct construction
+ c = *m.get(); // drive copy = operator
+
+ b = std::move( c ); // move = operator
+ xapp::Metrics d = std::move( b ); // move constructor
+
+
+ // ---------------------------- end housekeeping ---------------------------
+ announce_results( errors );
+ return !!errors;
+}
#include "../src/messaging/messenger.hpp"
#include "../src/messaging/msg_component.hpp"
#include "../src/alarm/alarm.hpp"
+#include "../src/metrics/metrics.hpp"
#include "../src/xapp/xapp.hpp"
#include "../src/messaging/callback.cpp"
x->Halt();
}
+/*
+ Drive the constructors so that we actually see the coverage
+ in the .gc* files. The metrics tests will actually verify that
+ the various underlying functions work.
+*/
+static int metrics( std::shared_ptr<Xapp> x ) {
+ std::shared_ptr<xapp::Metrics> m;
+
+ m = x->Alloc_metrics( ); // basic construction
+ m = x->Alloc_metrics( "different-source" ); // drive alternate builders
+ m = x->Alloc_metrics( "different-app", "different-source" );
+}
+
int main( int argc, char** argv ) {
std::thread* tinfo; // we'll start a thread that will shut things down after a few seconds
std::unique_ptr<xapp::Message> msg;
a = x->Alloc_alarm( 13, "meid-abc" );
errors += fail_if( a == NULL, "unable to allcoate an alarm with meid and problem id" );
-
- //#####: 210:xapp::Alarm::Alarm( const Alarm& soi ) {
- //#####: 227:Alarm& xapp::Alarm::operator=( const Alarm& soi ) {
- //#####: 249:xapp::Alarm::Alarm( Alarm&& soi ) {
- //#####: 269:Alarm& xapp::Alarm::operator=( Alarm&& soi ) {
-
a->Set_meid( "changed_meid" );
for( i = 0; i < 6; i++ ) {
a->Set_severity( i ); // drive all switch possibilities
b = std::move( c ); // move = operator
xapp::Alarm d = std::move( b ); // move constructor
+
+ // ------ minimal metrics to prove coverage ------------------------------------------------------------
+ metrics( x );
+
+ // ------------------------------------------------------------------------------------------------------
+
announce_results( errors );
return errors > 0;
}
spew="cat" # default to dumping all make output on failure (-q turns it to ~40 lines)
+if [[ -d ../.build ]]
+then
+ build_dir="../.build"
+else
+ if [[ -d ../build ]]
+ then
+ build_dir="../build"
+ fi
+fi
+
while [[ $1 == "-"* ]]
do
case $1 in
shift
done
-export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
-export LIBRARY_PATH=/usr/local/lib:$LIBRARY_PATH
+while [[ $1 == *"="* ]]
+do
+ case ${1%%=*} in
+ CMBUILD)
+ export build_dir=${1##*=}
+ ;;
+ esac
+
+ shift
+done
+
+echo "## INFO ##"
+echo "build dir=$build_dir"
+find $build_dir -name "libricxfcpp.*"
+echo "## INFO ##"
+
+export LD_LIBRARY_PATH=$build_dir:/usr/local/lib:$LD_LIBRARY_PATH
+export LIBRARY_PATH=$build_dir:/usr/local/lib:$LIBRARY_PATH
make nuke >/dev/null
make tests >/tmp/PID$$.log 2>&1
spew="cat"
-for x in unit_test jhash_test
+for x in unit_test jhash_test metrics_test
do
./$x >/tmp/PID$$.log 2>&1
abort_if_error $? "test failed: $x"