Provider details
diff --git a/assets/js/pages/HostDetailsPage/HostDetails.stories.jsx b/assets/js/pages/HostDetailsPage/HostDetails.stories.jsx
index 7ade5a6703..4e9dd12b55 100644
--- a/assets/js/pages/HostDetailsPage/HostDetails.stories.jsx
+++ b/assets/js/pages/HostDetailsPage/HostDetails.stories.jsx
@@ -55,13 +55,6 @@ export default {
control: 'object',
description: 'Status of the prometheus exporters',
},
- grafanaPublicUrl: {
- control: 'text',
- description: 'Grafana dashboard public URL',
- table: {
- type: { summary: 'string' },
- },
- },
heartbeat: {
control: { type: 'radio' },
options: ['passing', 'critical'],
@@ -165,7 +158,6 @@ export const Default = {
deregisterable: false,
deregistering: false,
exportersStatus: {},
- grafanaPublicUrl: 'some-url',
heartbeat: host.heartbeat,
hostID: host.id,
hostname: host.hostname,
diff --git a/assets/js/pages/HostDetailsPage/HostDetails.test.jsx b/assets/js/pages/HostDetailsPage/HostDetails.test.jsx
index 40f6c5e369..c0ef385435 100644
--- a/assets/js/pages/HostDetailsPage/HostDetails.test.jsx
+++ b/assets/js/pages/HostDetailsPage/HostDetails.test.jsx
@@ -4,6 +4,8 @@ import userEvent from '@testing-library/user-event';
import 'intersection-observer';
import '@testing-library/jest-dom';
import { faker } from '@faker-js/faker';
+import { networkClient } from '@lib/network';
+import MockAdapter from 'axios-mock-adapter';
import { renderWithRouter } from '@lib/test-utils';
import { hostFactory, saptuneStatusFactory } from '@lib/test-utils/factories';
@@ -11,7 +13,14 @@ import { TUNING_VALUES } from '@pages/SaptuneDetails/SaptuneDetails.test';
import HostDetails from './HostDetails';
+const axiosMock = new MockAdapter(networkClient);
+
describe('HostDetails component', () => {
+ beforeEach(() => {
+ axiosMock.reset();
+ axiosMock.onGet(/\/api\/v1\/charts.*/gm).reply(200, {});
+ });
+
describe('Checks execution', () => {
it('should show the Checks related action buttons', () => {
renderWithRouter();
diff --git a/assets/js/pages/HostDetailsPage/HostDetailsPage.jsx b/assets/js/pages/HostDetailsPage/HostDetailsPage.jsx
index 5933ee03f4..ad799585b0 100644
--- a/assets/js/pages/HostDetailsPage/HostDetailsPage.jsx
+++ b/assets/js/pages/HostDetailsPage/HostDetailsPage.jsx
@@ -23,9 +23,6 @@ import {
import { deregisterHost } from '@state/hosts';
import HostDetails from './HostDetails';
-// eslint-disable-next-line no-undef
-const { grafanaPublicUrl } = config;
-
function HostDetailsPage() {
const { hostID } = useParams();
const dispatch = useDispatch();
@@ -79,7 +76,6 @@ function HostDetailsPage() {
deregisterable={host.deregisterable}
deregistering={host.deregistering}
exportersStatus={exportersStatus}
- grafanaPublicUrl={grafanaPublicUrl}
heartbeat={host.heartbeat}
hostID={host.id}
hostname={host.hostname}
diff --git a/assets/package-lock.json b/assets/package-lock.json
index 3c321b022e..1fab2ac7aa 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -10,6 +10,10 @@
"@reduxjs/toolkit": "^1.9.7",
"axios": "^1.6.2",
"axios-auth-refresh": "^3.3.6",
+ "canvas": "^2.11.2",
+ "chart.js": "^4.4.0",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "chartjs-plugin-zoom": "^2.0.1",
"classnames": "^2.3.2",
"date-fns": "^2.30.0",
"dayjs": "^1.11.10",
@@ -19,6 +23,7 @@
"rc-tooltip": "^6.1.3",
"react": "^18.2.0",
"react-accessible-treeview": "^2.8.1",
+ "react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.12",
"react-hot-toast": "^2.4.1",
@@ -3505,6 +3510,30 @@
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==",
"dev": true
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
+ "node_modules/@mapbox/node-pre-gyp": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
+ "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "make-dir": "^3.1.0",
+ "node-fetch": "^2.6.7",
+ "nopt": "^5.0.0",
+ "npmlog": "^5.0.1",
+ "rimraf": "^3.0.2",
+ "semver": "^7.3.5",
+ "tar": "^6.1.11"
+ },
+ "bin": {
+ "node-pre-gyp": "bin/node-pre-gyp"
+ }
+ },
"node_modules/@mdx-js/react": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz",
@@ -7708,6 +7737,11 @@
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
"dev": true
},
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -7774,7 +7808,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "dev": true,
"dependencies": {
"debug": "4"
},
@@ -7902,7 +7935,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -7947,6 +7979,23 @@
"integrity": "sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==",
"dev": true
},
+ "node_modules/aproba": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+ },
+ "node_modules/are-we-there-yet": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+ "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+ "dependencies": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
@@ -8631,8 +8680,7 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -8786,7 +8834,6 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -9026,6 +9073,20 @@
}
]
},
+ "node_modules/canvas": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
+ "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@mapbox/node-pre-gyp": "^1.0.0",
+ "nan": "^2.17.0",
+ "simple-get": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/case-sensitive-paths-webpack-plugin": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
@@ -9078,6 +9139,37 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/chart.js": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz",
+ "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=7"
+ }
+ },
+ "node_modules/chartjs-adapter-date-fns": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
+ "integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
+ "peerDependencies": {
+ "chart.js": ">=2.8.0",
+ "date-fns": ">=2.0.0"
+ }
+ },
+ "node_modules/chartjs-plugin-zoom": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-2.0.1.tgz",
+ "integrity": "sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==",
+ "dependencies": {
+ "hammerjs": "^2.0.8"
+ },
+ "peerDependencies": {
+ "chart.js": ">=3.2.0"
+ }
+ },
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -9121,7 +9213,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
- "dev": true,
"engines": {
"node": ">=10"
}
@@ -9306,6 +9397,14 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
@@ -9416,8 +9515,7 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"node_modules/concat-stream": {
"version": "1.6.2",
@@ -9470,6 +9568,11 @@
"integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
"dev": true
},
+ "node_modules/console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+ },
"node_modules/constants-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@@ -9851,6 +9954,17 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/decompress-response": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
+ "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+ "dependencies": {
+ "mimic-response": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
@@ -9976,6 +10090,11 @@
"node": ">=0.4.0"
}
},
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+ },
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -10012,6 +10131,14 @@
"node": ">=8"
}
},
+ "node_modules/detect-libc": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
+ "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -10322,8 +10449,7 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/emojis-list": {
"version": "3.0.0",
@@ -11976,7 +12102,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
- "dev": true,
"dependencies": {
"minipass": "^3.0.0"
},
@@ -11988,7 +12113,6 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -12005,8 +12129,7 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/fsevents": {
"version": "2.3.2",
@@ -12057,6 +12180,25 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gauge": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+ "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+ "dependencies": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.2",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.1",
+ "object-assign": "^4.1.1",
+ "signal-exit": "^3.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -12210,7 +12352,6 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -12337,6 +12478,14 @@
"gunzip-maybe": "bin.js"
}
},
+ "node_modules/hammerjs": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+ "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/handlebars": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
@@ -12460,6 +12609,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+ },
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -12715,7 +12869,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "dev": true,
"dependencies": {
"agent-base": "6",
"debug": "4"
@@ -12925,7 +13078,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -12934,8 +13086,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/inline-style-parser": {
"version": "0.1.1",
@@ -13193,7 +13344,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -15167,7 +15317,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
"dependencies": {
"semver": "^6.0.0"
},
@@ -15182,7 +15331,6 @@
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -16280,6 +16428,17 @@
"node": ">=6"
}
},
+ "node_modules/mimic-response": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
+ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -16293,7 +16452,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -16311,7 +16469,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -16320,7 +16477,6 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
- "dev": true,
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
@@ -16333,7 +16489,6 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -16390,6 +16545,11 @@
"thenify-all": "^1.0.0"
}
},
+ "node_modules/nan": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
+ "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
+ },
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -16460,7 +16620,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dev": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -16494,6 +16653,20 @@
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"dev": true
},
+ "node_modules/nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -16545,6 +16718,17 @@
"node": ">=8"
}
},
+ "node_modules/npmlog": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+ "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+ "dependencies": {
+ "are-we-there-yet": "^2.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^3.0.0",
+ "set-blocking": "^2.0.0"
+ }
+ },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -16736,7 +16920,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -16934,7 +17117,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -17866,6 +18048,15 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-chartjs-2": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
+ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-colorful": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
@@ -18335,7 +18526,6 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -18751,7 +18941,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -18965,6 +19154,11 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+ },
"node_modules/set-function-name": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
@@ -19035,8 +19229,36 @@
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/simple-get": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
+ "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
+ "dependencies": {
+ "decompress-response": "^4.2.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
},
"node_modules/simple-update-notifier": {
"version": "2.0.0",
@@ -19209,7 +19431,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
- "dev": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
@@ -19218,7 +19439,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -19251,7 +19471,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -19344,7 +19563,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -19577,7 +19795,6 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
- "dev": true,
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@@ -19628,7 +19845,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
@@ -19958,8 +20174,7 @@
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/trim-lines": {
"version": "3.0.1",
@@ -20583,8 +20798,7 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
- "dev": true
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/utila": {
"version": "0.4.0",
@@ -20775,8 +20989,7 @@
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/webpack": {
"version": "5.76.1",
@@ -20990,7 +21203,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dev": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
@@ -21087,6 +21299,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/wide-align": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+ "dependencies": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -21131,8 +21351,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/write-file-atomic": {
"version": "4.0.2",
diff --git a/assets/package.json b/assets/package.json
index 19a8760340..efe03c9078 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -49,6 +49,10 @@
"@reduxjs/toolkit": "^1.9.7",
"axios": "^1.6.2",
"axios-auth-refresh": "^3.3.6",
+ "canvas": "^2.11.2",
+ "chart.js": "^4.4.0",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "chartjs-plugin-zoom": "^2.0.1",
"classnames": "^2.3.2",
"date-fns": "^2.30.0",
"dayjs": "^1.11.10",
@@ -58,6 +62,7 @@
"rc-tooltip": "^6.1.3",
"react": "^18.2.0",
"react-accessible-treeview": "^2.8.1",
+ "react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.12",
"react-hot-toast": "^2.4.1",
diff --git a/config/config.exs b/config/config.exs
index 4dee8fd332..0fa50e12cc 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -141,12 +141,7 @@ config :trento, Trento.Infrastructure.Prometheus,
config :trento, Trento.Infrastructure.Prometheus.PrometheusApi, url: "http://localhost:9090"
-config :trento, :grafana,
- user: "admin",
- password: "admin",
- public_url: "http://localhost:3000",
- api_url: "http://localhost:3000/api",
- dashboards: ["node_exporter"]
+config :trento, Trento.Charts, host_data_fetcher: Trento.Infrastructure.Prometheus.PrometheusApi
config :trento,
uuid_namespace: "fb92284e-aa5e-47f6-a883-bf9469e7a0dc",
diff --git a/config/runtime.exs b/config/runtime.exs
index 2818e41e02..148e55a672 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -80,12 +80,6 @@ if config_env() in [:prod, :demo] do
config :trento, :checks_service, base_url: System.get_env("CHECKS_SERVICE_BASE_URL") || ""
- config :trento, :grafana,
- user: System.get_env("GRAFANA_USER") || "admin",
- password: System.get_env("GRAFANA_PASSWORD") || "admin",
- public_url: System.get_env("GRAFANA_PUBLIC_URL") || "http://localhost:3000",
- api_url: System.get_env("GRAFANA_API_URL") || "http://localhost:3000/api"
-
config :trento, Trento.Infrastructure.Prometheus.PrometheusApi,
url: System.get_env("PROMETHEUS_URL") || "http://localhost:9090"
diff --git a/container_fixtures/prometheus/prometheus_entrypoint.sh b/container_fixtures/prometheus/prometheus_entrypoint.sh
new file mode 100755
index 0000000000..7bc8ccd56f
--- /dev/null
+++ b/container_fixtures/prometheus/prometheus_entrypoint.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+echo -n "starting prometheus seed using /container_init/prometheus_snap.tar.xz"
+
+tar -xvf /container_init/prometheus_snap.tar.xz -C /prometheus
+
+echo -n "starting prometheus will the provided arguments"
+
+exec /bin/prometheus $@
+
diff --git a/container_fixtures/prometheus/prometheus_snap.tar.xz b/container_fixtures/prometheus/prometheus_snap.tar.xz
new file mode 100644
index 0000000000..7563849993
Binary files /dev/null and b/container_fixtures/prometheus/prometheus_snap.tar.xz differ
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 269e3145e1..9a6f5e31fc 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,7 +1,36 @@
version: "3"
volumes:
pg_data:
+ prometheus_data:
services:
+ node-exporter:
+ image: prom/node-exporter:v1.7.0
+ volumes:
+ - /proc:/host/proc:ro
+ - /sys:/host/sys:ro
+ - /:/rootfs:ro
+ command:
+ - '--path.procfs=/host/proc'
+ - '--path.rootfs=/rootfs'
+ - '--path.sysfs=/host/sys'
+ - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
+ prometheus:
+ image: prom/prometheus:v2.48.1
+ user: "0:0"
+ volumes:
+ - ./prometheus-dev-config.yml:/etc/prometheus/prometheus.yml
+ - ./container_fixtures/prometheus:/container_init
+ - prometheus_data:/prometheus
+ entrypoint: "/container_init/prometheus_entrypoint.sh"
+ command:
+ - '--config.file=/etc/prometheus/prometheus.yml'
+ - '--storage.tsdb.path=/prometheus'
+ - '--web.console.libraries=/etc/prometheus/console_libraries'
+ - '--web.console.templates=/etc/prometheus/consoles'
+ - '--web.enable-lifecycle'
+ - '--web.enable-admin-api'
+ ports:
+ - 9090:9090
postgres:
image: postgres:latest
environment:
@@ -12,13 +41,6 @@ services:
- 5433:5432
volumes:
- pg_data:/var/lib/postgresql/data
- grafana:
- image: grafana/grafana:latest
- environment:
- GF_SECURITY_ALLOW_EMBEDDING: "true"
- GF_AUTH_ANONYMOUS_ENABLED: "true"
- ports:
- - 3000:3000
rabbitmq:
image: rabbitmq:3.12.6-management-alpine
ports:
diff --git a/guides/development/environment_variables.md b/guides/development/environment_variables.md
index 8e94e5ba8a..fffed1b550 100644
--- a/guides/development/environment_variables.md
+++ b/guides/development/environment_variables.md
@@ -25,10 +25,7 @@ Dig into [./config](https://github.com/trento-project/web/blob/main/config/) dir
- `RUNNER_URL`
**Monitoring**
-- `GRAFANA_USER`
-- `GRAFANA_PASSWORD`
-- `GRAFANA_PUBLIC_URL`
-- `GRAFANA_API_URL`
+- `PROMETHEUS_URL`
**Alerting**
- `ENABLE_ALERTING`
diff --git a/guides/development/hack_on_the_trento.md b/guides/development/hack_on_the_trento.md
index a70134a6d8..6d21892d68 100644
--- a/guides/development/hack_on_the_trento.md
+++ b/guides/development/hack_on_the_trento.md
@@ -30,7 +30,7 @@ asdf install
## Development environment
-The Trento project provides a docker-compose development environment that is used to start a Postgres database and a Grafana instance for storage and monitoring. To start the development environment, navigate to the root directory of the Trento project and run the following command:
+The Trento project provides a docker-compose development environment that is used to start a Postgres database and a prometheus instance for storage and monitoring. To start the development environment, navigate to the root directory of the Trento project and run the following command:
```
docker-compose up -d
diff --git a/guides/monitoring/monitoring.md b/guides/monitoring/monitoring.md
index 811a4c961b..36c72cd5fc 100644
--- a/guides/monitoring/monitoring.md
+++ b/guides/monitoring/monitoring.md
@@ -1,17 +1,14 @@
# Monitoring
-Currently Trento provides a basic integration with [Prometheus](https://github.com/prometheus/prometheus) and [Grafana](https://github.com/grafana/grafana) that gives realtime information of the following metrics:
+Currently Trento provides a basic integration with [Prometheus](https://github.com/prometheus/prometheus) that gives realtime information of the following metrics:
- Host CPU usage
- Host Memory usage
-Current integration strategy: Grafana Charts embedded in Trento UI (_Host Details_).
+Current integration strategy: Custom charts Trento UI (_Host Details_).
In order for monitoring to properly work here's the required environment variables
-- `GRAFANA_PUBLIC_URL` -> publicly accessible grafana url, the one embedded in iframes
-- `GRAFANA_API_URL` -> grafana API endpoint for dashboard initialization
-- `GRAFANA_USER` -> user allowed to perform operations on grafana
-- `GRAFANA_PASSWORD` -> well, the password
+- `PROMETHEUS_URL` -> prometheus URL, should be accessible from the web backend, it's not mandatory to expose on the internet.
On a full Trento installation monitoring is enabled by default and the configuration is handled by the helm-charts.
diff --git a/lib/mix/tasks/init_grafana_dashboards.ex b/lib/mix/tasks/init_grafana_dashboards.ex
deleted file mode 100644
index 2d67b67187..0000000000
--- a/lib/mix/tasks/init_grafana_dashboards.ex
+++ /dev/null
@@ -1,24 +0,0 @@
-defmodule Mix.Tasks.InitGrafanaDashboards do
- @moduledoc """
- Init Grafana Dashboards
- """
-
- use Mix.Task
-
- import Trento.Tasks.Helper
-
- alias Trento.Infrastructure.Grafana
-
- @shortdoc "Init Grafana dashboards."
- def run(_) do
- Application.ensure_all_started(:hackney)
-
- case Grafana.init_dashboards() do
- :ok ->
- IO.puts(IO.ANSI.green() <> "Grafana dashboards initialized.")
-
- {:error, reason} ->
- print_error("Failed to init grafana dashboards: #{reason}")
- end
- end
-end
diff --git a/lib/trento/charts.ex b/lib/trento/charts.ex
new file mode 100644
index 0000000000..a8c6f95c6a
--- /dev/null
+++ b/lib/trento/charts.ex
@@ -0,0 +1,90 @@
+defmodule Trento.Charts do
+ @moduledoc """
+ Charts module, responsible for assembling the charts
+ """
+
+ alias Trento.Hosts
+
+ alias Trento.Charts.Hosts.{
+ HostCpuChart,
+ HostMemoryChart
+ }
+
+ alias Trento.Charts.ChartTimeSeries
+
+ @spec host_cpu_chart(String.t(), DateTime.t(), DateTime.t()) ::
+ {:ok, HostCpuChart.t()} | {:error, any}
+ def host_cpu_chart(host_id, from, to) do
+ with {:ok, _} <- Hosts.by_host_id(host_id),
+ {:ok, num_cpus} <- host_data_fetcher().num_cpus(from, to),
+ {:ok, cpu_busy_iowait_samples} <-
+ host_data_fetcher().cpu_busy_iowait(host_id, from, to),
+ {:ok, cpu_idle_samples} <- host_data_fetcher().cpu_idle(host_id, from, to),
+ {:ok, cpu_busy_system_samples} <-
+ host_data_fetcher().cpu_busy_system(host_id, from, to),
+ {:ok, cpu_busy_user_samples} <- host_data_fetcher().cpu_busy_user(host_id, from, to),
+ {:ok, cpu_busy_irqs_samples} <- host_data_fetcher().cpu_busy_irqs(host_id, from, to),
+ {:ok, cpu_busy_other_samples} <-
+ host_data_fetcher().cpu_busy_other(host_id, from, to) do
+ {:ok,
+ %HostCpuChart{
+ busy_iowait: %ChartTimeSeries{
+ label: "cpu_busy_iowait",
+ series: normalize_cpu_series_to_num_cpus(cpu_busy_iowait_samples, num_cpus)
+ },
+ idle: %ChartTimeSeries{
+ label: "cpu_idle",
+ series: normalize_cpu_series_to_num_cpus(cpu_idle_samples, num_cpus)
+ },
+ busy_system: %ChartTimeSeries{
+ label: "cpu_busy_system",
+ series: normalize_cpu_series_to_num_cpus(cpu_busy_system_samples, num_cpus)
+ },
+ busy_user: %ChartTimeSeries{
+ label: "cpu_busy_user",
+ series: normalize_cpu_series_to_num_cpus(cpu_busy_user_samples, num_cpus)
+ },
+ busy_other: %ChartTimeSeries{
+ label: "cpu_busy_other",
+ series: normalize_cpu_series_to_num_cpus(cpu_busy_other_samples, num_cpus)
+ },
+ busy_irqs: %ChartTimeSeries{
+ label: "cpu_busy_irqs",
+ series: normalize_cpu_series_to_num_cpus(cpu_busy_irqs_samples, num_cpus)
+ }
+ }}
+ end
+ end
+
+ @spec host_memory_chart(String.t(), DateTime.t(), DateTime.t()) ::
+ {:ok, HostMemoryChart.t()} | {:error, any}
+ def host_memory_chart(host_id, from, to) do
+ with {:ok, _} <- Hosts.by_host_id(host_id),
+ {:ok, ram_total_samples} <- host_data_fetcher().ram_total(host_id, from, to),
+ {:ok, ram_used_samples} <- host_data_fetcher().ram_used(host_id, from, to),
+ {:ok, ram_cache_and_buffer_samples} <-
+ host_data_fetcher().ram_cache_and_buffer(host_id, from, to),
+ {:ok, ram_free_samples} <- host_data_fetcher().ram_free(host_id, from, to),
+ {:ok, swap_used_samples} <- host_data_fetcher().swap_used(host_id, from, to) do
+ {:ok,
+ %HostMemoryChart{
+ ram_total: %ChartTimeSeries{label: "ram_total", series: ram_total_samples},
+ ram_used: %ChartTimeSeries{label: "ram_used", series: ram_used_samples},
+ ram_cache_and_buffer: %ChartTimeSeries{
+ label: "ram_cache_and_buffer",
+ series: ram_cache_and_buffer_samples
+ },
+ ram_free: %ChartTimeSeries{label: "ram_free", series: ram_free_samples},
+ swap_used: %ChartTimeSeries{label: "swap_used", series: swap_used_samples}
+ }}
+ end
+ end
+
+ defp host_data_fetcher, do: Application.fetch_env!(:trento, __MODULE__)[:host_data_fetcher]
+
+ defp normalize_cpu_series_to_num_cpus(cpu_series, num_cpus) do
+ Enum.map(cpu_series, fn %{timestamp: timestamp, value: value} ->
+ %{timestamp: timestamp, value: value / num_cpus}
+ end)
+ end
+end
diff --git a/lib/trento/charts/chart_time_series.ex b/lib/trento/charts/chart_time_series.ex
new file mode 100644
index 0000000000..afccf58db8
--- /dev/null
+++ b/lib/trento/charts/chart_time_series.ex
@@ -0,0 +1,16 @@
+defmodule Trento.Charts.ChartTimeSeries do
+ @moduledoc """
+ Represents a time series of a chart
+ The series has a label and the samples distributed through time
+ """
+
+ alias Trento.Charts.ChartTimeSeriesSample
+
+ @enforce_keys [:label, :series]
+ defstruct [:label, :series]
+
+ @type t :: %__MODULE__{
+ label: String.t(),
+ series: [ChartTimeSeriesSample.t()]
+ }
+end
diff --git a/lib/trento/charts/chart_time_series_sample.ex b/lib/trento/charts/chart_time_series_sample.ex
new file mode 100644
index 0000000000..ef74969a3d
--- /dev/null
+++ b/lib/trento/charts/chart_time_series_sample.ex
@@ -0,0 +1,14 @@
+defmodule Trento.Charts.ChartTimeSeriesSample do
+ @moduledoc """
+ ChartTimeSeriesSample represent a sample in a chart timeseries according to the Trento Domain.
+
+ Contains a timestamp, as DateTime and a float value.
+ """
+ @enforce_keys [:timestamp, :value]
+ defstruct [:timestamp, :value]
+
+ @type t :: %__MODULE__{
+ timestamp: DateTime.t(),
+ value: float()
+ }
+end
diff --git a/lib/trento/charts/hosts/host_cpu_chart.ex b/lib/trento/charts/hosts/host_cpu_chart.ex
new file mode 100644
index 0000000000..0896c27ed9
--- /dev/null
+++ b/lib/trento/charts/hosts/host_cpu_chart.ex
@@ -0,0 +1,27 @@
+defmodule Trento.Charts.Hosts.HostCpuChart do
+ @moduledoc """
+ Represents CPU chart data for a single host
+
+ The chart has different series
+ - busy_iowait
+ - busy_irqs
+ - busy_other
+ - busy_system
+ - busy_user
+ - idle
+ """
+
+ alias Trento.Charts.ChartTimeSeries
+
+ @enforce_keys [:busy_iowait, :idle, :busy_system, :busy_user, :busy_other, :busy_irqs]
+ defstruct [:busy_iowait, :idle, :busy_system, :busy_user, :busy_other, :busy_irqs]
+
+ @type t :: %__MODULE__{
+ busy_iowait: [ChartTimeSeries.t()],
+ busy_irqs: [ChartTimeSeries.t()],
+ busy_other: [ChartTimeSeries.t()],
+ busy_system: [ChartTimeSeries.t()],
+ busy_user: [ChartTimeSeries.t()],
+ idle: [ChartTimeSeries.t()]
+ }
+end
diff --git a/lib/trento/charts/hosts/host_data_fetcher.ex b/lib/trento/charts/hosts/host_data_fetcher.ex
new file mode 100644
index 0000000000..0edbadde05
--- /dev/null
+++ b/lib/trento/charts/hosts/host_data_fetcher.ex
@@ -0,0 +1,43 @@
+defmodule Trento.Charts.HostDataFetcher do
+ @moduledoc """
+ Behaviour of host charts data fetcher
+ """
+
+ alias Trento.Charts.ChartTimeSeriesSample
+
+ @callback cpu_busy_iowait(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback cpu_idle(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback cpu_busy_system(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback cpu_busy_user(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback cpu_busy_other(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback cpu_busy_irqs(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback ram_total(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback ram_used(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback ram_cache_and_buffer(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback ram_free(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback swap_used(host_id :: String.t(), from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+
+ @callback num_cpus(from :: DateTime.t(), to :: DateTime.t()) ::
+ {:ok, integer()} | {:error, any}
+end
diff --git a/lib/trento/charts/hosts/host_memory_chart.ex b/lib/trento/charts/hosts/host_memory_chart.ex
new file mode 100644
index 0000000000..26af3eb105
--- /dev/null
+++ b/lib/trento/charts/hosts/host_memory_chart.ex
@@ -0,0 +1,25 @@
+defmodule Trento.Charts.Hosts.HostMemoryChart do
+ @moduledoc """
+ Represents Memory chart data for a single host
+
+ The chart has different series
+ - ram_total
+ - ram_used
+ - ram_cache_and_buffer
+ - ram_free
+ - swap_used
+ """
+
+ alias Trento.Charts.ChartTimeSeries
+
+ @enforce_keys [:ram_total, :ram_used, :ram_cache_and_buffer, :ram_free, :swap_used]
+ defstruct [:ram_total, :ram_used, :ram_cache_and_buffer, :ram_free, :swap_used]
+
+ @type t :: %__MODULE__{
+ ram_total: [ChartTimeSeries.t()],
+ ram_cache_and_buffer: [ChartTimeSeries.t()],
+ ram_free: [ChartTimeSeries.t()],
+ ram_used: [ChartTimeSeries.t()],
+ swap_used: [ChartTimeSeries.t()]
+ }
+end
diff --git a/lib/trento/hosts.ex b/lib/trento/hosts.ex
index b9bbf90820..1887d43490 100644
--- a/lib/trento/hosts.ex
+++ b/lib/trento/hosts.ex
@@ -119,6 +119,15 @@ defmodule Trento.Hosts do
)
end
+ def by_host_id(host_id) do
+ case HostReadModel
+ |> where([h], h.id == ^host_id and is_nil(h.deregistered_at))
+ |> Repo.one() do
+ nil -> {:error, :not_found}
+ host -> {:ok, host}
+ end
+ end
+
@spec enrich_host_read_model_query(Ecto.Query.t()) :: Ecto.Query.t()
defp enrich_host_read_model_query(query) do
query
diff --git a/lib/trento/infrastructure/grafana/grafana.ex b/lib/trento/infrastructure/grafana/grafana.ex
deleted file mode 100644
index 305bf9289d..0000000000
--- a/lib/trento/infrastructure/grafana/grafana.ex
+++ /dev/null
@@ -1,112 +0,0 @@
-defmodule Trento.Infrastructure.Grafana do
- @moduledoc """
- Grafana integration service
- """
-
- require Logger
-
- @retries 10
- @retry_after 1_000
-
- def init_dashboards do
- dashboards = Application.fetch_env!(:trento, :grafana)[:dashboards]
-
- Enum.reduce_while(dashboards, :ok, fn dashboard_name, _ ->
- case init_dashboard(dashboard_name) do
- :ok ->
- {:cont, :ok}
-
- error ->
- {:halt, error}
- end
- end)
- end
-
- def init_dashboard(dashboard_name) do
- case load_dashboard(dashboard_name) do
- {:ok, content} ->
- create_dashboard(content, @retries - 1)
-
- {:error, reason} = error ->
- Logger.error("Failed to load grafana dashboard #{dashboard_name}", error: reason)
- error
- end
- end
-
- defp create_dashboard(_, 0) do
- Logger.error("Failed to create grafana dashboard")
- {:error, :max_retries_reached}
- end
-
- defp create_dashboard(content, retry) do
- with {:ok, token} <- create_token(),
- :ok <- post_dashboard(content, token) do
- :ok
- else
- {:error, reason} ->
- Logger.error("Failed to create grafana dashboard, retrying...", error: inspect(reason))
- Process.sleep(@retry_after)
- create_dashboard(content, retry - 1)
- end
- end
-
- defp load_dashboard(name) do
- grafana_dashboards_path = Application.app_dir(:trento, "/priv/data/grafana/")
-
- grafana_dashboards_path
- |> Path.join("#{name}.json")
- |> File.read()
- end
-
- defp create_token do
- payload =
- Jason.encode!(%{
- "role" => "Admin",
- "name" => UUID.uuid4(),
- "secondsToLive" => 60
- })
-
- api_url = Application.fetch_env!(:trento, :grafana)[:api_url]
- user = Application.fetch_env!(:trento, :grafana)[:user]
- password = Application.fetch_env!(:trento, :grafana)[:password]
- credentials = Base.encode64("#{user}:#{password}")
-
- with {:ok, %HTTPoison.Response{body: body, status_code: 200}} <-
- HTTPoison.post(
- "#{api_url}/auth/keys",
- payload,
- [{"Content-type", "application/json"}, {"Authorization", "Basic #{credentials}"}]
- ),
- {:ok, %{"key" => token}} <- Jason.decode(body) do
- {:ok, token}
- else
- %HTTPoison.Response{body: body, status_code: status_code} ->
- Logger.error("Failed to create grafana token, status code: #{status_code}",
- body: inspect(body)
- )
-
- {:error, :failed_to_create_token}
-
- {:error, reason} ->
- Logger.error("Failed to create grafana token", error: inspect(reason))
- {:error, :failed_to_create_token}
- end
- end
-
- defp post_dashboard(content, token) do
- api_url = Application.fetch_env!(:trento, :grafana)[:api_url]
-
- case HTTPoison.post(
- "#{api_url}/dashboards/db",
- content,
- [{"Content-type", "application/json"}, {"Authorization", "Bearer #{token}"}]
- ) do
- {:ok, %HTTPoison.Response{status_code: 200}} ->
- :ok
-
- {:error, reason} ->
- Logger.error("Failed to create grafana dashboard", error: inspect(reason))
- {:error, :failed_to_create_dashboard}
- end
- end
-end
diff --git a/lib/trento/infrastructure/prometheus/adapter/prometheus_api.ex b/lib/trento/infrastructure/prometheus/adapter/prometheus_api.ex
index 2bc09304a9..b64c4830ff 100644
--- a/lib/trento/infrastructure/prometheus/adapter/prometheus_api.ex
+++ b/lib/trento/infrastructure/prometheus/adapter/prometheus_api.ex
@@ -7,9 +7,95 @@ defmodule Trento.Infrastructure.Prometheus.PrometheusApi do
alias Trento.Repo
+ alias Trento.Infrastructure.Prometheus.ChartIntegration
+
require Logger
@behaviour Trento.Infrastructure.Prometheus.Gen
+ @behaviour Trento.Charts.HostDataFetcher
+
+ def ram_total(host_id, from, to) do
+ query = "node_memory_MemTotal_bytes{agentID=\"#{host_id}\"}"
+
+ perform_query_range(query, from, to)
+ end
+
+ def ram_used(host_id, from, to) do
+ query =
+ "node_memory_MemTotal_bytes{agentID=\"#{host_id}\"} - node_memory_MemFree_bytes{agentID=\"#{host_id}\"} - (node_memory_Cached_bytes{agentID=\"#{host_id}\"} + node_memory_Buffers_bytes{agentID=\"#{host_id}\"})"
+
+ perform_query_range(query, from, to)
+ end
+
+ def ram_cache_and_buffer(host_id, from, to) do
+ query =
+ "node_memory_Cached_bytes{agentID=\"#{host_id}\"} + node_memory_Buffers_bytes{agentID=\"#{host_id}\"}"
+
+ perform_query_range(query, from, to)
+ end
+
+ def ram_free(host_id, from, to) do
+ query = "node_memory_MemFree_bytes{agentID=\"#{host_id}\"}"
+
+ perform_query_range(query, from, to)
+ end
+
+ def swap_used(host_id, from, to) do
+ query =
+ "(node_memory_SwapTotal_bytes{agentID=\"#{host_id}\"} - node_memory_SwapFree_bytes{agentID=\"#{host_id}\"})"
+
+ perform_query_range(query, from, to)
+ end
+
+ def cpu_busy_irqs(host_id, from, to) do
+ query =
+ "sum by (instance)(irate(node_cpu_seconds_total{mode=~\".*irq\",agentID=\"#{host_id}\"}[5m])) * 100"
+
+ perform_query_range(query, from, to)
+ end
+
+ def cpu_busy_other(host_id, from, to) do
+ query =
+ "sum (irate(node_cpu_seconds_total{mode!='idle',mode!='user',mode!='system',mode!='iowait',mode!='irq',mode!='softirq',agentID=\"#{host_id}\"}[5m])) * 100"
+
+ perform_query_range(query, from, to)
+ end
+
+ def cpu_busy_iowait(host_id, from, to) do
+ query =
+ "sum by (instance)(irate(node_cpu_seconds_total{mode=\"iowait\", agentID=\"#{host_id}\"}[5m])) * 100"
+
+ perform_query_range(query, from, to)
+ end
+
+ def cpu_busy_user(host_id, from, to) do
+ query =
+ "sum by (instance)(irate(node_cpu_seconds_total{mode=\"user\", agentID=\"#{host_id}\"}[5m])) * 100"
+
+ perform_query_range(query, from, to)
+ end
+
+ def cpu_idle(host_id, from, to) do
+ query =
+ "sum by (mode)(irate(node_cpu_seconds_total{mode='idle',agentID=\"#{host_id}\"}[5m])) * 100"
+
+ perform_query_range(query, from, to)
+ end
+
+ def cpu_busy_system(host_id, from, to) do
+ query =
+ "sum by (instance)(irate(node_cpu_seconds_total{mode=\"system\", agentID=\"#{host_id}\"}[5m])) * 100"
+
+ perform_query_range(query, from, to)
+ end
+
+ def num_cpus(from, to) do
+ query = "count(count(node_cpu_seconds_total{}) by (cpu))"
+
+ with {:ok, [%{value: value} | _]} <- perform_query_range(query, from, to) do
+ {:ok, trunc(value)}
+ end
+ end
def get_exporters_status(host_id) do
prometheus_url = Application.fetch_env!(:trento, __MODULE__)[:url]
@@ -56,4 +142,42 @@ defmodule Trento.Infrastructure.Prometheus.PrometheusApi do
:unknown
end}
end
+
+ defp perform_query_range(query, from, to) do
+ prometheus_url = Application.fetch_env!(:trento, __MODULE__)[:url]
+
+ start_parameter = DateTime.to_iso8601(from)
+ end_parameter = DateTime.to_iso8601(to)
+
+ request = %HTTPoison.Request{
+ method: :get,
+ url: "#{prometheus_url}/api/v1/query_range",
+ headers: [{"Accept", "application/json"}],
+ params: %{query: query, start: start_parameter, end: end_parameter, step: "60s"}
+ }
+
+ with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.request(request),
+ {:ok, result_body} <- Jason.decode(body),
+ query_values <- extract_query_values_from_result(result_body),
+ {:ok, samples} <- ChartIntegration.query_values_to_samples(query_values) do
+ {:ok, samples}
+ else
+ %HTTPoison.Response{status_code: status_code, body: body} ->
+ Logger.error(
+ "Unexpected response from Prometheus API, status code: #{status_code}, body: #{inspect(body)}."
+ )
+
+ {:error, :unexpected_response}
+
+ {:error, reason} = error ->
+ Logger.error("Error getting cpu busy system data from Prometheus API: #{inspect(reason)}")
+
+ error
+ end
+ end
+
+ defp extract_query_values_from_result(%{"data" => %{"result" => [%{"values" => query_values}]}}),
+ do: query_values
+
+ defp extract_query_values_from_result(_), do: []
end
diff --git a/lib/trento/infrastructure/prometheus/chart_integration.ex b/lib/trento/infrastructure/prometheus/chart_integration.ex
new file mode 100644
index 0000000000..e1bea3c6df
--- /dev/null
+++ b/lib/trento/infrastructure/prometheus/chart_integration.ex
@@ -0,0 +1,23 @@
+defmodule Trento.Infrastructure.Prometheus.ChartIntegration do
+ @moduledoc """
+ ChartIntegration provides a mechanism for mapping prometheus query information to domain
+ Chart time series objects
+ """
+ alias Trento.Infrastructure.Prometheus.PrometheusSamples
+
+ alias Trento.Charts.ChartTimeSeriesSample
+
+ @spec query_values_to_samples([map()]) :: {:ok, [ChartTimeSeriesSample.t()]} | {:error, any}
+ def query_values_to_samples(query_values) do
+ Enum.reduce_while(query_values, {:ok, []}, fn [timestamp, value], {_, acc} ->
+ with {:ok, utc_timestamp} <- DateTime.from_unix(trunc(timestamp)),
+ {:ok, %PrometheusSamples{value: float_value, timestamp: sample_timestamp}} <-
+ PrometheusSamples.new(%{timestamp: utc_timestamp, value: value}) do
+ {:cont,
+ {:ok, [%ChartTimeSeriesSample{timestamp: sample_timestamp, value: float_value} | acc]}}
+ else
+ {:error, error} -> {:halt, {:error, error}}
+ end
+ end)
+ end
+end
diff --git a/lib/trento/infrastructure/prometheus/prometheus_samples.ex b/lib/trento/infrastructure/prometheus/prometheus_samples.ex
new file mode 100644
index 0000000000..0313048338
--- /dev/null
+++ b/lib/trento/infrastructure/prometheus/prometheus_samples.ex
@@ -0,0 +1,13 @@
+defmodule Trento.Infrastructure.Prometheus.PrometheusSamples do
+ @moduledoc """
+ PrometheusSamples represent a prometheus sample returned from a range query, having a unix timestamp and a float value
+ """
+ @required_fields :all
+
+ use Trento.Support.Type
+
+ deftype do
+ field :timestamp, :utc_datetime_usec
+ field :value, :float
+ end
+end
diff --git a/lib/trento/release.ex b/lib/trento/release.ex
index 7b02cbce23..eb178f60bb 100644
--- a/lib/trento/release.ex
+++ b/lib/trento/release.ex
@@ -12,7 +12,6 @@ defmodule Trento.Release do
migrate()
init_event_store()
migrate_event_store()
- init_grafana_dashboards()
init_admin_user()
end
@@ -63,12 +62,6 @@ defmodule Trento.Release do
Mix.Tasks.PruneEvents.run(args)
end
- def init_grafana_dashboards do
- load_app()
-
- Mix.Tasks.InitGrafanaDashboards.run([])
- end
-
def init_admin_user do
load_app()
Enum.each([:postgrex, :ecto], &Application.ensure_all_started/1)
diff --git a/lib/trento_web/controllers/page_controller.ex b/lib/trento_web/controllers/page_controller.ex
index 61a55b37de..e0020274ba 100644
--- a/lib/trento_web/controllers/page_controller.ex
+++ b/lib/trento_web/controllers/page_controller.ex
@@ -2,13 +2,11 @@ defmodule TrentoWeb.PageController do
use TrentoWeb, :controller
def index(conn, _params) do
- grafana_public_url = Application.fetch_env!(:trento, :grafana)[:public_url]
check_service_base_url = Application.fetch_env!(:trento, :checks_service)[:base_url]
deregistration_debounce = Application.fetch_env!(:trento, :deregistration_debounce)
render(conn, "index.html",
- grafana_public_url: grafana_public_url,
check_service_base_url: check_service_base_url,
deregistration_debounce: deregistration_debounce
)
diff --git a/lib/trento_web/controllers/v1/chart_controller.ex b/lib/trento_web/controllers/v1/chart_controller.ex
new file mode 100644
index 0000000000..e88caed13a
--- /dev/null
+++ b/lib/trento_web/controllers/v1/chart_controller.ex
@@ -0,0 +1,100 @@
+defmodule TrentoWeb.V1.ChartController do
+ use TrentoWeb, :controller
+ use OpenApiSpex.ControllerSpecs
+
+ alias TrentoWeb.OpenApi.V1.Schema
+
+ alias Trento.Charts
+
+ plug OpenApiSpex.Plug.CastAndValidate, json_render_error_v2: true
+ action_fallback TrentoWeb.FallbackController
+
+ tags ["Charts"]
+
+ operation :host_cpu,
+ summary: "Get a CPU chart of a host",
+ description:
+ "Get information about cpu usage for a host, providing a from/to interval as ISO8601 timestamp",
+ parameters: [
+ id: [
+ in: :path,
+ required: true,
+ description: "Host ID",
+ type: %OpenApiSpex.Schema{type: :string, format: :uuid, description: "Host ID"}
+ ],
+ from: [
+ in: :query,
+ required: true,
+ description: "Start of the chart interval, as ISO8601 timestamp",
+ type: %OpenApiSpex.Schema{
+ type: :string,
+ format: :"date-time",
+ description: "Start of the chart interval, as ISO8601 timestamp"
+ }
+ ],
+ to: [
+ in: :query,
+ required: true,
+ description: "End of the chart interval, as ISO8601 timestamp",
+ type: %OpenApiSpex.Schema{
+ type: :string,
+ format: :"date-time",
+ description: "End of the chart interval, as ISO8601 timestamp"
+ }
+ ]
+ ],
+ responses: [
+ ok:
+ {"Information about CPU usage of the host", "application/json", Schema.Chart.HostCpuChart}
+ ]
+
+ def host_cpu(conn, %{id: id, from: from, to: to}) do
+ with {:ok, chart} <- Charts.host_cpu_chart(id, from, to) do
+ render(conn, "host_cpu_chart.json", chart: chart)
+ end
+ end
+
+ operation :host_memory,
+ summary: "Get a Memory chart of a host",
+ description:
+ "Get information about memory usage for a host, providing a from/to interval as ISO8601 timestamp",
+ parameters: [
+ id: [
+ in: :path,
+ required: true,
+ description: "Host ID",
+ type: %OpenApiSpex.Schema{type: :string, format: :uuid, description: "Host ID"}
+ ],
+ from: [
+ in: :query,
+ required: true,
+ description: "Start of the chart interval, as ISO8601 timestamp",
+ type: %OpenApiSpex.Schema{
+ type: :string,
+ format: :"date-time",
+ description: "Start of the chart interval, as ISO8601 timestamp"
+ }
+ ],
+ to: [
+ in: :query,
+ required: true,
+ description: "End of the chart interval, as ISO8601 timestamp",
+ type: %OpenApiSpex.Schema{
+ type: :string,
+ format: :"date-time",
+ description: "End of the chart interval, as ISO8601 timestamp"
+ }
+ ]
+ ],
+ responses: [
+ ok:
+ {"Information about memory usage of the host", "application/json",
+ Schema.Chart.HostMemoryChart}
+ ]
+
+ def host_memory(conn, %{id: id, from: from, to: to}) do
+ with {:ok, chart} <- Charts.host_memory_chart(id, from, to) do
+ render(conn, "host_memory_chart.json", chart: chart)
+ end
+ end
+end
diff --git a/lib/trento_web/openapi/v1/schema/chart.ex b/lib/trento_web/openapi/v1/schema/chart.ex
new file mode 100644
index 0000000000..cf887178c1
--- /dev/null
+++ b/lib/trento_web/openapi/v1/schema/chart.ex
@@ -0,0 +1,75 @@
+defmodule TrentoWeb.OpenApi.V1.Schema.Chart do
+ @moduledoc false
+
+ require OpenApiSpex
+ alias OpenApiSpex.Schema
+
+ defmodule ChartTimeSeries do
+ @moduledoc false
+ OpenApiSpex.schema(%{
+ title: "ChartTimeSeries",
+ description:
+ "A Time Series for a chart, has a series of float values distributed through time",
+ type: :object,
+ properties: %{
+ label: %Schema{type: :string, description: "The name of series"},
+ series: %Schema{
+ type: :array,
+ description: "The values of the series",
+ items: %Schema{
+ type: :object,
+ description: "A timestamp/value pair",
+ properties: %{
+ timestamp: %Schema{
+ type: :string,
+ format: "date-time",
+ description: "ISO8601 timestamp"
+ },
+ value: %Schema{
+ type: :number,
+ example: 270_396.2030,
+ format: :float,
+ description: "Float value"
+ }
+ }
+ }
+ }
+ }
+ })
+ end
+
+ defmodule HostCpuChart do
+ @moduledoc false
+
+ OpenApiSpex.schema(%{
+ title: "HostCpuChart",
+ description: "A Time Series chart with information about the cpu usage of a host",
+ type: :object,
+ properties: %{
+ busy_iowait: ChartTimeSeries,
+ busy_irqs: ChartTimeSeries,
+ busy_other: ChartTimeSeries,
+ busy_system: ChartTimeSeries,
+ busy_user: ChartTimeSeries,
+ idle: ChartTimeSeries
+ }
+ })
+ end
+
+ defmodule HostMemoryChart do
+ @moduledoc false
+
+ OpenApiSpex.schema(%{
+ title: "HostMemoryChart",
+ description: "A Time series chart with information about the memory usage of a host",
+ type: :object,
+ properties: %{
+ ram_total: ChartTimeSeries,
+ ram_cache_and_buffer: ChartTimeSeries,
+ ram_free: ChartTimeSeries,
+ ram_used: ChartTimeSeries,
+ swap_used: ChartTimeSeries
+ }
+ })
+ end
+end
diff --git a/lib/trento_web/router.ex b/lib/trento_web/router.ex
index bdf84feb65..e5494eaf5c 100644
--- a/lib/trento_web/router.ex
+++ b/lib/trento_web/router.ex
@@ -139,6 +139,9 @@ defmodule TrentoWeb.Router do
post "/accept_eula", SettingsController, :accept_eula
get "/hosts/:id/exporters_status", PrometheusController, :exporters_status
+
+ get "/charts/hosts/:id/cpu", ChartController, :host_cpu
+ get "/charts/hosts/:id/memory", ChartController, :host_memory
end
scope "/v2", TrentoWeb.V2 do
diff --git a/lib/trento_web/templates/page/index.html.heex b/lib/trento_web/templates/page/index.html.heex
index b656636703..b1f7755b62 100644
--- a/lib/trento_web/templates/page/index.html.heex
+++ b/lib/trento_web/templates/page/index.html.heex
@@ -2,7 +2,6 @@