Merge "Make assertions of log messages better"
[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 function fetchAllMetrics() {
189     setTimeout(() => {
190
191         console.log("Fetching all metics data")
192         if (refreshInterval < 20000) {
193             refreshInterval+=100
194         }
195         if (getCtr%3 == 0) {
196             //Extract the port numbers from the running simulators, for every 3 calls
197             const { exec } = require('child_process');
198             exec('docker ps --filter "name=ricsim" --format "{{.Names}} {{.Ports}}" | sed s/0.0.0.0:// | cut -d \'>\' -f1 | sed \'s/[[-]]*$//\'', (err, stdout, stderr) => {
199
200                 var simulators = ""
201                 simulators=`${stdout}`.replace(/(\r\n|\n|\r)/gm," ");
202                 simulators=simulators.trim();
203                 var sims=simulators.split(" ")
204                 simnames=[]
205                 simports=[]
206                 for(i=0;i<sims.length;i=i+2) {
207                     simnames[i/2]=sims[i]
208                     simports[i/2]=sims[i+1]
209                 }
210             });
211         }
212         getCtr=getCtr+1
213
214         //Get metric values from the simulators
215         for(var index=0;index<simnames.length;index++) {
216
217             if (checkFunctionFlag("simvar1_"+index)) {
218                 getSimCtr(LOCALHOST+simports[index]+"/counter/num_instances", index, function(data, index) {
219                     simvar1[index] = data;
220                     clearFlag("simvar1_"+index)
221                 });
222             }
223             if (checkFunctionFlag("simvar2_"+index)) {
224                 getSimCtr(LOCALHOST+simports[index]+"/counter/num_types", index, function(data,index) {
225                     simvar2[index] = data;
226                     clearFlag("simvar2_"+index)
227                 });
228             }
229             if (checkFunctionFlag("simvar3_"+index)) {
230                 getSimCtr(LOCALHOST+simports[index]+"/policytypes", index, function(data,index) {
231                     data=data.replace(/\[/g,'');
232                     data=data.replace(/\]/g,'');
233                     data=data.replace(/ /g,'');
234                     data=data.replace(/\"/g,'');
235                     simvar3[index] = data;
236                     clearFlag("simvar3_"+index)
237                 });
238             }
239             if (checkFunctionFlag("simvar4_"+index)) {
240                 getSimCtr(LOCALHOST+simports[index]+"/counter/interface", index, function(data,index) {
241                     simvar4[index] = data;
242                     clearFlag("simvar4_"+index)
243                 });
244             }
245             if (checkFunctionFlag("simvar5_"+index)) {
246                 getSimCtr(LOCALHOST+simports[index]+"/counter/remote_hosts", index, function(data,index) {
247                     simvar5[index] = data;
248                     clearFlag("simvar5_"+index)
249                 });
250             }
251         }
252
253         //MR - get metrics values from the MR stub
254         if (checkFunctionFlag("mr1")) {
255             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_submitted", 0, function(data, index) {
256                 mr1 = data;
257                 clearFlag("mr1")
258             });
259         }
260         if (checkFunctionFlag("mr2")) {
261             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/requests_fetched", 0, function(data, index) {
262                 mr2 = data;
263                 clearFlag("mr2")
264             });
265         }
266         if (checkFunctionFlag("mr3")) {
267             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_requests", 0, function(data, index) {
268                 mr3 = data;
269                 clearFlag("mr3")
270             });
271         }
272         if (checkFunctionFlag("mr4")) {
273             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_submitted", 0, function(data, index) {
274                 mr4 = data;
275                 clearFlag("mr4")
276             });
277         }
278         if (checkFunctionFlag("mr5")) {
279             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/responses_fetched", 0, function(data, index) {
280                 mr5 = data;
281                 clearFlag("mr5")
282             });
283         }
284         if (checkFunctionFlag("mr6")) {
285             getSimCtr(LOCALHOST+MRSTUB_PORT+"/counter/current_responses", 0, function(data, index) {
286                 mr6 = data;
287                 clearFlag("mr6")
288             });
289         }
290
291         //CR - get metrics values from the callbackreceiver
292         if (checkFunctionFlag("cr1")) {
293             getSimCtr(LOCALHOST+CR_PORT+"/counter/received_callbacks", 0, function(data, index) {
294                 cr1 = data;
295                 clearFlag("cr1")
296             });
297         }
298         if (checkFunctionFlag("cr2")) {
299             getSimCtr(LOCALHOST+CR_PORT+"/counter/fetched_callbacks", 0, function(data, index) {
300                 cr2 = data;
301                 clearFlag("cr2")
302             });
303         }
304         if (checkFunctionFlag("cr3")) {
305             getSimCtr(LOCALHOST+CR_PORT+"/counter/current_messages", 0, function(data, index) {
306                 cr3 = data;
307                 clearFlag("cr3")
308             });
309         }
310         //Agent - more get metrics from the agent
311         if (checkFunctionFlag("ag1")) {
312             getSimCtr(LOCALHOST+AGENT_PORT+"/status", 0, function(data, index) {
313                 ag1 = data;
314                 clearFlag("ag1")
315             });
316         }
317         if (checkFunctionFlag("ag2")) {
318             getSimCtr(LOCALHOST+AGENT_PORT+"/services", 0, function(data, index) {
319                 ag2="";
320                 try {
321                     var jd=JSON.parse(data);
322                     for(var key in jd) {
323                         if (ag2.length > 1) {
324                             ag2=ag2+", "
325                         }
326                         ag2=ag2+(jd[key]["serviceName"]).trim()
327                     }
328                 }
329                 catch (err) {
330                     ag2=data
331                 }
332                 clearFlag("ag2")
333             });
334         }
335         if (checkFunctionFlag("ag3")) {
336             getSimCtr(LOCALHOST+AGENT_PORT+"/policy_types", 0, function(data, index) {
337                 ag3="";
338                 try {
339                     var jd=JSON.parse(data);
340                     for(var key in jd) {
341                         if (ag3.length > 0) {
342                             ag3=ag3+", "
343                         }
344                         ag3=ag3+jd[key].trim()
345                     }
346                 }
347                 catch (err) {
348                     ag3=""
349                 }
350                 clearFlag("ag3")
351             });
352         }
353         if (checkFunctionFlag("ag4")) {
354             getSimCtr(LOCALHOST+AGENT_PORT+"/policy_ids", 0, function(data, index) {
355                 ag4=""
356                 try {
357                     var jd=JSON.parse(data);
358                     ag4=""+jd.length
359                 }
360                 catch (err) {
361                     ag4=""
362                 }
363                 clearFlag("ag4")
364             });
365         }
366
367
368         fetchAllMetrics();
369     }, refreshInterval)
370 }
371
372 fetchAllMetrics();
373
374 setInterval(() => {
375     console.log("Setting interval "+refreshInterval+"ms")
376 }, refreshInterval)
377
378 app.get("/mon",function(req, res){
379
380
381     refreshInterval=2000
382
383   //Build web page
384         var htmlStr = "<!DOCTYPE html>" +
385           "<html>" +
386           "<head>" +
387             "<meta http-equiv=\"refresh\" content=\"2\">"+  //2 sec auto refresh
388             "<title>Policy Agent and simulator monitor</title>"+
389             "</head>" +
390             "<body>" +
391             "<h3>Policy agent</h3>" +
392             "<font face=\"monospace\">" +
393             "Status:..............................." + formatDataRow(ag1) + "<br>" +
394             "Services:............................." + formatIdRowCompact(ag2) + "<br>" +
395             "Types:................................" + formatIdRowCompact(ag3) + "<br>" +
396             "Number of instances:.................." + formatDataRow(ag4) + "<br>" +
397             "</font>" +
398             "<h3>MR Stub interface</h3>" +
399             "<font face=\"monospace\">"+
400             "Submitted requests:............................" + formatDataRow(mr1) + "<br>" +
401             "Fetched requests:.............................." + formatDataRow(mr2) + "<br>" +
402             "Current requests waiting:......................" + formatDataRow(mr3) + "<br>" +
403             "Submitted responses:..........................." + formatDataRow(mr4) + "<br>" +
404             "Fetched responses.............................." + formatDataRow(mr5) + "<br>" +
405             "Current responses waiting......................" + formatDataRow(mr6) + "<br>" +
406             "</font>"+
407             "<h3>Callback receiver</h3>" +
408             "<font face=\"monospace\">" +
409             "Callbacks received:..................." + formatDataRow(cr1) + "<br>" +
410             "Callbacks fetched:...................." + formatDataRow(cr2) + "<br>" +
411             "Number of waiting callback messages:.." + formatDataRow(cr3) + "<br>" +
412             "</font>" +
413             "<h3>Near-RT RIC Simulators</h3>" +
414             "<font face=\"monospace\">"
415
416             htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35,"&nbsp;")
417             htmlStr=htmlStr+padding("Types", 10,"&nbsp;")
418             htmlStr=htmlStr+padding("Instances", 10,"&nbsp;")+"<br>"
419             htmlStr=htmlStr+padding("",55,"=")+"<br>"
420             for(var simIndex=0;simIndex<simnames.length;simIndex++) {
421                 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35,"&nbsp;");
422                 htmlStr=htmlStr+padding(simvar2[simIndex],10,"&nbsp;")
423                 htmlStr=htmlStr+padding(simvar1[simIndex],10,"&nbsp;")
424                 htmlStr=htmlStr+"<br>";
425             }
426
427             htmlStr=htmlStr+"<br>";
428             htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35,"&nbsp;")
429             htmlStr=htmlStr+padding("Version", 20,"&nbsp;")
430             htmlStr=htmlStr+padding("Type-IDs", 10,"&nbsp;")+"<br>"
431             htmlStr=htmlStr+padding("",65,"=")+"<br>"
432             for(simIndex=0;simIndex<simnames.length;simIndex++) {
433                 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35,"&nbsp;");
434                 htmlStr=htmlStr+padding(simvar4[simIndex],20,"&nbsp;")
435                 htmlStr=htmlStr+padding(formatIdRowCompact(simvar3[simIndex]),10,"&nbsp;")
436                 htmlStr=htmlStr+"<br>";
437             }
438
439             htmlStr=htmlStr+"<br>";
440             htmlStr=htmlStr+padding("Near-RT RIC Simulator name", 35,"&nbsp;")
441             htmlStr=htmlStr+padding("Remote hosts", 50,"&nbsp;")+"<br>"
442             htmlStr=htmlStr+padding("",90,"=")+"<br>"
443             for(simIndex=0;simIndex<simnames.length;simIndex++) {
444                 htmlStr=htmlStr+padding(simnames[simIndex]+ " ("+simports[simIndex]+")",35,"&nbsp;");
445                 htmlStr=htmlStr+padding(simvar5[simIndex],50,"&nbsp;")
446                 htmlStr=htmlStr+"<br>";
447             }
448
449             htmlStr=htmlStr+
450            "</body>" +
451           "</html>";
452         res.send(htmlStr);
453 })
454
455 var httpServer = http.createServer(app);
456 var httpPort=9999;
457 httpServer.listen(httpPort);
458 console.log("Simulator monitor listening (http) at "+httpPort);
459 console.log("Open the web page on localhost:9999/mon to view the statistics page.")