| /* |
| # ============LICENSE_START=============================================== |
| # Copyright (C) 2020 Nordix Foundation. 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. |
| # ============LICENSE_END================================================= |
| # |
| */ |
| |
| // Sim mon server - query the agent and the simulators for counters and other data |
| // Presents a web page on localhost:9999/mon |
| |
| var LOCALHOST="http://127.0.0.1:" |
| var MRSTUB_PORT="3905" |
| var AGENT_PORT="8081" |
| var CR_PORT="8090" |
| var http = require('http'); |
| |
| var express = require('express'); |
| var app = express(); |
| var fieldSize=32; |
| |
| var flagstore={} |
| |
| //I am alive |
| app.get("/",function(req, res){ |
| res.send("ok"); |
| }) |
| |
| //Get parameter valuue from other server |
| function getSimCtr(url, index, cb) { |
| var data = ''; |
| |
| //console.log("URL: "+ url + " - ") |
| try { |
| http.get(url, (resp) => { |
| // A chunk of data has been recieved. |
| resp.on('data', (chunk) => { |
| data += chunk; |
| }); |
| |
| // The whole response has been received. |
| resp.on('end', () => { |
| var code=resp.statusCode |
| if (code > 199 && code < 300) { |
| cb(data, index); |
| } else { |
| cb("not found", index); |
| } |
| }); |
| |
| }).on("error", (err) => { |
| console.log("Error: " + err.message); |
| cb("no response", index); |
| }); |
| } catch(err) { |
| cb("no response", index); |
| } |
| }; |
| |
| |
| //Format a comma separated list of data to a html-safe string with fixed fieldsizes |
| function formatDataRow(commaList) { |
| var str = ""; |
| var tmp=commaList.split(','); |
| for(var i=0;i<tmp.length;i++) { |
| var data=tmp[i]; |
| var len = fieldSize-data.length; |
| while(len>0) { |
| data = data+" "; |
| len--; |
| } |
| str=str+data+" "; |
| } |
| return str; |
| } |
| |
| //Format a comma separated list of ids to a html-safe string with fixed fieldsizes |
| function formatIdRow(commaList) { |
| var str = ""; |
| var tmp=commaList.split(','); |
| for(var i=0;i<tmp.length;i++) { |
| tmp[i] = tmp[i].trim(); |
| var data="<"+tmp[i]+">"; |
| var len = fieldSize+4-data.length; |
| while(len>0) { |
| data = data+" "; |
| len--; |
| } |
| str=str+data+" "; |
| } |
| return str; |
| } |
| |
| //Format a list of ids to a html-safe string in compact format |
| function formatIdRowCompact(commaList) { |
| if (commaList == undefined) { |
| commaList= ""; |
| } |
| var str = ""; |
| var tmp=commaList.split(','); |
| for(var i=0;i<tmp.length;i++) { |
| tmp[i] = tmp[i].trim(); |
| var data="<"+tmp[i]+">"; |
| str=str+data+" "; |
| } |
| return str; |
| } |
| |
| //Pad a string upto a certain size using a pad string |
| function padding(val, fieldSize, pad) { |
| var s=""+val; |
| for(var i=s.length;i<fieldSize;i++) { |
| s=s+pad |
| } |
| return s; |
| } |
| |
| //Function to check if the previous call has returned, if so return true, if not return false |
| //For preventing multiple calls to slow containers. |
| function checkFunctionFlag(flag) { |
| if (flagstore.hasOwnProperty(flag)) { |
| if (flagstore[flag] == 0) { |
| flagstore[flag]=1 |
| return true |
| } else if (flagstore[flag] > 10) { |
| //Reset flag after ten attempts |
| console.log("Force release flag "+flag) |
| flagstore[flag]=1 |
| return true |
| } else { |
| //Previous call not returned |
| console.log("Flag not available "+flag) |
| flagstore[flag]=flagstore[flag]+1 |
| return false |
| } |
| } else { |
| flagstore[flag]=1 |
| return true |
| } |
| } |
| //Clear flag for parameter |
| function clearFlag(flag) { |
| flagstore[flag]=0 |
| } |
| |
| //Status variables, for parameters values fetched from other simulators |
| var mr1="", mr2="", mr3="", mr4="", mr5="", mr6=""; |
| |
| //Status variables for agent |
| var ag1="" |
| var ag2="" |
| var ag3="" |
| var ag4="" |
| |
| //Status variables for callback receiver |
| var cr1="" |
| var cr2="" |
| var cr3="" |
| |
| |
| //Container names and ports of the ric simulator |
| var simnames=[] |
| var simports=[] |
| |
| //Status variables for each ric simulator |
| var simvar1=[] |
| var simvar2=[] |
| var simvar3=[] |
| var simvar4=[] |
| var simvar5=[] |
| |
| //Counts the number of get request for the html page |
| var getCtr=0 |
| |
| var refreshInterval=4000 |
| |
| var ricbasename="ricsim" |
| |
| function fetchAllMetrics() { |
| setTimeout(() => { |
| |
| console.log("Fetching all metics data") |
| if (refreshInterval < 20000) { |
| refreshInterval+=100 |
| } |
| if (getCtr%3 == 0) { |
| //Extract the port numbers from the running simulators, for every 3 calls |
| const { exec } = require('child_process'); |
| exec('docker ps --filter "name='+ricbasename+'" --format "{{.Names}} {{.Ports}}" | sed s/0.0.0.0:// | cut -d \'>\' -f1 | sed \'s/[[-]]*$//\'', (err, stdout, stderr) => { |
| |
| var simulators = "" |
| simulators=`${stdout}`.replace(/(\r\n|\n|\r)/gm," "); |
| simulators=simulators.trim(); |
| var sims=simulators.split(" ") |
| simnames=[] |
| simports=[] |
| for(i=0;i<sims.length;i=i+2) { |
| simnames[i/2]=sims[i] |
| simports[i/2]=sims[i+1] |
| } |
| }); |
| } |
| getCtr=getCtr+1 |
| |
| //Get metric values from the simulators |
| for(var index=0;index<simnames.length;index++) { |
| |
| if (checkFunctionFlag("simvar1_"+index)) { |
| getSimCtr(LOCALHOST+simports[index]+"/counter/num_instances", index, function(data, index) { |
| simvar1[index] = data; |
| clearFlag("simvar1_"+index) |
| }); |
| } |
| if (checkFunctionFlag("simvar2_"+index)) { |
| getSimCtr(LOCALHOST+simports[index]+"/counter/num_types", index, function(data,index) { |
| simvar2[index] = data; |
| clearFlag("simvar2_"+index) |
| }); |
| } |
| if (checkFunctionFlag("simvar3_"+index)) { |
| getSimCtr(LOCALHOST+simports[index]+"/policytypes", index, function(data,index) { |
| data=data.replace(/\[/g,''); |
| data=data.replace(/\]/g,''); |
| data=data.replace(/ /g,''); |
| data=data.replace(/\"/g,''); |
| simvar3[index] = data; |
| clearFlag("simvar3_"+index) |
| }); |
| } |
| if (checkFunctionFlag("simvar4_"+index)) { |
| getSimCtr(LOCALHOST+simports[index]+"/counter/interface", index, function(data,index) { |
| simvar4[index] = data; |
| clearFlag("simvar4_"+index) |
| }); |
| } |
| if (checkFunctionFlag("simvar5_"+index)) { |
| getSimCtr(LOCALHOST+simports[index]+"/counter/remote_hosts", index, function(data,index) { |
| simvar5[index] = data; |
| clearFlag("simvar5_"+index) |
| }); |
| } |
| } |
| |
| //MR - get metrics values from the MR stub |
| if (checkFunctionFlag("mr1")) { |
| getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_submitted", 0, function(data, index) { |
| mr1 = data; |
| clearFlag("mr1") |
| }); |
| } |
| if (checkFunctionFlag("mr2")) { |
| getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_fetched", 0, function(data, index) { |
| mr2 = data; |
| clearFlag("mr2") |
| }); |
| } |
| if (checkFunctionFlag("mr3")) { |
| getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_requests", 0, function(data, index) { |
| mr3 = data; |
| clearFlag("mr3") |
| }); |
| } |
| if (checkFunctionFlag("mr4")) { |
| getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_submitted", 0, function(data, index) { |
| mr4 = data; |
| clearFlag("mr4") |
| }); |
| } |
| if (checkFunctionFlag("mr5")) { |
| getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_fetched", 0, function(data, index) { |
| mr5 = data; |
| clearFlag("mr5") |
| }); |
| } |
| if (checkFunctionFlag("mr6")) { |
| getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_responses", 0, function(data, index) { |
| mr6 = data; |
| clearFlag("mr6") |
| }); |
| } |
| |
| //CR - get metrics values from the callbackreceiver |
| if (checkFunctionFlag("cr1")) { |
| getSimCtr(LOCALHOST+CR_PORT+"/counter/received_callbacks", 0, function(data, index) { |
| cr1 = data; |
| clearFlag("cr1") |
| }); |
| } |
| if (checkFunctionFlag("cr2")) { |
| getSimCtr(LOCALHOST+CR_PORT+"/counter/fetched_callbacks", 0, function(data, index) { |
| cr2 = data; |
| clearFlag("cr2") |
| }); |
| } |
| if (checkFunctionFlag("cr3")) { |
| getSimCtr(LOCALHOST+CR_PORT+"/counter/current_messages", 0, function(data, index) { |
| cr3 = data; |
| clearFlag("cr3") |
| }); |
| } |
| //Agent - more get metrics from the agent |
| if (checkFunctionFlag("ag1")) { |
| getSimCtr(LOCALHOST+AGENT_PORT+"/status", 0, function(data, index) { |
| ag1 = data; |
| clearFlag("ag1") |
| }); |
| } |
| if (checkFunctionFlag("ag2")) { |
| getSimCtr(LOCALHOST+AGENT_PORT+"/services", 0, function(data, index) { |
| ag2=""; |
| try { |
| var jd=JSON.parse(data); |
| for(var key in jd) { |
| if (ag2.length > 1) { |
| ag2=ag2+", " |
| } |
| ag2=ag2+(jd[key]["serviceName"]).trim() |
| } |
| } |
| catch (err) { |
| ag2=data |
| } |
| clearFlag("ag2") |
| }); |
| } |
| if (checkFunctionFlag("ag3")) { |
| getSimCtr(LOCALHOST+AGENT_PORT+"/policy_types", 0, function(data, index) { |
| ag3=""; |
| try { |
| var jd=JSON.parse(data); |
| for(var key in jd) { |
| if (ag3.length > 0) { |
| ag3=ag3+", " |
| } |
| ag3=ag3+jd[key].trim() |
| } |
| } |
| catch (err) { |
| ag3="" |
| } |
| clearFlag("ag3") |
| }); |
| } |
| if (checkFunctionFlag("ag4")) { |
| getSimCtr(LOCALHOST+AGENT_PORT+"/policy_ids", 0, function(data, index) { |
| ag4="" |
| try { |
| var jd=JSON.parse(data); |
| ag4=""+jd.length |
| } |
| catch (err) { |
| ag4="" |
| } |
| clearFlag("ag4") |
| }); |
| } |
| |
| |
| fetchAllMetrics(); |
| }, refreshInterval) |
| } |
| |
| fetchAllMetrics(); |
| |
| setInterval(() => { |
| console.log("Setting interval "+refreshInterval+"ms") |
| }, refreshInterval) |
| |
| app.get("/mon",function(req, res){ |
| |
| var bn=req.query.basename |
| |
| if (bn == undefined) { |
| getCtr=0 |
| return res.redirect('/mon?basename=ricsim'); |
| } else { |
| ricbasename=bn |
| } |
| |
| refreshInterval=2000 |
| |
| //Build web page |
| var htmlStr = "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "<meta http-equiv=\"refresh\" content=\"2\">"+ //2 sec auto refresh |
| "<title>Policy Agent and simulator monitor</title>"+ |
| "</head>" + |
| "<body>" + |
| "<font size=\"-3\" face=\"monospace\">" + |
| "<p>Change basename in url if other ric sim prefix is used</p>" + |
| "</font>" + |
| "<h3>Policy agent</h3>" + |
| "<font face=\"monospace\">" + |
| "Status:..............................." + formatDataRow(ag1) + "<br>" + |
| "Services:............................." + formatIdRowCompact(ag2) + "<br>" + |
| "Types:................................" + formatIdRowCompact(ag3) + "<br>" + |
| "Number of instances:.................." + formatDataRow(ag4) + "<br>" + |
| "</font>" + |
| "<h3>MR Stub interface</h3>" + |
| "<font face=\"monospace\">"+ |
| "Submitted requests:............................" + formatDataRow(mr1) + "<br>" + |
| "Fetched requests:.............................." + formatDataRow(mr2) + "<br>" + |
| "Current requests waiting:......................" + formatDataRow(mr3) + "<br>" + |
| "Submitted responses:..........................." + formatDataRow(mr4) + "<br>" + |
| "Fetched responses.............................." + formatDataRow(mr5) + "<br>" + |
| "Current responses waiting......................" + formatDataRow(mr6) + "<br>" + |
| "</font>"+ |
| "<h3>Callback receiver</h3>" + |
| "<font face=\"monospace\">" + |
| "Callbacks received:..................." + formatDataRow(cr1) + "<br>" + |
| "Callbacks fetched:...................." + formatDataRow(cr2) + "<br>" + |
| "Number of waiting callback messages:.." + formatDataRow(cr3) + "<br>" + |
| "</font>" + |
| "<h3>Near-RT RIC Simulators</h3>" + |
| "<font face=\"monospace\">" |
| |
| htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35," ") |
| htmlStr=htmlStr+padding("Types", 10," ") |
| htmlStr=htmlStr+padding("Instances", 10," ")+"<br>" |
| htmlStr=htmlStr+padding("",55,"=")+"<br>" |
| for(var simIndex=0;simIndex<simnames.length;simIndex++) { |
| htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35," "); |
| htmlStr=htmlStr+padding(simvar2[simIndex],10," ") |
| htmlStr=htmlStr+padding(simvar1[simIndex],10," ") |
| htmlStr=htmlStr+"<br>"; |
| } |
| |
| htmlStr=htmlStr+"<br>"; |
| htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35," ") |
| htmlStr=htmlStr+padding("Version", 20," ") |
| htmlStr=htmlStr+padding("Type-IDs", 10," ")+"<br>" |
| htmlStr=htmlStr+padding("",65,"=")+"<br>" |
| for(simIndex=0;simIndex<simnames.length;simIndex++) { |
| htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35," "); |
| htmlStr=htmlStr+padding(simvar4[simIndex],20," ") |
| htmlStr=htmlStr+padding(formatIdRowCompact(simvar3[simIndex]),10," ") |
| htmlStr=htmlStr+"<br>"; |
| } |
| |
| htmlStr=htmlStr+"<br>"; |
| htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35," ") |
| htmlStr=htmlStr+padding("Remote hosts", 50," ")+"<br>" |
| htmlStr=htmlStr+padding("",90,"=")+"<br>" |
| for(simIndex=0;simIndex<simnames.length;simIndex++) { |
| htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35," "); |
| htmlStr=htmlStr+padding(simvar5[simIndex],50," ") |
| htmlStr=htmlStr+"<br>"; |
| } |
| |
| htmlStr=htmlStr+ |
| "</body>" + |
| "</html>"; |
| res.send(htmlStr); |
| }) |
| |
| var httpServer = http.createServer(app); |
| var httpPort=9999; |
| httpServer.listen(httpPort); |
| console.log("Simulator monitor listening (http) at "+httpPort); |
| console.log("Open the web page on localhost:9999/mon to view the statistics page.") |