2 # ============LICENSE_START===============================================
3 # Copyright (C) 2020 Nordix Foundation. All rights reserved.
4 # ========================================================================
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 # ============LICENSE_END=================================================
20 // Sim mon server - query the agent and the simulators for counters and other data
21 // Presents a web page on localhost:9999/mon
23 var LOCALHOST="http://127.0.0.1:"
24 var MRSTUB_PORT="3905"
28 var PRODSTUB_PORT="8092"
30 var http = require('http');
32 var express = require('express');
39 app.get("/",function(req, res){
43 //Get parameter value from other server
44 function getSimCtr(url, index, cb) {
48 http.get(url, (resp) => {
49 // A chunk of data has been recieved.
50 resp.on('data', (chunk) => {
54 // The whole response has been received.
55 resp.on('end', () => {
56 var code=resp.statusCode
57 if (code > 199 && code < 300) {
60 cb("not found", index);
64 }).on("error", (err) => {
65 console.log("Error: " + err.message);
66 cb("no response", index);
69 cb("no response", index);
74 //Format a comma separated list of data to a html-safe string with fixed fieldsizes
75 function formatDataRow(commaList) {
77 var tmp=commaList.split(',');
78 for(var i=0;i<tmp.length;i++) {
80 var len = fieldSize-data.length;
85 str=str+data+" ";
90 //Format a comma separated list of ids to a html-safe string with fixed fieldsizes
91 function formatIdRow(commaList) {
93 var tmp=commaList.split(',');
94 for(var i=0;i<tmp.length;i++) {
95 tmp[i] = tmp[i].trim();
96 var data="<"+tmp[i]+">";
97 var len = fieldSize+4-data.length;
102 str=str+data+" ";
107 //Format a list of ids to a html-safe string in compact format
108 function formatIdRowCompact(commaList) {
109 if (commaList == undefined) {
113 var tmp=commaList.split(',');
114 for(var i=0;i<tmp.length;i++) {
115 tmp[i] = tmp[i].trim();
116 var data="<"+tmp[i]+">";
117 str=str+data+" ";
122 //Pad a string upto a certain size using a pad string
123 function padding(val, fieldSize, pad) {
125 for(var i=s.length;i<fieldSize;i++) {
131 //Function to check if the previous call has returned, if so return true, if not return false
132 //For preventing multiple calls to slow containers.
133 function checkFunctionFlag(flag) {
134 if (flagstore.hasOwnProperty(flag)) {
135 if (flagstore[flag] == 0) {
138 } else if (flagstore[flag] > 10) {
139 //Reset flag after ten attempts
140 console.log("Force release flag "+flag)
144 //Previous call not returned
145 console.log("Flag not available "+flag)
146 flagstore[flag]=flagstore[flag]+1
154 //Clear flag for parameter
155 function clearFlag(flag) {
159 //Status variables, for parameters values fetched from other simulators
160 var mr1="", mr2="", mr3="", mr4="", mr5="", mr6="";
162 //Status variables for agent
169 //Status variables for callback receiver
175 //Container names and ports of the ric simulator
179 //Status variables for each ric simulator
187 //Status variables, for parameters values fetched from ecs
188 var ecs1="", ecs2="", ecs3="", ecs4="", ecs_types="-", ecs_producers="-";
189 var ecs_producer_arr=new Array(0)
190 var ecs_producer_type_arr=new Array(0)
191 var ecs_producer_jobs_arr=new Array(0)
192 var ecs_producer_status_arr=new Array(0)
194 //Status variables, for parameters values fetched from prodstub
195 var ps2="", ps3="", ps4="", ps_types="-", ps_producers="-";
196 var ps_producer_type_arr=new Array(0)
197 var ps_producer_jobs_arr=new Array(0)
198 var ps_producer_delivery_arr=new Array(0)
200 //Counts the number of get request for the html page
203 var refreshCount_pol=-1
205 var refreshCount_ecs=-1
207 var ricbasename="ricsim"
209 function fetchAllMetrics_pol() {
211 console.log("Fetching policy metrics " + refreshCount_pol)
213 if (refreshCount_pol < 0) {
214 refreshCount_pol = -1
217 refreshCount_pol = refreshCount_pol - 1
222 //Extract the port numbers from the running simulators, for every 3 calls
223 const { exec } = require('child_process');
224 exec('docker ps --filter "name='+ricbasename+'" --format "{{.Names}} {{.Ports}}" | sed s/0.0.0.0:// | cut -d \'>\' -f1 | sed \'s/[[-]]*$//\'', (err, stdout, stderr) => {
227 simulators=`${stdout}`.replace(/(\r\n|\n|\r)/gm," ");
228 simulators=simulators.trim();
229 var sims=simulators.split(" ")
232 for(i=0;i<sims.length;i=i+2) {
233 simnames[i/2]=sims[i]
234 simports[i/2]=sims[i+1]
240 //Get metric values from the simulators
241 for(var index=0;index<simnames.length;index++) {
243 if (checkFunctionFlag("simvar1_"+index)) {
244 getSimCtr(LOCALHOST+simports[index]+"/counter/num_instances", index, function(data, index) {
245 simvar1[index] = data;
246 clearFlag("simvar1_"+index)
249 if (checkFunctionFlag("simvar2_"+index)) {
250 getSimCtr(LOCALHOST+simports[index]+"/counter/num_types", index, function(data,index) {
251 simvar2[index] = data;
252 clearFlag("simvar2_"+index)
255 if (checkFunctionFlag("simvar3_"+index)) {
256 getSimCtr(LOCALHOST+simports[index]+"/policytypes", index, function(data,index) {
257 data=data.replace(/\[/g,'');
258 data=data.replace(/\]/g,'');
259 data=data.replace(/ /g,'');
260 data=data.replace(/\"/g,'');
261 simvar3[index] = data;
262 clearFlag("simvar3_"+index)
265 if (checkFunctionFlag("simvar4_"+index)) {
266 getSimCtr(LOCALHOST+simports[index]+"/counter/interface", index, function(data,index) {
267 simvar4[index] = data;
268 clearFlag("simvar4_"+index)
271 if (checkFunctionFlag("simvar5_"+index)) {
272 getSimCtr(LOCALHOST+simports[index]+"/counter/remote_hosts", index, function(data,index) {
273 simvar5[index] = data;
274 clearFlag("simvar5_"+index)
277 if (checkFunctionFlag("simvar6_"+index)) {
278 getSimCtr(LOCALHOST+simports[index]+"/counter/datadelivery", index, function(data,index) {
279 simvar6[index] = data;
280 clearFlag("simvar6_"+index)
285 //MR - get metrics values from the MR stub
286 if (checkFunctionFlag("mr1")) {
287 getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_submitted", 0, function(data, index) {
292 if (checkFunctionFlag("mr2")) {
293 getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_fetched", 0, function(data, index) {
298 if (checkFunctionFlag("mr3")) {
299 getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_requests", 0, function(data, index) {
304 if (checkFunctionFlag("mr4")) {
305 getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_submitted", 0, function(data, index) {
310 if (checkFunctionFlag("mr5")) {
311 getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_fetched", 0, function(data, index) {
316 if (checkFunctionFlag("mr6")) {
317 getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_responses", 0, function(data, index) {
323 //CR - get metrics values from the callbackreceiver
324 if (checkFunctionFlag("cr1")) {
325 getSimCtr(LOCALHOST+CR_PORT+"/counter/received_callbacks", 0, function(data, index) {
330 if (checkFunctionFlag("cr2")) {
331 getSimCtr(LOCALHOST+CR_PORT+"/counter/fetched_callbacks", 0, function(data, index) {
336 if (checkFunctionFlag("cr3")) {
337 getSimCtr(LOCALHOST+CR_PORT+"/counter/current_messages", 0, function(data, index) {
342 //Agent - more get metrics from the agent
343 if (checkFunctionFlag("ag1")) {
344 getSimCtr(LOCALHOST+AGENT_PORT+"/status", 0, function(data, index) {
349 if (checkFunctionFlag("ag2")) {
350 getSimCtr(LOCALHOST+AGENT_PORT+"/services", 0, function(data, index) {
353 var jd=JSON.parse(data);
355 if (ag2.length > 1) {
358 ag2=ag2+(jd[key]["serviceName"]).trim()
367 if (checkFunctionFlag("ag3")) {
368 getSimCtr(LOCALHOST+AGENT_PORT+"/policy_types", 0, function(data, index) {
371 var jd=JSON.parse(data);
373 if (ag3.length > 0) {
376 ag3=ag3+jd[key].trim()
386 if (checkFunctionFlag("ag4")) {
387 getSimCtr(LOCALHOST+AGENT_PORT+"/policy_ids", 0, function(data, index) {
389 var jd=JSON.parse(data);
399 if (checkFunctionFlag("ag5")) {
400 getSimCtr(LOCALHOST+AGENT_PORT+"/rics", 0, function(data, index) {
402 var jd=JSON.parse(data);
412 fetchAllMetrics_pol();
417 function fetchAllMetrics_ecs() {
419 console.log("Fetching enrichment metrics - timer:" + refreshCount_ecs)
421 if (refreshCount_ecs < 0) {
422 refreshCount_ecs = -1
425 refreshCount_ecs = refreshCount_ecs - 1
429 if (checkFunctionFlag("ecs_stat")) {
430 getSimCtr(LOCALHOST+ECS_PORT+"/status", 0, function(data, index) {
436 var jd=JSON.parse(data);
438 ecs2=""+jd["no_of_producers"]
439 ecs3=""+jd["no_of_types"]
440 ecs4=""+jd["no_of_jobs"]
443 ecs1="error response"
444 ecs2="error response"
445 ecs3="error response"
446 ecs4="error response"
450 getSimCtr(LOCALHOST+ECS_PORT+"/ei-producer/v1/eitypes", 0, function(data, index) {
453 var jd=JSON.parse(data);
454 for(var i=0;i<jd.length;i++) {
455 if (ecs_types.length == 1) {
458 ecs_types=""+ecs_types+jd[i]+" "
462 ecs_types="error response"
466 getSimCtr(LOCALHOST+ECS_PORT+"/ei-producer/v1/eiproducers", 0, function(data, index) {
469 var jd=JSON.parse(data);
470 var tmp_ecs_producer_arr=new Array(jd.length)
471 for(var i=0;i<jd.length;i++) {
472 if (ecs_producers.length == 1) {
475 ecs_producers=""+ecs_producers+jd[i]+" "
476 tmp_ecs_producer_arr[i]=jd[i]
478 ecs_producer_arr = tmp_ecs_producer_arr
481 ecs_producers="error response"
482 ecs_producer_arr=new Array(0)
486 ecs_producer_type_arr = JSON.parse(JSON.stringify(ecs_producer_arr))
487 for(var x=0;x<ecs_producer_type_arr.length;x++) {
488 getSimCtr(LOCALHOST+ECS_PORT+"/ei-producer/v1/eiproducers/"+ecs_producer_type_arr[x], x, function(data, x) {
489 var row=""+ecs_producer_type_arr[x]+" : "
491 var jd=JSON.parse(data);
492 var jda=jd["supported_ei_types"]
493 for(var j=0;j<jda.length;j++) {
494 row=""+row+jda[j]["ei_type_identity"]+" "
496 ecs_producer_type_arr[x]=row
499 ecs_producer_type_arr=new Array(0)
504 ecs_producer_jobs_arr = JSON.parse(JSON.stringify(ecs_producer_arr))
505 for(var x=0;x<ecs_producer_jobs_arr.length;x++) {
506 getSimCtr(LOCALHOST+ECS_PORT+"/ei-producer/v1/eiproducers/"+ecs_producer_jobs_arr[x]+"/eijobs", x, function(data, x) {
507 var row=""+ecs_producer_jobs_arr[x]+" : "
509 var jd=JSON.parse(data);
510 for(var j=0;j<jd.length;j++) {
512 row=""+row+jda["ei_job_identity"]+"("+jda["ei_type_identity"]+") "
514 ecs_producer_jobs_arr[x]=row
517 ecs_producer_jobs_arr=new Array(0)
522 ecs_producer_status_arr = JSON.parse(JSON.stringify(ecs_producer_arr))
523 for(var x=0;x<ecs_producer_status_arr.length;x++) {
524 getSimCtr(LOCALHOST+ECS_PORT+"/ei-producer/v1/eiproducers/"+ecs_producer_status_arr[x]+"/status", x, function(data, x) {
525 var row=""+ecs_producer_status_arr[x]+" : "
527 var jd=JSON.parse(data);
528 row=""+row+jd["operational_state"]
529 ecs_producer_status_arr[x]=row
532 ecs_producer_status_arr=new Array(0)
536 clearFlag("ecs_stat")
538 if (checkFunctionFlag("prodstub_stat")) {
539 getSimCtr(LOCALHOST+PRODSTUB_PORT+"/status", x, function(data, x) {
540 var ctr2_map=new Map()
541 var ctr3_map=new Map()
546 ps_producer_type_arr=new Array()
547 ps_producer_jobs_arr=new Array()
548 ps_producer_delivery_arr=new Array()
553 var jp=JSON.parse(data);
554 for(var prod_name in jp) {
555 ctr2_map.set(prod_name, prod_name)
558 var row=""+prod_name+" : "
559 var rowj=""+prod_name+" : "
560 var rowd=""+prod_name+" : "
561 ps_producers += prod_name + " "
565 for(var i=0;i<ta.length;i++) {
566 ctr3_map.set(ta[i], ta[i])
569 } else if (ji == "supervision_response") {
570 } else if (ji == "supervision_counter") {
571 } else if (ji == "types") {
576 var job_data=jj[ji]["json"]
577 if (job_data != undefined) {
578 rowj += "("+job_data["ei_type_identity"]+")"
580 rowd += "("+jj[ji]["delivery_attempts"]+")"
583 ps_producer_type_arr[(ctr2-1)]=row
584 ps_producer_jobs_arr[(ctr2-1)]=rowj
585 ps_producer_delivery_arr[(ctr2-1)]=rowd
589 for(const [key, value] of ctr3_map.entries()) {
590 ps_types += key + " "
596 ps_producers="error response"
597 ps_types="error response"
598 ps_producer_type_arr=new Array()
599 ps_producer_jobs_arr=new Array()
600 ps_producer_delivery_arr=new Array()
606 clearFlag("prodstub_stat")
609 fetchAllMetrics_ecs();
615 app.get("/mon2",function(req, res){
617 console.log("Creating enrichment metrics - timer: " + refreshCount_ecs)
619 if (refreshCount_ecs < 0) {
621 fetchAllMetrics_ecs()
625 var summary=req.query.summary
627 if (summary == undefined) {
628 return res.redirect('/mon2?summary=false');
632 var htmlStr = "<!DOCTYPE html>" +
635 "<meta http-equiv=\"refresh\" content=\"2\">"+ //2 sec auto refresh
636 "<title>Enrichment coordinator service and producer stub</title>"+
639 "<font size=\"-3\" face=\"summary\">"
640 if (summary == "false") {
641 htmlStr=htmlStr+"<p>Set query param '?summary' to true to only show summary statistics</p>"
643 htmlStr=htmlStr+"<p>Set query param '?summary' to false to only show full statistics</p>"
645 htmlStr=htmlStr+"</font>" +
646 "<h3>Enrichment Coordinator Service</h3>" +
647 "<font face=\"monospace\">" +
648 "Status:..........." + formatDataRow(ecs1) + "<br>" +
649 "Producers:........" + formatDataRow(ecs2) + "<br>" +
650 "Types:............" + formatDataRow(ecs3) + "<br>" +
651 "Jobs:............." + formatDataRow(ecs4) + "<br>" +
653 if (summary == "false") {
656 "<font face=\"monospace\">" +
657 "Producer ids:....." + formatDataRow(ecs_producers) + "<br>" +
658 "Type ids:........." + formatDataRow(ecs_types) + "<br>" +
660 for(var i=0;i<ecs_producer_type_arr.length;i++) {
661 var tmp=ecs_producer_type_arr[i]
662 if (tmp != undefined) {
663 var s = "Producer types...." + formatDataRow(ecs_producer_type_arr[i]) + "<br>"
667 htmlStr=htmlStr+"<br>";
668 for(var i=0;i<ecs_producer_jobs_arr.length;i++) {
669 var tmp=ecs_producer_jobs_arr[i]
670 if (tmp != undefined) {
671 var s = "Producer jobs....." + formatDataRow(ecs_producer_jobs_arr[i]) + "<br>"
675 htmlStr=htmlStr+"<br>";
676 for(var i=0;i<ecs_producer_status_arr.length;i++) {
677 var tmp=ecs_producer_status_arr[i]
678 if (tmp != undefined) {
679 var s = "Producer status..." + formatDataRow(ecs_producer_status_arr[i]) + "<br>"
683 htmlStr=htmlStr+"<br>"+"<br>" +
687 "<h3>Producer stub</h3>" +
688 "<font face=\"monospace\">" +
689 "Producers:........" + formatDataRow(ps2) + "<br>" +
690 "Types:............" + formatDataRow(ps3) + "<br>" +
691 "Jobs:............." + formatDataRow(ps4) + "<br>" +
693 if (summary == "false") {
696 "<font face=\"monospace\">" +
697 "Producer ids:....." + formatDataRow(ps_producers) + "<br>" +
698 "Type ids:........." + formatDataRow(ps_types) + "<br>" +
700 for(var i=0;i<ps_producer_type_arr.length;i++) {
701 var tmp=ps_producer_type_arr[i]
702 if (tmp != undefined) {
703 var s = "Producer types...." + formatDataRow(ps_producer_type_arr[i]) + "<br>"
707 htmlStr=htmlStr+"<br>";
708 for(var i=0;i<ps_producer_jobs_arr.length;i++) {
709 var tmp=ps_producer_jobs_arr[i]
710 if (tmp != undefined) {
711 var s = "Producer jobs....." + formatDataRow(ps_producer_jobs_arr[i]) + "<br>"
715 htmlStr=htmlStr+"<br>";
716 for(var i=0;i<ps_producer_delivery_arr.length;i++) {
717 var tmp=ps_producer_delivery_arr[i]
718 if (tmp != undefined) {
719 var s = "Producer delivery." + formatDataRow(ps_producer_delivery_arr[i]) + "<br>"
731 // Monitor for policy management
732 app.get("/mon",function(req, res){
734 console.log("Creating policy metrics page " + refreshCount_pol)
736 if (refreshCount_pol < 0) {
738 fetchAllMetrics_pol()
742 var bn=req.query.basename
744 if (bn == undefined) {
746 return res.redirect('/mon?basename=ricsim');
752 var htmlStr = "<!DOCTYPE html>" +
755 "<meta http-equiv=\"refresh\" content=\"2\">"+ //2 sec auto refresh
756 "<title>Policy Agent and simulator monitor</title>"+
759 "<font size=\"-3\" face=\"monospace\">" +
760 "<p>Change basename in url if other ric sim prefix is used</p>" +
762 "<h3>Policy agent</h3>" +
763 "<font face=\"monospace\">" +
764 "Status:..............................." + formatDataRow(ag1) + "<br>" +
765 "Services:............................." + formatIdRowCompact(ag2) + "<br>" +
766 "Types:................................" + formatIdRowCompact(ag3) + "<br>" +
767 "Number of instances:.................." + formatDataRow(ag4) + "<br>" +
768 "Near-RT RICs:........................." + formatDataRow(ag5) + "<br>" +
770 "<h3>MR Stub interface</h3>" +
771 "<font face=\"monospace\">"+
772 "Submitted requests:............................" + formatDataRow(mr1) + "<br>" +
773 "Fetched requests:.............................." + formatDataRow(mr2) + "<br>" +
774 "Current requests waiting:......................" + formatDataRow(mr3) + "<br>" +
775 "Submitted responses:..........................." + formatDataRow(mr4) + "<br>" +
776 "Fetched responses.............................." + formatDataRow(mr5) + "<br>" +
777 "Current responses waiting......................" + formatDataRow(mr6) + "<br>" +
779 "<h3>Callback receiver</h3>" +
780 "<font face=\"monospace\">" +
781 "Callbacks received:..................." + formatDataRow(cr1) + "<br>" +
782 "Callbacks fetched:...................." + formatDataRow(cr2) + "<br>" +
783 "Number of waiting callback messages:.." + formatDataRow(cr3) + "<br>" +
785 "<h3>Near-RT RIC Simulators</h3>" +
786 "<font face=\"monospace\">"
788 htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35," ")
789 htmlStr=htmlStr+padding("Types", 10," ")
790 htmlStr=htmlStr+padding("Instances", 12," ")
791 htmlStr=htmlStr+padding("Data delivery", 12," ")+"<br>"
792 htmlStr=htmlStr+padding("",70,"=")+"<br>"
793 for(var simIndex=0;simIndex<simnames.length;simIndex++) {
794 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35," ");
795 htmlStr=htmlStr+padding(simvar2[simIndex],10," ")
796 htmlStr=htmlStr+padding(simvar1[simIndex],12 ," ")
797 htmlStr=htmlStr+padding(simvar6[simIndex],12," ")
798 htmlStr=htmlStr+"<br>";
801 htmlStr=htmlStr+"<br>";
802 htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35," ")
803 htmlStr=htmlStr+padding("Version", 20," ")
804 htmlStr=htmlStr+padding("Type-IDs", 10," ")+"<br>"
805 htmlStr=htmlStr+padding("",65,"=")+"<br>"
806 for(simIndex=0;simIndex<simnames.length;simIndex++) {
807 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35," ");
808 htmlStr=htmlStr+padding(simvar4[simIndex],20," ")
809 htmlStr=htmlStr+padding(formatIdRowCompact(simvar3[simIndex]),10," ")
810 htmlStr=htmlStr+"<br>";
813 htmlStr=htmlStr+"<br>";
814 htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35," ")
815 htmlStr=htmlStr+padding("Remote hosts", 50," ")+"<br>"
816 htmlStr=htmlStr+padding("",90,"=")+"<br>"
817 for(simIndex=0;simIndex<simnames.length;simIndex++) {
818 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35," ");
819 htmlStr=htmlStr+padding(simvar5[simIndex],50," ")
820 htmlStr=htmlStr+"<br>";
829 var httpServer = http.createServer(app);
831 httpServer.listen(httpPort);
832 console.log("Simulator monitor listening (http) at "+httpPort);
833 console.log("Open the web page on localhost:9999/mon to view the policy statistics page.")
834 console.log("Open the web page on localhost:9999/mon2 to view the enrichment statistics page.")