| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| * |
| */ |
| |
| const axios = require('axios'); |
| const { buildUrl } = require('../utils/urlHelper'); |
| const { getEnv } = require('../utils/envParser'); |
| const logger = require('../utils/logger'); |
| |
| /** |
| * Fetches CPU usage data from Node Exporter and sends it as a response. |
| * |
| * @async |
| * @function getCpuUsage |
| * @param {Object} req - The HTTP request object. |
| * @param {Object} req.body - The request body containing query parameters. |
| * @param {string} req.body.query - The PromQL query to fetch CPU usage data. |
| * @param {string} [req.body.from="now-5m"] - The start time for the query range (default: 5 minutes ago). |
| * @param {string} [req.body.until="now"] - The end time for the query range (default: now). |
| * @param {number} [req.body.step=28] - The step interval in seconds for the query (default: 28 seconds). |
| * @param {Object} res - The HTTP response object. |
| * @returns {Promise<void>} Sends the filtered CPU usage data or an error response. |
| */ |
| async function getCpuUsage(req, res) { |
| const { query, from = "now-5m", until = "now", step = 28 } = req.body; |
| logger.debug(req.body); |
| const baseUrl = buildUrl(getEnv("NODE_EXPORTER_BASE_URL"), { |
| query, |
| start: from, |
| end: until, |
| step, |
| }); |
| |
| const config = { |
| method: 'get', |
| maxBodyLength: Infinity, |
| url: baseUrl, |
| }; |
| |
| try { |
| const response = await axios.request(config); |
| const data = response?.data?.data?.result.filter((val) => val?.metric?.groupname === 'kv_service'); |
| const responseData = data.length > 0 ? data[0]?.values : []; |
| return res.send({ data: responseData }); |
| } catch (error) { |
| logger.error(error); |
| return res.status(400).send({ |
| error: error.message || "An error occurred", |
| }); |
| } |
| } |
| |
| /** |
| * Fetches Disk IOPS (Input/Output Operations Per Second) data from Node Exporter. |
| * |
| * @async |
| * @function getDiskIOPS |
| * @param {Object} req - The HTTP request object containing query parameters in the body. |
| * @param {Object} res - The HTTP response object. |
| * @returns {Promise<void>} Sends the formatted Disk IOPS data or an error response. |
| */ |
| async function getDiskIOPS(req, res) { |
| const { from = "now-12h", until = "now", step = 28 } = req.body; |
| |
| const baseUrl = buildUrl(getEnv("NODE_EXPORTER_BASE_URL"), { |
| query: "rate(node_disk_writes_completed_total{device='vda',job='node_exporter'}[5m])", |
| start: from, |
| end: until, |
| step, |
| }); |
| |
| const config = { |
| method: 'get', |
| maxBodyLength: Infinity, |
| url: baseUrl, |
| }; |
| |
| try { |
| const response = await axios.request(config); |
| const data = response?.data?.data?.result.filter( |
| (val) => val?.metric?.device === "vda" && val?.metric?.job === "node_exporter" |
| ); |
| |
| const formattedResponseData = data.length > 0 |
| ? data[0]?.values.map(([timestamp, value]) => ({ |
| time: new Date(Number(timestamp) * 1000).toISOString(), |
| value: parseFloat(value).toFixed(2), |
| })) |
| : []; |
| |
| return res.send({ data: formattedResponseData }); |
| } catch (error) { |
| logger.error(error); |
| return res.status(400).send({ |
| error: error.message || "An error occurred while fetching the DiskIOPS data", |
| }); |
| } |
| } |
| |
| /** |
| * Fetches Disk Wait Time data (read and write) from Node Exporter. |
| * |
| * @async |
| * @function getDiskWaitTime |
| * @param {Object} req - The HTTP request object containing query parameters in the body. |
| * @param {Object} res - The HTTP response object. |
| * @returns {Promise<void>} Sends the formatted Disk Wait Time data or an error response. |
| */ |
| async function getDiskWaitTime(req, res) { |
| const { from = "now-5m", until = "now", step = 28 } = req.body; |
| |
| try { |
| const queries = { |
| readWaitTime: "rate(node_disk_read_time_seconds_total{device='vda'}[5m]) / rate(node_disk_reads_completed_total{device='vda'}[5m])", |
| writeWaitTime: "rate(node_disk_write_time_seconds_total{device='vda'}[5m]) / rate(node_disk_writes_completed_total{device='vda'}[5m])", |
| }; |
| |
| const responses = await Promise.all( |
| Object.entries(queries).map(async ([key, query]) => { |
| const baseUrl = buildUrl(getEnv("NODE_EXPORTER_BASE_URL"), { |
| query, |
| start: from, |
| end: until, |
| step, |
| }); |
| const response = await axios.get(baseUrl); |
| return { |
| key, |
| values: response?.data?.data?.result?.[0]?.values || [], |
| }; |
| }) |
| ); |
| |
| const formattedData = responses.reduce((acc, { key, values }) => { |
| acc[key] = values.map(([timestamp, value]) => ({ |
| time: new Date(timestamp * 1000).toISOString(), |
| value: parseFloat(value), |
| })); |
| return acc; |
| }, {}); |
| |
| return res.send({ data: formattedData }); |
| } catch (error) { |
| logger.error(error); |
| return res.status(400).send({ |
| error: error.message || "An error occurred while fetching Disk Wait Time data", |
| }); |
| } |
| } |
| |
| /** |
| * Fetches the time spent doing I/O operations from Node Exporter. |
| * |
| * @async |
| * @function getTimeSpentDoingIO |
| * @param {Object} req - The HTTP request object containing query parameters in the body. |
| * @param {Object} res - The HTTP response object. |
| * @returns {Promise<void>} Sends the formatted I/O time data or an error response. |
| */ |
| async function getTimeSpentDoingIO(req, res) { |
| const { from = "now-5m", until = "now", step = 28 } = req.body; |
| |
| const query = "rate(node_disk_io_time_seconds_total{device='vda', job='node_exporter'}[5m])"; |
| |
| try { |
| const baseUrl = buildUrl(getEnv("NODE_EXPORTER_BASE_URL"), { |
| query, |
| start: from, |
| end: until, |
| step, |
| }); |
| |
| const config = { |
| method: 'get', |
| url: baseUrl, |
| }; |
| |
| const response = await axios.request(config); |
| const result = response?.data?.data?.result?.[0]?.values || []; |
| |
| const formattedData = result.map(([timestamp, value]) => ({ |
| time: new Date(timestamp * 1000).toISOString(), |
| value: parseFloat(value), |
| })); |
| |
| return res.send({ data: formattedData }); |
| } catch (error) { |
| logger.error("Error fetching Disk IO Time data:", error); |
| return res.status(400).send({ |
| error: error.message || "Failed to fetch Disk IO Time data", |
| }); |
| } |
| } |
| |
| /** |
| * Fetches Disk Read and Write Merged data from Node Exporter. |
| * |
| * @async |
| * @function getDiskRWData |
| * @param {Object} req - The HTTP request object containing query parameters in the body. |
| * @param {Object} res - The HTTP response object. |
| * @returns {Promise<void>} Sends the formatted Disk Read and Write Merged data or an error response. |
| */ |
| async function getDiskRWData(req, res) { |
| const { from = "now-5m", until = "now", step = 28 } = req.body; |
| |
| try { |
| const queries = { |
| readMerged: "rate(node_disk_reads_merged_total{device='vda'}[5m])", |
| writeMerged: "rate(node_disk_writes_merged_total{device='vda'}[5m])", |
| }; |
| |
| const responses = await Promise.all( |
| Object.entries(queries).map(async ([key, query]) => { |
| const baseUrl = buildUrl(getEnv("NODE_EXPORTER_BASE_URL"), { |
| query, |
| start: from, |
| end: until, |
| step, |
| }); |
| const response = await axios.get(baseUrl); |
| return { |
| key, |
| values: response?.data?.data?.result?.[0]?.values || [], |
| }; |
| }) |
| ); |
| |
| const formattedData = responses.reduce((acc, { key, values }) => { |
| acc[key] = values.map(([timestamp, value]) => ({ |
| time: new Date(timestamp * 1000).toISOString(), |
| value: parseFloat(value), |
| })); |
| return acc; |
| }, {}); |
| |
| return res.send({ data: formattedData }); |
| } catch (error) { |
| logger.error(error); |
| return res.status(400).send({ |
| error: error.message || "An error occurred while fetching Disk Merged Data", |
| }); |
| } |
| } |
| |
| module.exports = { |
| getCpuUsage, |
| getDiskIOPS, |
| getDiskWaitTime, |
| getTimeSpentDoingIO, |
| getDiskRWData, |
| }; |