Merge changes Id5e8969d,I56437e93
[nonrtric.git] / test / simulator-group / sim-monitor.js
1 /*
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
8 #
9 #       http://www.apache.org/licenses/LICENSE-2.0
10 #
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=================================================
17 #
18 */
19
20 // Sim mon server - query the agent and the simulators for counters and other data
21 // Presents a web page on localhost:9999/mon
22
23 var LOCALHOST="http://127.0.0.1:"
24 var MRSTUB_PORT="3905"
25 var AGENT_PORT="8081"
26 var CR_PORT="8090"
27 var http = require('http');
28
29 var express = require('express');
30 var app = express();
31 var fieldSize=32;
32
33 var flagstore={}
34
35 //I am alive
36 app.get("/",function(req, res){
37         res.send("ok");
38 })
39
40 //Get parameter valuue from other server
41 function getSimCtr(url, index, cb) {
42     var data = '';
43
44     //console.log("URL: "+ url + " - ")
45     try {
46         http.get(url, (resp) => {
47             // A chunk of data has been recieved.
48             resp.on('data', (chunk) => {
49                 data += chunk;
50             });
51
52             // The whole response has been received.
53             resp.on('end', () => {
54                 var code=resp.statusCode
55                 if (code > 199 && code < 300) {
56                     cb(data, index);
57                 } else {
58                     cb("not found", index);
59                 }
60             });
61
62         }).on("error", (err) => {
63             console.log("Error: " + err.message);
64             cb("no response", index);
65         });
66     } catch(err) {
67         cb("no response", index);
68     }
69 };
70
71
72 //Format a comma separated list of data to a html-safe string with fixed fieldsizes
73 function formatDataRow(commaList) {
74         var str = "";
75         var tmp=commaList.split(',');
76     for(var i=0;i<tmp.length;i++) {
77         var data=tmp[i];
78         var len = fieldSize-data.length;
79         while(len>0) {
80             data = data+"&nbsp;";
81             len--;
82         }
83         str=str+data+"&nbsp;&nbsp;&nbsp;";
84      }
85         return str;
86 }
87
88 //Format a comma separated list of ids to a html-safe string with fixed fieldsizes
89 function formatIdRow(commaList) {
90         var str = "";
91         var tmp=commaList.split(',');
92     for(var i=0;i<tmp.length;i++) {
93         tmp[i] = tmp[i].trim();
94         var data="&lt"+tmp[i]+"&gt";
95         var len = fieldSize+4-data.length;
96         while(len>0) {
97             data = data+"&nbsp;";
98             len--;
99         }
100         str=str+data+"&nbsp;&nbsp;&nbsp;";
101     }
102         return str;
103 }
104
105 //Format a list of ids to a html-safe string in compact format
106 function formatIdRowCompact(commaList) {
107     if (commaList == undefined) {
108         commaList= "";
109     }
110         var str = "";
111         var tmp=commaList.split(',');
112     for(var i=0;i<tmp.length;i++) {
113         tmp[i] = tmp[i].trim();
114         var data="&lt"+tmp[i]+"&gt";
115         str=str+data+"&nbsp;";
116     }
117         return str;
118 }
119
120 //Pad a string upto a certain size using a pad string
121 function padding(val, fieldSize, pad) {
122         var s=""+val;
123         for(var i=s.length;i<fieldSize;i++) {
124                 s=s+pad
125         }
126         return s;
127 }
128
129 //Function to check if the previous call has returned, if so return true, if not return false
130 //For preventing multiple calls to slow containers.
131 function checkFunctionFlag(flag) {
132     if (flagstore.hasOwnProperty(flag)) {
133         if (flagstore[flag] == 0) {
134             flagstore[flag]=1
135             return true
136         } else if (flagstore[flag] > 10) {
137             //Reset flag after ten attempts
138             console.log("Force release flag "+flag)
139             flagstore[flag]=1
140             return true
141         } else {
142             //Previous call not returned
143             console.log("Flag not available "+flag)
144             flagstore[flag]=flagstore[flag]+1
145             return false
146         }
147     } else {
148         flagstore[flag]=1
149         return true
150     }
151 }
152 //Clear flag for parameter
153 function clearFlag(flag) {
154     flagstore[flag]=0
155 }
156
157 //Status variables, for parameters values fetched from other simulators
158 var mr1="", mr2="", mr3="", mr4="", mr5="", mr6="";
159
160 //Status variables for agent
161 var ag1=""
162 var ag2=""
163 var ag3=""
164 var ag4=""
165
166 //Status variables for callback receiver
167 var cr1=""
168 var cr2=""
169 var cr3=""
170
171
172 //Container names and ports of the ric simulator
173 var simnames=[]
174 var simports=[]
175
176 //Status variables for each ric simulator
177 var simvar1=[]
178 var simvar2=[]
179 var simvar3=[]
180 var simvar4=[]
181 var simvar5=[]
182
183 //Counts the number of get request for the html page
184 var getCtr=0
185
186 var refreshInterval=4000
187
188 var ricbasename="ricsim"
189
190 function fetchAllMetrics() {
191     setTimeout(() => {
192
193         console.log("Fetching all metics data")
194         if (refreshInterval < 20000) {
195             refreshInterval+=100
196         }
197         if (getCtr%3 == 0) {
198             //Extract the port numbers from the running simulators, for every 3 calls
199             const { exec } = require('child_process');
200             exec('docker ps --filter "name='+ricbasename+'" --format "{{.Names}} {{.Ports}}" | sed s/0.0.0.0:// | cut -d \'>\' -f1 | sed \'s/[[-]]*$//\'', (err, stdout, stderr) => {
201
202                 var simulators = ""
203                 simulators=`${stdout}`.replace(/(\r\n|\n|\r)/gm," ");
204                 simulators=simulators.trim();
205                 var sims=simulators.split(" ")
206                 simnames=[]
207                 simports=[]
208                 for(i=0;i<sims.length;i=i+2) {
209                     simnames[i/2]=sims[i]
210                     simports[i/2]=sims[i+1]
211                 }
212             });
213         }
214         getCtr=getCtr+1
215
216         //Get metric values from the simulators
217         for(var index=0;index<simnames.length;index++) {
218
219             if (checkFunctionFlag("simvar1_"+index)) {
220                 getSimCtr(LOCALHOST+simports[index]+"/counter/num_instances", index, function(data, index) {
221                     simvar1[index] = data;
222                     clearFlag("simvar1_"+index)
223                 });
224             }
225             if (checkFunctionFlag("simvar2_"+index)) {
226                 getSimCtr(LOCALHOST+simports[index]+"/counter/num_types", index, function(data,index) {
227                     simvar2[index] = data;
228                     clearFlag("simvar2_"+index)
229                 });
230             }
231             if (checkFunctionFlag("simvar3_"+index)) {
232                 getSimCtr(LOCALHOST+simports[index]+"/policytypes", index, function(data,index) {
233                     data=data.replace(/\[/g,'');
234                     data=data.replace(/\]/g,'');
235                     data=data.replace(/ /g,'');
236                     data=data.replace(/\"/g,'');
237                     simvar3[index] = data;
238                     clearFlag("simvar3_"+index)
239                 });
240             }
241             if (checkFunctionFlag("simvar4_"+index)) {
242                 getSimCtr(LOCALHOST+simports[index]+"/counter/interface", index, function(data,index) {
243                     simvar4[index] = data;
244                     clearFlag("simvar4_"+index)
245                 });
246             }
247             if (checkFunctionFlag("simvar5_"+index)) {
248                 getSimCtr(LOCALHOST+simports[index]+"/counter/remote_hosts", index, function(data,index) {
249                     simvar5[index] = data;
250                     clearFlag("simvar5_"+index)
251                 });
252             }
253         }
254
255         //MR - get metrics values from the MR stub
256         if (checkFunctionFlag("mr1")) {
257             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_submitted", 0, function(data, index) {
258                 mr1 = data;
259                 clearFlag("mr1")
260             });
261         }
262         if (checkFunctionFlag("mr2")) {
263             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_fetched", 0, function(data, index) {
264                 mr2 = data;
265                 clearFlag("mr2")
266             });
267         }
268         if (checkFunctionFlag("mr3")) {
269             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_requests", 0, function(data, index) {
270                 mr3 = data;
271                 clearFlag("mr3")
272             });
273         }
274         if (checkFunctionFlag("mr4")) {
275             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_submitted", 0, function(data, index) {
276                 mr4 = data;
277                 clearFlag("mr4")
278             });
279         }
280         if (checkFunctionFlag("mr5")) {
281             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_fetched", 0, function(data, index) {
282                 mr5 = data;
283                 clearFlag("mr5")
284             });
285         }
286         if (checkFunctionFlag("mr6")) {
287             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_responses", 0, function(data, index) {
288                 mr6 = data;
289                 clearFlag("mr6")
290             });
291         }
292
293         //CR - get metrics values from the callbackreceiver
294         if (checkFunctionFlag("cr1")) {
295             getSimCtr(LOCALHOST+CR_PORT+"/counter/received_callbacks", 0, function(data, index) {
296                 cr1 = data;
297                 clearFlag("cr1")
298             });
299         }
300         if (checkFunctionFlag("cr2")) {
301             getSimCtr(LOCALHOST+CR_PORT+"/counter/fetched_callbacks", 0, function(data, index) {
302                 cr2 = data;
303                 clearFlag("cr2")
304             });
305         }
306         if (checkFunctionFlag("cr3")) {
307             getSimCtr(LOCALHOST+CR_PORT+"/counter/current_messages", 0, function(data, index) {
308                 cr3 = data;
309                 clearFlag("cr3")
310             });
311         }
312         //Agent - more get metrics from the agent
313         if (checkFunctionFlag("ag1")) {
314             getSimCtr(LOCALHOST+AGENT_PORT+"/status", 0, function(data, index) {
315                 ag1 = data;
316                 clearFlag("ag1")
317             });
318         }
319         if (checkFunctionFlag("ag2")) {
320             getSimCtr(LOCALHOST+AGENT_PORT+"/services", 0, function(data, index) {
321                 ag2="";
322                 try {
323                     var jd=JSON.parse(data);
324                     for(var key in jd) {
325                         if (ag2.length > 1) {
326                             ag2=ag2+", "
327                         }
328                         ag2=ag2+(jd[key]["serviceName"]).trim()
329                     }
330                 }
331                 catch (err) {
332                     ag2=data
333                 }
334                 clearFlag("ag2")
335             });
336         }
337         if (checkFunctionFlag("ag3")) {
338             getSimCtr(LOCALHOST+AGENT_PORT+"/policy_types", 0, function(data, index) {
339                 ag3="";
340                 try {
341                     var jd=JSON.parse(data);
342                     for(var key in jd) {
343                         if (ag3.length > 0) {
344                             ag3=ag3+", "
345                         }
346                         ag3=ag3+jd[key].trim()
347                     }
348                 }
349                 catch (err) {
350                     ag3=""
351                 }
352                 clearFlag("ag3")
353             });
354         }
355         if (checkFunctionFlag("ag4")) {
356             getSimCtr(LOCALHOST+AGENT_PORT+"/policy_ids", 0, function(data, index) {
357                 ag4=""
358                 try {
359                     var jd=JSON.parse(data);
360                     ag4=""+jd.length
361                 }
362                 catch (err) {
363                     ag4=""
364                 }
365                 clearFlag("ag4")
366             });
367         }
368
369
370         fetchAllMetrics();
371     }, refreshInterval)
372 }
373
374 fetchAllMetrics();
375
376 setInterval(() => {
377     console.log("Setting interval "+refreshInterval+"ms")
378 }, refreshInterval)
379
380 app.get("/mon",function(req, res){
381
382     var bn=req.query.basename
383
384     if (bn == undefined) {
385         getCtr=0
386         return res.redirect('/mon?basename=ricsim');
387     } else {
388         ricbasename=bn
389     }
390
391     refreshInterval=2000
392
393   //Build web page
394         var htmlStr = "<!DOCTYPE html>" +
395           "<html>" +
396           "<head>" +
397             "<meta http-equiv=\"refresh\" content=\"2\">"+  //2 sec auto refresh
398             "<title>Policy Agent and simulator monitor</title>"+
399             "</head>" +
400             "<body>" +
401             "<font size=\"-3\" face=\"monospace\">" +
402             "<p>Change basename in url if other ric sim prefix is used</p>" +
403             "</font>" +
404             "<h3>Policy agent</h3>" +
405             "<font face=\"monospace\">" +
406             "Status:..............................." + formatDataRow(ag1) + "<br>" +
407             "Services:............................." + formatIdRowCompact(ag2) + "<br>" +
408             "Types:................................" + formatIdRowCompact(ag3) + "<br>" +
409             "Number of instances:.................." + formatDataRow(ag4) + "<br>" +
410             "</font>" +
411             "<h3>MR Stub interface</h3>" +
412             "<font face=\"monospace\">"+
413             "Submitted requests:............................" + formatDataRow(mr1) + "<br>" +
414             "Fetched requests:.............................." + formatDataRow(mr2) + "<br>" +
415             "Current requests waiting:......................" + formatDataRow(mr3) + "<br>" +
416             "Submitted responses:..........................." + formatDataRow(mr4) + "<br>" +
417             "Fetched responses.............................." + formatDataRow(mr5) + "<br>" +
418             "Current responses waiting......................" + formatDataRow(mr6) + "<br>" +
419             "</font>"+
420             "<h3>Callback receiver</h3>" +
421             "<font face=\"monospace\">" +
422             "Callbacks received:..................." + formatDataRow(cr1) + "<br>" +
423             "Callbacks fetched:...................." + formatDataRow(cr2) + "<br>" +
424             "Number of waiting callback messages:.." + formatDataRow(cr3) + "<br>" +
425             "</font>" +
426             "<h3>Near-RT RIC Simulators</h3>" +
427             "<font face=\"monospace\">"
428
429             htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35,"&nbsp;")
430             htmlStr=htmlStr+padding("Types", 10,"&nbsp;")
431             htmlStr=htmlStr+padding("Instances", 10,"&nbsp;")+"<br>"
432             htmlStr=htmlStr+padding("",55,"=")+"<br>"
433             for(var simIndex=0;simIndex<simnames.length;simIndex++) {
434                 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35,"&nbsp;");
435                 htmlStr=htmlStr+padding(simvar2[simIndex],10,"&nbsp;")
436                 htmlStr=htmlStr+padding(simvar1[simIndex],10,"&nbsp;")
437                 htmlStr=htmlStr+"<br>";
438             }
439
440             htmlStr=htmlStr+"<br>";
441             htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35,"&nbsp;")
442             htmlStr=htmlStr+padding("Version", 20,"&nbsp;")
443             htmlStr=htmlStr+padding("Type-IDs", 10,"&nbsp;")+"<br>"
444             htmlStr=htmlStr+padding("",65,"=")+"<br>"
445             for(simIndex=0;simIndex<simnames.length;simIndex++) {
446                 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35,"&nbsp;");
447                 htmlStr=htmlStr+padding(simvar4[simIndex],20,"&nbsp;")
448                 htmlStr=htmlStr+padding(formatIdRowCompact(simvar3[simIndex]),10,"&nbsp;")
449                 htmlStr=htmlStr+"<br>";
450             }
451
452             htmlStr=htmlStr+"<br>";
453             htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35,"&nbsp;")
454             htmlStr=htmlStr+padding("Remote hosts", 50,"&nbsp;")+"<br>"
455             htmlStr=htmlStr+padding("",90,"=")+"<br>"
456             for(simIndex=0;simIndex<simnames.length;simIndex++) {
457                 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35,"&nbsp;");
458                 htmlStr=htmlStr+padding(simvar5[simIndex],50,"&nbsp;")
459                 htmlStr=htmlStr+"<br>";
460             }
461
462             htmlStr=htmlStr+
463            "</body>" +
464           "</html>";
465         res.send(htmlStr);
466 })
467
468 var httpServer = http.createServer(app);
469 var httpPort=9999;
470 httpServer.listen(httpPort);
471 console.log("Simulator monitor listening (http) at "+httpPort);
472 console.log("Open the web page on localhost:9999/mon to view the statistics page.")