diff --git a/Documentation/User Documentation/9. create_metrics.md b/Documentation/User Documentation/9. create_metrics.md new file mode 100644 index 0000000..bea06a1 --- /dev/null +++ b/Documentation/User Documentation/9. create_metrics.md @@ -0,0 +1,101 @@ +# The New Metrics User Interface + The new Metrics User Interface for ZEBRA allows users to create and delete custom metrics for prometheus. It contains two forms: New metrics and Existing metrics form. It also displays Metrics already created by the user and users can view and delete these metrics. + +# Terminilogies + **Metric** : This represented an RMF III report and its LPAR. its format is LPAR_REPORT e.g RPRT_CPC, RPRT_CHANNEL, RPRT_SYSINFO. Only one metric can be created for each report in a single LPAR. This means only one CPC report can exist for RPRT LPAR. Below is an example of a CPC metric +``` + "RPRT_CPC": { + "lpar": "RPRT", + "request": { + "report": "CPC", + "resource": ",RPRT,MVS_IMAGE" + }, + "identifiers": [ + + ] + } +``` + + **Identifiers** : This represent the parameters required to build a prometheus client metric from a Report. e.g parameters from CPC report needed to create a Total Utilization or effective utilization prometheus metric in ZEBRA. Each Metric can have multiple identifiers. Identifiers has 5 parameters: + - **Identifier Key**: This represent the parameter in a report that has a Unique value you will like to monitor. e.g CPCHPNAM in That represent Name of partition that collected the data in CPC report + - **Identifier Value**: This takes either ALL(to represent all Identifier key values) or a specific value for the identifier key (e.g VIRPT for CPCHPNAM in CPC report to signify intrest in only VIRPT LPAR data) + - **Metric ID (m_id)**: This is a unique ID selected by user to represent the Identifier (e.g TOU for Total Utilization, EFU for effective utilization) + - **Field**: This represent a numeric field in the report from which the number will be used to plot a chart in grafana. e.g CPCPLTOU which represent Logical processor total utilization, CPCPLEFU which represent Logical processor effective utilization in CPC Report + - **Description (desc)**: This is a user choosen description for the identifier + + Below is an example of CPC metric with Total Utilization and Effective Utilization Identifiers +``` + "RPRT_CPC": { + "lpar": "RPRT", + "request": { + "report": "CPC", + "resource": ",RPRT,MVS_IMAGE" + }, + "identifiers": [ + { + "field": "CPCPLTOU", + "key": "CPCPPNAM", + "value": "ALL", + "m_id": "TOU", + "desc": "CPC Total Utilization" + }, + { + "key": "CPCPPNAM", + "value": "ALL", + "field": "CPCPLEFU", + "m_id": "EFU", + "desc": "Effective Utilization" + } + ] + } +``` + **Prometheus Metric** : These are the final metrics created by ZEBRA and exposed through http(s)://ZEBRA Ip:port/prommetric Endpoint. They are of the folloeing format: + ``` + {LPAR}_{IDENTIFIER_VALUE}_{METRIC_ID} + ``` + + example of ZEBRA Prometheus Metrics + +``` +# HELP RPRT_TRNG_TOU CPC Total Utilization +# TYPE RPRT_TRNG_TOU gauge +RPRT_TRNG_TOU{parm="CPCPLTOU"} 0.4 + +# HELP RPRT_VIDVLP_TOU CPC Total Utilization +# TYPE RPRT_VIDVLP_TOU gauge +RPRT_VIDVLP_TOU{parm="CPCPLTOU"} 0.5 + +# HELP RPRT_VIRPT_TOU CPC Total Utilization +# TYPE RPRT_VIRPT_TOU gauge +RPRT_VIRPT_TOU{parm="CPCPLTOU"} 0.5 + +# HELP RPRT_TRNG_EFU Effective Utilization +# TYPE RPRT_TRNG_EFU gauge +RPRT_TRNG_EFU{parm="CPCPLEFU"} 0.4 + +# HELP RPRT_VIDVLP_EFU Effective Utilization +# TYPE RPRT_VIDVLP_EFU gauge +RPRT_VIDVLP_EFU{parm="CPCPLEFU"} 0.4 + +# HELP RPRT_VIRPT_EFU Effective Utilization +# TYPE RPRT_VIRPT_EFU gauge +RPRT_VIRPT_EFU{parm="CPCPLEFU"} 0.5 +``` + +# How To create a Metric +- Using the metrics user interface (http://zebraIP:port/metrics) +- Click on New Metric. Use this form to add a new metric with one identifier +- Click Save + +# How to Add Identifiers +- Using the metrics user interface (http://zebraIP:port/metrics) +- Click on Existing Metric +- Select The Metric from the drop Down +- Fill the form +- Click Save + +# View and Delete Metric/Identifier +- From the dropdown under metrics added in the center of the page +- Select a metric to view it details +- Click Delete ID to delete a single identifier +- Click Delete Metric to delete the metric and its Identifiers diff --git a/src/.dockerignore b/src/.dockerignore index e42e335..1949651 100644 --- a/src/.dockerignore +++ b/src/.dockerignore @@ -3,6 +3,7 @@ db data my.db my.dbrefresh +admin.db # node modules node_modules diff --git a/src/Auth.js b/src/Auth.js index 7f966b6..aff954f 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -49,7 +49,6 @@ module.exports.token = function(req, res){ res.json({accessToken: accessToken}) }) }); - }catch(err){ res.send("Token Generation Failed"); } diff --git a/src/admin.db b/src/admin.db new file mode 100644 index 0000000..5bc02da Binary files /dev/null and b/src/admin.db differ diff --git a/src/app.js b/src/app.js index 0efc8bc..3e9eb55 100644 --- a/src/app.js +++ b/src/app.js @@ -27,6 +27,7 @@ var path = require('path'); var cookieParser = require('cookie-parser'); const fs = require('fs'); var session = require('express-session'); +const tls = require('tls'); const cors = require('cors'); const bodyParser = require('body-parser'); @@ -73,6 +74,7 @@ if(lpar_prom.length > 0){ } //require("./Eureka_conn"); process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0; +tls.DEFAULT_MIN_VERSION = "TLSv1.1"; var mainRouter = require('./app_server/routes/mainRouter'); var rmf3Router = require('./app_server/routes/rmf3Router'); var rmfppRouter = require('./app_server/routes/rmfppRouter'); diff --git a/src/app_server/routes/mainRouter.js b/src/app_server/routes/mainRouter.js index 373a6b1..79b20db 100644 --- a/src/app_server/routes/mainRouter.js +++ b/src/app_server/routes/mainRouter.js @@ -57,14 +57,14 @@ function parameters(fn){ usePrometheus: Zconfig.usePrometheus, https: Zconfig.https, grafanaurl: Zconfig.grafanaurl, - grafanaport: Zconfig.grafanaport + grafanaport: Zconfig.grafanaport } fn(parms); //return the parameters } -router.get('/pwdd', (req, res) => { //remember to delete +/* router.get('/pwdd', (req, res) => { //remember to delete res.render("login", {data: "pwd"}) -}) +}) */ // Checks if user login session is available in browser var sessionChecker = (req, res, next) => { @@ -96,6 +96,49 @@ router.post('/delmtr', (req, res) => { }); }); +router.post('/delid', (req, res) => { + fs.readFile('metrics.json', (err, data) => { + if (err) throw err; + let metricsfile = JSON.parse(data); + var id_len = metricsfile[req.body.ky]["identifiers"].length; + for(i=0; i < id_len; i++){ + if (metricsfile[req.body.ky]["identifiers"][i]["m_id"] == req.body.id){ + metricsfile[req.body.ky]["identifiers"].splice(i,1); + break; + } + } + fs.writeFile("metrics.json", JSON.stringify(metricsfile, null, '\t'), 'utf-8', function(err, data) { + res.send("Metric ID Deleted Successfully"); + }); + }); +}); + + +router.get('/getmetr', (req, res) => { + fs.readFile('metrics.json', (err, data) => { + if (err) throw err; + let metricsfile = JSON.parse(data); + res.send({sc:Object.keys(metricsfile)}); + }); +}) + +router.post('/getmetricdis', (req, res) => { + try{ + var metric = req.body.metric; + fs.readFile('metrics.json', (err, data) => { + if (err) throw err; + let metricsfile = JSON.parse(data); + let mtr = metricsfile[metric] + res.send({sysid: mtr.lpar, rpt: mtr.request.report, rsc: mtr.request.resource, ids:mtr.identifiers}); + }); + + }catch(err){ + res.send("error") + + } + +}) + router.post('/savemtr', (req, res) => { try{ var lpar = req.body.lpar; @@ -106,7 +149,7 @@ router.post('/savemtr', (req, res) => { var umi = req.body.umi; var umd = req.body.umd; var rst = req.body.rst; - var key = `${lpar}_${snvl}_${umi}`; + var key = `${lpar}_${rpt}`; var mtr = JSON.parse(`{ "lpar": "${lpar}", "request": { @@ -116,18 +159,56 @@ router.post('/savemtr', (req, res) => { "identifiers": [ { "key": "${nid}", - "value": "${snvl}" + "value": "${snvl}", + "field": "${vid}", + "m_id": "${umi}", + "desc": "${umd}" } - ], - "field": "${vid}", - "desc": "${umd}" + ] }`) fs.readFile('metrics.json', (err, data) => { if (err) throw err; let metricsfile = JSON.parse(data); - metricsfile[`${key}`] = mtr + metrkeys = Object.keys(metricsfile); + if(metrkeys.includes(key)){ + res.status(304).send(); + }else { + metricsfile[`${key}`] = mtr + fs.writeFile("metrics.json", JSON.stringify(metricsfile, null, '\t'), 'utf-8', function(err, data) { + res.send("Metric Added Successfully"); + }); + } + }); + + }catch(err){ + res.send("error") + } +}) + +router.post('/saveexmtr', (req, res) => { + try{ + var metr = req.body.metr; + var nidex = req.body.nidex; + var snvlex = req.body.snvlex; + var videx = req.body.videx; + var umiex = req.body.umiex; + var umdex = req.body.umdex; + var idf = JSON.parse(` + { + + "key": "${nidex}", + "value": "${snvlex}", + "field": "${videx}", + "m_id": "${umiex}", + "desc": "${umdex}" + } + `) + fs.readFile('metrics.json', (err, data) => { + if (err) throw err; + let metricsfile = JSON.parse(data); + (metricsfile[`${metr}`]["identifiers"]).push(idf) fs.writeFile("metrics.json", JSON.stringify(metricsfile, null, '\t'), 'utf-8', function(err, data) { - res.send("Metric Added Successfully"); + res.send("Metric Identifier Successfully"); }); }); @@ -201,9 +282,9 @@ router.get('/metrics', sessionChecker, (req, res) => { } //console.log(c); if(req.session.name){ //Check if User login session is available - res.render("metrics",{msg:"Admin", resources:resource, lpars:lpar, reports:REPORTS.RMFM3}); // render the metrics page wih Admin previledge + res.render("metricdev",{msg:"Admin", resources:resource, lpars:lpar, reports:REPORTS.RMFM3}); // render the metrics page wih Admin previledge }else{ - res.render("metrics", {resources:resource, lpars:lpar, reports:REPORTS.RMFM3}); + res.render("metricdev", {resources:resource, lpars:lpar, reports:REPORTS.RMFM3}); } }) diff --git a/src/app_server/views/metricdev.pug b/src/app_server/views/metricdev.pug new file mode 100644 index 0000000..071fd48 --- /dev/null +++ b/src/app_server/views/metricdev.pug @@ -0,0 +1,478 @@ +extends layout + +block header + link(rel='stylesheet', href='/stylesheets/slider.css') + style. + .center { + margin: 0; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + } + + .container { + height: 200px; + position: relative; + } + + form { + --background: white; + --border: rgba(0, 0, 0, 0.125); + --borderDark: rgba(0, 0, 0, 0.25); + --borderDarker: rgba(0, 0, 0, 0.5); + --bgColorH: 0; + --bgColorS: 0%; + --bgColorL: 98%; + --fgColorH: 210; + --fgColorS: 50%; + --fgColorL: 38%; + --shadeDark: 0.3; + --shadeLight: 0.7; + --shadeNormal: 0.5; + --borderRadius: 0.125rem; + --highlight: #306090; + background: white; + border: 1px solid var(--border); + border-radius: var(--borderRadius); + box-shadow: 0 1rem 1rem -0.75rem var(--border); + display: flex; + flex-direction: column; + padding: 1rem; + position: relative; + overflow: hidden; + width:80%; + } + + .topa{ + width:80%; + margin: auto; + border: 1%; + padding: 10px; + } + + table { + font-family: arial, sans-serif; + border-collapse: collapse; + width: 96%; + } + + td, th { + border: 1px solid #dddddd; + text-align: left; + padding: 8px; + } + + tr:nth-child(even) { + background-color: #dddddd; + } + + /* Style the tab */ + .tab { + overflow: hidden; + border: 1px solid #ccc; + background-color: #f1f1f1; + } + + /* Style the buttons inside the tab */ + .tab button { + background-color: inherit; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 14px 16px; + transition: 0.3s; + font-size: 17px; + } + + /* Change background color of buttons on hover */ + .tab button:hover { + background-color: #ddd; + } + + /* Create an active/current tablink class */ + .tab button.active { + background-color: #ccc; + } + + /* Style the tab content */ + .tabcontent { + display: none; + padding: 6px 12px; + border: 1px solid #ccc; + border-top: none; + } + +block content + div(class="sidefm") + .salis(style={"width":"96%", "margin-top":"0px", "margin-bottom":"30px", "margin-left": "5%"}) + + div(class="tab") + button(class="tablinks" onclick="openTab(event, 'New')") New Metric + button(class="tablinks" onclick="openTab(event, 'Existing')") Existing Metric + div(id="New" class="tabcontent") + .row + form() + .row + .col-md-6 + label(style={"margin-top":"5px", "margin-right": "10px", "font-size":"12px"} for=`sysid`) SYSID + select.custom-select(class="form-control-sm" name="lpar" id="lpar" style={"margin-right": "10px"}) + option(selected='') Select LPAR + - for(dat of lpars) + option(value=`${dat}`) !{dat} + .col-md-6 + label(style={"margin-top":"5px", "margin-right": "10px", "font-size":"12px"} for=`rpt`) Report + select.custom-select(class="form-control-sm" name="rpt" id="rpt" style={"margin-right": "10px"} onchange="getrpt()") + option(selected='') Select + each report in reports + option(value=report) #{report} + label(style={"margin-top":"5px", "margin-right": "10px"} for="mid") Unique Metric id + input(type="text" class="form-control-sm" id=`umi` style={"margin-right": "10px"} placeholder=`e.g TOU for Total utilization`) + label(style={"margin-top":"5px", "margin-right": "10px"}) Resource + select.custom-select(class="form-control-sm" name="rst" id="rst" style={"margin-right": "10px"}) + option(selected='') Select Resource + - for(dt of resources) + option(value=`${dt}`) !{dt} + label(style={"margin-top":"5px", "margin-right": "10px"}) Identifier Key + select.custom-select(class="form-control-sm" name="prm" id="nid" style={"margin-right": "10px"} onchange="getnvl()") + option(selected='') Select + label(style={"margin-top":"5px", "margin-right": "10px"}) Identifier Value + select.custom-select(class="form-control-sm" name="nvl" id="snvl" style={"margin-right": "10px"}) + option(selected='*') ALL + label(style={"margin-top":"5px", "margin-right": "10px"}) Field + select.custom-select(class="form-control-sm" name="vrm" id="vid" style={"margin-right": "10px"}) + option(selected='') Select + label(style={"margin-top":"5px", "margin-right": "10px"}) Metric Description + input(type="text" class="form-control-sm" id=`umd` style={"margin-right": "10px"} placeholder=`A simple metrics description`) + + a(href='javascript:savemtr()' class='btn btn-primary' id="rmf3b", style={"margin-top":"13px", "width":"98%", "color":"white"}) Save + + div(id="Existing" class="tabcontent") + .salis(style={"width":"96%", "margin-top":"0px", "margin-bottom":"30px", "margin-left": "5%"}) + .row + form() + label(style={"margin-top":"5px", "margin-right": "10px"}) Metric(s) + select.custom-select(class="form-control-sm" name="metr" id="metr" style={"margin-right": "10px"} onchange="getrptex()") + option(selected='') Select Metric + label(style={"margin-top":"5px", "margin-right": "10px"} for="mid") Unique Metric id + input(type="text" class="form-control-sm" id=`umiex` style={"margin-right": "10px"} placeholder=`e.g TOU for Total utilization`) + label(style={"margin-top":"5px", "margin-right": "10px"}) Identifier Key + select.custom-select(class="form-control-sm" name="prmex" id="nidex" style={"margin-right": "10px"} onchange="getnvlex()") + option(selected='') Select + label(style={"margin-top":"5px", "margin-right": "10px"}) Identifier Value + select.custom-select(class="form-control-sm" name="nvlex" id="snvlex" style={"margin-right": "10px"}) + option(selected='*') ALL + label(style={"margin-top":"5px", "margin-right": "10px"}) Field + select.custom-select(class="form-control-sm" name="vrmex" id="videx" style={"margin-right": "10px"}) + option(selected='') Select + label(style={"margin-top":"5px", "margin-right": "10px"}) Metric Description + input(type="text" class="form-control-sm" id=`umdex` style={"margin-right": "10px"} placeholder=`A simple metrics description`) + + + a(href='javascript:saveexmtr()' class='btn btn-primary' id="rmf3b", style={"margin-top":"13px", "width":"98%", "color":"white"}) Save + + + div(class="mainfm", style={"overflow" : "auto"}) + h5 Add Custom Metrics Page + p Use the form to add customize ZEBRA Prometheus Metrics. Note, you will need to first configure ZEBRA and have a working internet connection + br + p Full information on how the form works can be found in the ZEBRA documentation. + h5 Metrics Added + div(style={"overflow":"auto", "max-height":"50vh"}) + .row(style={"margin-right": "30px", "margin-left": "10px"}) + label(style={"margin-top":"5px", "margin-right": "10px"}) Metric(s) + select.custom-select(class="form-control-sm" name="metrdis" id="metrdis" style={"margin-right": "10px"} onchange="getmetrdis()") + option(selected='') Select Metric + div(style={"clear": "both", "margin-top":"10px"}) + p(style={"float": "left", "font-size": "18px"}) SYSID: + p(id="sysid" style={"font-weight": "bold", "float": "right", "margin-left":"5px", "font-size": "18px"}) + hr + div(style={"clear": "both", "margin-top":"10px"}) + p(style={"float": "left", "font-size": "18px"}) Report: + p(id="rptdis" style={"font-weight": "bold", "float": "right", "margin-left":"5px", "font-size": "18px"}) + hr + div(style={"clear": "both", "margin-top":"10px"}) + p(style={"float": "left", "font-size": "18px"}) Resource: + p(id="rsc" style={"font-weight": "bold", "float": "right", "margin-left":"5px", "font-size": "18px"}) + hr + div(style={"clear": "both", "margin-top":"10px"}) + a(href='javascript:delmtr()' class='btn btn-danger' id="rmf3b", style={ "width":"100%", "float": "left", "color":"white"}) Delete Metric + + table(id="sctable") + tr + th Identifier Name + th Identifier Value + th Field + th Metric ID + th Description + th Action + + + +block scripts + script(src='https://code.jquery.com/jquery-3.5.1.min.js') + script(src='/stylesheets/bootstrap/js/bootstrap.min.js') + script(src='/stylesheets/bootstrap/js/bootstrap.bundle.min.js') + script. + + function getmetrdis() { + var data = {metric: $("#metrdis").val()}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/getmetricdis', + data: data, + }) + .done(function(e){ + //window.reloadTable("sctable"); + //$("#sctable > tr").empty(); + $('#sysid').text(`${e.sysid}`) + $('#rptdis').text(`${e.rpt}`) + $('#rsc').text(`${e.rsc}`) + $("#sctable").find("tr:gt(0)").remove(); + $.each(e.ids, function(i){ + var len = $("#sctable tr").toArray().length; + var newRow = "" + +""+ e.ids[i]["key"] +"" + +""+e.ids[i]["value"]+"" + +""+e.ids[i]["field"]+"" + +""+e.ids[i]["m_id"]+"" + +""+e.ids[i]["desc"]+"" + +`
Delete ID
` + +""; + $(newRow).appendTo("#sctable"); + }) + }); + } + + function gettbl(){ + //$("#sctable > tr").empty(); + $("#sctable").find("tr:gt(0)").remove(); + $.ajax({ + dataType: "json", + type: 'get', + url: '/mtrfile' + }) + .done(function(e){ + //window.reloadTable("sctable"); + + $.each(e.mtr, function(i){ + var len = $("#sctable tr").toArray().length; + var newRow = "" + +""+ e.mtr[i] +"" + +""+e.jsn[e.mtr[i]]["lpar"]+"" + +""+ e.jsn[e.mtr[i]]["identifiers"][0]["key"] +"" + +""+e.jsn[e.mtr[i]]["identifiers"][0]["value"]+"" + +""+e.jsn[e.mtr[i]]["identifiers"][0]["field"]+"" + +`
Delete ID
` + +""; + $(newRow).appendTo("#sctable"); + }) + }); + } + + window.onload = function () { + gettmetr("metrdis"); + //gettbl(); + + } + + function delmtr() { + var data = {ky: $("#metrdis").val()}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/delmtr', + data: data, + complete: function(data) { + if(data.status === 200){ + alert(`Metric Deleted Successfully`); + }else{ + alert(`Deleting Metric Failed`); + } + location.reload(); + } + }) + } + + function del_id(a) { + var metr = $("#metrdis").val(); + var data = {ky: metr, id:a}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/delid', + data: data, + complete: function(data) { + if(data.status === 200){ + alert(`${metr} Metric ${a} Identifier Deleted Successfully`); + }else{ + alert(`Deleting Metric Identifier Failed`); + } + location.reload(); + } + }) + } + + function getrpt() { + var data = {lpar: $("#lpar").val(), rpt: $("#rpt").val()}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/getrpt', + data: data, + }) + .done(function(e){ + $.each(e.sc, function(i){ + var x = document.getElementById("nid"); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + $.each(e.sc, function(i){ + var x = document.getElementById("vid"); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + }); + } + + function getrptex() { + var mtrx = $("#metr").val().split("_") + var data = {lpar: mtrx[0], rpt: mtrx[1]}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/getrpt', + data: data, + }) + .done(function(e){ + //alert(e.sc); + //window.reloadTable("nid"); + $.each(e.sc, function(i){ + var x = document.getElementById("nidex"); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + $.each(e.sc, function(i){ + var x = document.getElementById("videx"); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + }); + } + + function getnvl() { + var data = {lpar: $("#lpar").val(), rpt: $("#rpt").val(), nid: $("#nid").val()}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/getnvl', + data: data, + }) + .done(function(e){ + $.each(e.sc, function(i){ + var x = document.getElementById("snvl"); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + }); + } + + function getnvlex() { + var mtrx = $("#metr").val().split("_") + var data = {lpar: mtrx[0], rpt: mtrx[1], nid: $("#nidex").val()}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/getnvl', + data: data, + }) + .done(function(e){ + $.each(e.sc, function(i){ + var x = document.getElementById("snvlex"); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + }); + } + + function savemtr() { + var data = {lpar: $("#lpar").val(), rpt: $("#rpt").val(), nid: $("#nid").val(), umi: $("#umi").val(), umd: $("#umd").val(), snvl: $("#snvl").val(), vid: $("#vid").val(), rst: $("#rst").val() }; + $.ajax({ + dataType: "json", + type: 'post', + url: '/savemtr', + data: data, + complete: function(data) { + var c = `${$("#lpar").val()}_${$("#rpt").val()}` + + if(data.status === 200){ + alert(`${c} Metric Saved Successfully`); + }else if(data.status === 304){ + alert(`Metric Already Exist`); + }else{ + alert(`Saving Metric Failed`); + } + location.reload(); + } + }) + } + + function saveexmtr() { + var data = {metr: $("#metr").val(), nidex: $("#nidex").val(), umiex: $("#umiex").val(), umdex: $("#umdex").val(), snvlex: $("#snvlex").val(), videx: $("#videx").val()}; + $.ajax({ + dataType: "json", + type: 'post', + url: '/saveexmtr', + data: data, + complete: function(data) { + var c = `${$("#metr").val()}` + + if(data.status === 200){ + alert(`${c} Metric Identifier Saved Successfully`); + }else{ + alert(`Saving Metric Identifier Failed`); + } + location.reload(); + } + }) + } + function gettmetr(elem){ + $.ajax({ + dataType: "json", + type: 'get', + url: '/getmetr' + }) + .done(function(e){ + $.each(e.sc, function(i){ + var x = document.getElementById(`${elem}`); + var c = document.createElement("option"); + c.text = e.sc[i]; + x.options.add(c, (i+1)); + }) + }); + } + function openTab(evt, Name) { + var i, tabcontent, tablinks; + if(Name == "Existing"){ + gettmetr("metr"); + } + tabcontent = document.getElementsByClassName("tabcontent"); + for (i = 0; i < tabcontent.length; i++) { + tabcontent[i].style.display = "none"; + } + tablinks = document.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + } + document.getElementById(Name).style.display = "block"; + evt.currentTarget.className += " active"; + } \ No newline at end of file diff --git a/src/metrics.js b/src/metrics.js index 66379a9..96ba59c 100644 --- a/src/metrics.js +++ b/src/metrics.js @@ -37,13 +37,20 @@ setInterval(async () => { for (const lpar of lpars) { // Dynamically get which DDS reports must be made to get data via the metrics var requests = {}; + var multi_true_request = {} for (const metricName in metrics) { var metric = metrics[metricName]; - if (metric.lpar === lpar) { - requests[metric.request.report + " " + (metric.request.resource ? metric.request.resource : dds[lpar]["mvsResource"])] = true; + if(metric.multi === true){ + if (metric.lpar === lpar) { + multi_true_request[metric.request.report + " " + (metric.request.resource ? metric.request.resource : dds[lpar]["mvsResource"])] = true; + } + }else{ + if (metric.lpar === lpar) { + requests[metric.request.report + " " + (metric.request.resource ? metric.request.resource : dds[lpar]["mvsResource"])] = true; + } } } - // Loop through DDS requests + // Loop through DDS requests for single identifier for (const request in requests) { var [ report, resource ] = request.split(" ") // Get XML data from DDS (this method is more efficient than calling our own API with axios) @@ -53,54 +60,141 @@ setInterval(async () => { const result = response.data; for (const metricName in metrics) { var metric = metrics[metricName]; - // Check if users want data from all records in the JSON table having the metrics key - if(metric.identifiers[0].value === "ALL"){ - for (i in result['table']) { // loop through the entities - var mtrid = metricName.split("_")[2] // get the metric identifier provided by the user - var JSONBody = result['table'][i]; - var name = `${lpar}_${JSONBody[metric.identifiers[0].key]}_${mtrid}`; //append TOU(Total Utilization) to lpar name - var value = JSONBody[metric.field]; - try { - (new prometheus.Gauge({ - name: name, - help: metric.desc, - labelNames: ['parm'] - })).set({ - parm: metric.field - }, parseFloat(value)); - } catch (err) { - //console.log(err); + //Loop through Identifiers + for (var z in metric.identifiers) + { + // Check if users want data from all records in the JSON table having the metrics key + if(metric.identifiers[z].value === "ALL"){ + for (i in result['table']) { // loop through the entities + var mtrid = metricName.split("_")[2] // get the metric identifier provided by the user + var JSONBody = result['table'][i]; + var name = `${lpar}_${JSONBody[metric.identifiers[z].key]}_${metric.identifiers[z].m_id}`; //append TOU(Total Utilization) to lpar name + var value = JSONBody[metric.identifiers[z].field]; + try { + (new prometheus.Gauge({ + name: name, + help: metric.identifiers[z].desc, + labelNames: ['parm'] + })).set({ + parm: metric.identifiers[z].field + }, parseFloat(value)); + } catch (err) { + //console.log(err); + } } - } - }else{ - if (metric.request.report === report && metric.request.resource === resource) { - // Get value of metric - var value = getValue(result, metric); - // If no match, log error and continue - if (!value) { - // console.log(`WARNING: Could not find field '${metric.field}' in report '${report}' for Prometheus metric '${metricName}'. Field could be missing because its value is 0, but make sure field is valid key.`); - continue; + }else{ + if (metric.request.report === report && metric.request.resource === resource) { + for (i in result['table']) { // loop through the entities + var mtrid = metricName.split("_")[2] // get the metric identifier provided by the user + var JSONBody = result['table'][i]; + if(JSONBody[metric.identifiers[z].key] == metric.identifiers[z].value) + { + var name = `${lpar}_${JSONBody[metric.identifiers[z].key]}_${metric.identifiers[z].m_id}`; //append TOU(Total Utilization) to lpar name + var value = JSONBody[metric.identifiers[z].field]; + if (!value) { + // console.log(`WARNING: Could not find field '${metric.field}' in report '${report}' for Prometheus metric '${metricName}'. Field could be missing because its value is 0, but make sure field is valid key.`); + continue; + } + if (!isNumeric(value)) { + console.log(`ERROR: Non-numeric error - the field '${metric.identifiers[z].field}' in report '${report}' for Prometheus metric '${metricName}' is not numeric.`); + continue; + } + try { + (new prometheus.Gauge({ + name: name, + help: metric.identifiers[z].desc, + labelNames: ['parm'] + })).set({ + parm: metric.identifiers[z].field + }, parseFloat(value)); + } catch (err) { + //console.log(err); + } + break; + } + + } } - // If non numeric value is chosen, log error and continue - if (!isNumeric(value)) { - console.log(`ERROR: Non-numeric error - the field '${metric.field}' in report '${report}' for Prometheus metric '${metricName}' is not numeric.`); - continue; + } + } + } + }) + .catch((err) => { + console.log(err); + }); + } + + // Loop through DDS requests for multiple identifier + for (const request in multi_true_request) { + var [ report, resource ] = request.split(" ") + // Get XML data from DDS (this method is more efficient than calling our own API with axios) + await axios.get(`${use_cert == 'true' ? 'https' : 'http'}://${appurl}:${appport}/v1/${lpar}/rmf3/${report}?resource=${resource}`) + .then((response) => { + // Loop through metrics using this report + const result = response.data; + for (const metricName in metrics) { + var metric = metrics[metricName]; + //Loop through Identifiers + for (var z in metric.identifiers) + { + /* console.log(metric.identifiers[z].id[0].key) + console.log(metric.identifiers[z].id[1].key) + console.log("==================") */ + //Check if users want data from all records in the JSON table having the metrics key + if(metric.identifiers[z].id[0].value === "ALL" && metric.identifiers[z].id[1].value === "ALL"){ + for (i in result['table']) { // loop through the entities + var JSONBody = result['table'][i]; + var name = `${lpar}_${JSONBody[metric.identifiers[z].id[0].key]}_${JSONBody[metric.identifiers[z].id[1].key]}_${metric.identifiers[z].m_id}`; //append TOU(Total Utilization) to lpar name + var value = JSONBody[metric.identifiers[z].field]; + try { + (new prometheus.Gauge({ + name: name, + help: metric.identifiers[z].desc, + labelNames: ['parm'] + })).set({ + parm: metric.identifiers[z].field + }, parseFloat(value)); + } catch (err) { + //console.log(err); + } } - try{ - (new prometheus.Gauge({ - name: metricName, - help: metric.desc, - labelNames: ['parm'] - })).set({ - parm: metric.field - }, parseFloat(value)); - } catch(err) { - console.log(err); + + }else{ + if (metric.request.report === report && metric.request.resource === resource) { + for (i in result['table']) { // loop through the entities + var JSONBody = result['table'][i]; + //console.log(JSONBody[metric.identifiers[z].id[0].key] == metric.identifiers[z].id[0].value) + if(JSONBody[metric.identifiers[z].id[0].key] == metric.identifiers[z].id[0].value || JSONBody[metric.identifiers[z].id[1].key] == metric.identifiers[z].id[1].value) + { + var name = `${lpar}_${JSONBody[metric.identifiers[z].id[0].key]}_${JSONBody[metric.identifiers[z].id[1].key]}_${metric.identifiers[z].m_id}`; //append TOU(Total Utilization) to lpar name + var value = JSONBody[metric.identifiers[z].field]; + if (!value) { + // console.log(`WARNING: Could not find field '${metric.field}' in report '${report}' for Prometheus metric '${metricName}'. Field could be missing because its value is 0, but make sure field is valid key.`); + continue; + } + if (!isNumeric(value)) { + console.log(`ERROR: Non-numeric error - the field '${metric.identifiers[z].field}' in report '${report}' for Prometheus metric '${metricName}' is not numeric.`); + continue; + } + try { + (new prometheus.Gauge({ + name: name, + help: metric.identifiers[z].desc, + labelNames: ['parm'] + })).set({ + parm: metric.identifiers[z].field + }, parseFloat(value)); + } catch (err) { + console.log(err); + } + break; + } + + } } } - } - + } } }) .catch((err) => { @@ -117,8 +211,8 @@ function getValue (data, metric) { // Check through caption if it exists if (data["caption"]) { for (const key in data["caption"]) { - if (key === metric.field) { - return data["caption"][metric.field]; + if (key === metric.identifiers[z].field) { + return data["caption"][metric.identifiers[z].field]; } } } @@ -133,7 +227,7 @@ function getValue (data, metric) { } } if (passes) { - return entity[metric.field]; + return entity[metric.identifiers[z].field]; } } } diff --git a/src/nedbAdmin.js b/src/nedbAdmin.js index de8505e..a503c9b 100644 --- a/src/nedbAdmin.js +++ b/src/nedbAdmin.js @@ -15,7 +15,7 @@ db = new sqlite3.Database('./admin.db', sqlite3.OPEN_READWRITE, (err) => { /* runQueries(db, function(data){ console.log(data); }); */ - console.log("Admin DB Created"); + console.log("Admin DB Started"); }); function createDatabase() { diff --git a/src/package-lock.json b/src/package-lock.json index 6ca1ad2..1e11816 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -37,6 +37,7 @@ "sqlite3": "^5.1.1", "swagger-ui-express": "^4.1.4", "sweetalert": "^2.1.2", + "tls": "^0.0.1", "xml2js": "^0.4.23" } }, @@ -3648,6 +3649,11 @@ "bintrees": "1.0.1" } }, + "node_modules/tls": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tls/-/tls-0.0.1.tgz", + "integrity": "sha512-GzHpG+hwupY8VMR6rYsnAhTHqT/97zT45PG8WD5eTT1lq+dFE0nN+1PYpsoBcHJgSmTz5ceK2Cv88IkPmIPOtQ==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6939,6 +6945,11 @@ "bintrees": "1.0.1" } }, + "tls": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tls/-/tls-0.0.1.tgz", + "integrity": "sha512-GzHpG+hwupY8VMR6rYsnAhTHqT/97zT45PG8WD5eTT1lq+dFE0nN+1PYpsoBcHJgSmTz5ceK2Cv88IkPmIPOtQ==" + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", diff --git a/src/package.json b/src/package.json index e599069..272cec6 100644 --- a/src/package.json +++ b/src/package.json @@ -39,6 +39,7 @@ "sqlite3": "^5.1.1", "swagger-ui-express": "^4.1.4", "sweetalert": "^2.1.2", + "tls": "^0.0.1", "xml2js": "^0.4.23" } }