From 03f0b88138804d2292e8de79cd05dd74d2034637 Mon Sep 17 00:00:00 2001 From: Geir Horn Date: Wed, 10 Jan 2024 16:26:09 +0100 Subject: [PATCH] First release - Added build script and AMPL license file - Fixed merge errors for the makefile - Extended the makefile header - Added initial AMQ message topics - Tested remote build - Removed AMPL license file - Validated build script - Accepting the metric definition message from the EMS Change-Id: I2c7c661d1006ff5a29f2d4ea77859036072c821b --- MakeSolver.sh | 27 +++++++++++++++++++-------- MetricUpdater.cpp | 37 +++++++++++++++++++++---------------- MetricUpdater.hpp | 40 +++++++++++++--------------------------- 3 files changed, 53 insertions(+), 51 deletions(-) diff --git a/MakeSolver.sh b/MakeSolver.sh index 5c3c8ba..63b2c26 100644 --- a/MakeSolver.sh +++ b/MakeSolver.sh @@ -7,6 +7,9 @@ # minimal installation of the latest Fedora version. The intent is to load # all dependencies in a constructive way. # +# Note that this is intended to be executed in a build machine that already +# has the solver code installed in the directory where the script is running. +# # Author and Copyright: Geir Horn, University of Oslo # Contact: Geir.Horn@mn.uio.no # License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/) @@ -16,7 +19,7 @@ # as root on the machine - put 'sudo' in front if the build user is not root. dnf --assumeyes install gcc-c++ make git boost boost-devel ccache \ -qpid-proton-cpp* jsoncpp-devel coin-or-Couenne wget +qpid-proton-cpp* json* coin-or-Couenne wget # Cloning the open source dependencies @@ -24,9 +27,9 @@ git clone https://github.com/jarro2783/cxxopts.git CxxOpts git clone https://github.com/GeirHo/TheronPlusPlus.git Theron++ mkdir Theron++/Bin -# Clone the solver component +# Clone the solver component (if it is not in this build directory already) -git clone https://opendev.org/nebulous/optimiser-solver.git Solver +#git clone https://opendev.org/nebulous/optimiser-solver.git Solver # Installing the AMPL library @@ -34,11 +37,19 @@ wget https://portal.ampl.com/external/?url=\ https://portal.ampl.com/dl/amplce/ampl.linux64.tgz -O ampl.linux64.tgz tar --file=ampl.linux64.tgz --extract mv ampl.linux-intel64 AMPL -rm ampl.linux64.tgz + #cp ampl.lic AMPL -# Building the solver component +# Buildirm ampl.linux64.tgzng the solver component +# Note: use this make command if the solver is installed in a +# subdirectory. -make -C Solver SolverComponent -e THERON=../Theron++ \ -AMPL_INCLUDE=../AMPL/amplapi/include AMPL_LIB=../AMPL/amplapi/lib \ -CxxOpts_DIR=../CxxOpts/include +#make -C Solver SolverComponent -e THERON=../Theron++ \ +#AMPL_INCLUDE=../AMPL/amplapi/include AMPL_LIB=../AMPL/amplapi/lib \ +#CxxOpts_DIR=../CxxOpts/include + +# Use this script if the solver is already in the current directory + +make SolverComponent -e THERON=./Theron++ \ +AMPL_INCLUDE=./AMPL/amplapi/include AMPL_LIB=./AMPL/amplapi/lib \ +CxxOpts_DIR=./CxxOpts/include diff --git a/MetricUpdater.cpp b/MetricUpdater.cpp index 19f6623..ce090c9 100644 --- a/MetricUpdater.cpp +++ b/MetricUpdater.cpp @@ -41,17 +41,22 @@ namespace NebulOuS void MetricUpdater::AddMetricSubscription( const MetricTopic & TheMetrics, const Address OptimiserController ) { - if( TheMetrics.is_object() ) - for( const auto & [MetricName, TopicName] : TheMetrics.items() ) + if( TheMetrics.is_object() && + TheMetrics.at( NebulOuS::MetricList ).is_object() ) { - auto [ MetricRecord, NewMetric ] = MetricValues.try_emplace( - TopicName, MetricName, JSON() ); + JSON MetricList = TheMetrics.at( NebulOuS::MetricList ); - if( NewMetric ) - Send( Theron::AMQ::NetworkLayer::TopicSubscription( - Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, - TopicName ), - Theron::AMQ::Network::GetAddress( Theron::Network::Layer::Session) ); + for( const JSON MetricDefinition : MetricList.items() ) + { + auto [ MetricRecord, NewMetric ] = MetricValues.try_emplace( + MetricDefinition.at( NebulOuS::MetricName ), JSON() ); + + if( NewMetric ) + Send( Theron::AMQ::NetworkLayer::TopicSubscription( + Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, + MetricRecord->first ), + Theron::AMQ::Network::GetAddress( Theron::Network::Layer::Session) ); + } } else { @@ -62,7 +67,7 @@ void MetricUpdater::AddMetricSubscription( const MetricTopic & TheMetrics, << "in function " << Location.function_name() <<"] " << "The message to define a new metric subscription is given as " << std::endl << TheMetrics.dump(2) << std::endl - << "this is not a JSON object!"; + << "this is not as expected!"; throw std::invalid_argument( ErrorMessage.str() ); } @@ -98,11 +103,12 @@ void MetricUpdater::UpdateMetricValue( const MetricValueUpdate & TheMetricValue, const Address TheMetricTopic) { Theron::AMQ::TopicName TheTopic - = TheMetricTopic.AsString().erase(0, MetricValueRootString.size() ); + = TheMetricTopic.AsString().erase( 0, + NebulOuS::MetricValueRootString.size() ); if( MetricValues.contains( TheTopic ) ) { - MetricValues.at( TheTopic ).Value = TheMetricValue[ NebulOuS::ValueLabel ]; + MetricValues.at( TheTopic ) = TheMetricValue[ NebulOuS::ValueLabel ]; ValidityTime = std::max( ValidityTime, TheMetricValue[ NebulOuS::TimePoint ].get< Solver::TimePointType >() ); @@ -135,10 +141,9 @@ void MetricUpdater::SLOViolationHandler( Solver::MetricValueType TheApplicationExecutionContext; - for( const auto & [_, MetricRecord ] : MetricValues ) - if( !MetricRecord.Value.is_null() ) - TheApplicationExecutionContext.emplace( MetricRecord.OptimisationName, - MetricRecord.Value ); + for( const auto & [ MetricName, MetricValue ] : MetricValues ) + if( !MetricValue.is_null() ) + TheApplicationExecutionContext.emplace( MetricName, MetricValue ); // The application context can then be sent to the solution manager // using the corresponding message, and the time stamp of the severity diff --git a/MetricUpdater.hpp b/MetricUpdater.hpp index d4fcf1c..db2b7ca 100644 --- a/MetricUpdater.hpp +++ b/MetricUpdater.hpp @@ -88,7 +88,15 @@ constexpr std::string_view TimePoint = "predictionTime"; // defined next. constexpr std::string_view MetricSubscriptions - = "eu.nebulouscloud.optimiser.solver.metrics"; + = "eu.nebulouscloud.monitoring.metric_lists"; + +// The JSON message attribute for the list of metrics is another JSON object +// stored under the following key, see the Event type III defined in +// https://158.39.75.54/projects/nebulous-collaboration-hub/wiki/slo-severity-based-violation-detector +// where the name of the metric is defined under as sub-key. + +constexpr std::string_view MetricList = "metric_list"; +constexpr std::string_view MetricName = "name"; // The metric value messages will be published on different topics and to // check if an inbound message is from a metric value topic, it is necessary @@ -154,33 +162,11 @@ private: // -------------------------------------------------------------------------- // // The metric values are stored essentially as a JSON values where the - // attributes are the metric names and the values are JSON values because - // they are polymorphic with respect to different variable types, and as - // they arrive as JSON values this avoids converting the values on input and - // output. The metric optimisation name is just a string. + // attributes are the metric names and the values are JSON values. It is + // assumed that same metric name is used both for the optimisation model + // and for the metric topic. - class MetricValueRecord - { - public: - const std::string OptimisationName; - JSON Value; - - MetricValueRecord( const std::string & TheName, const JSON InitialValue ) - : OptimisationName( TheName ), Value( InitialValue ) - {} - - MetricValueRecord( const MetricValueRecord & Other ) - : OptimisationName( Other.OptimisationName ), Value( Other.Value ) - {} - - MetricValueRecord() = delete; - ~MetricValueRecord() = default; - }; - - // This value record is used in the map where the subscribed topic name is - // the key so that values can quickly be updated when messages arrives. - - std::unordered_map< Theron::AMQ::TopicName, MetricValueRecord > MetricValues; + std::unordered_map< Theron::AMQ::TopicName, JSON > MetricValues; // The metric values should ideally be forecasted for the same future time // point, but this may not be assured, and as such a zero-order hold is