diff --git a/public/components/kubernetes/common/gourp_dashboards.tsx b/public/components/kubernetes/common/gourp_dashboards.tsx index ea1dfdde32..3956dd5749 100644 --- a/public/components/kubernetes/common/gourp_dashboards.tsx +++ b/public/components/kubernetes/common/gourp_dashboards.tsx @@ -11,6 +11,7 @@ import { EuiCompressedSuperDatePicker, EuiSpacer, EuiPageHeader, + EuiText, } from '@elastic/eui'; import { Responsive, WidthProvider } from 'react-grid-layout'; import { PlotlyHTMLElement } from 'plotly.js'; @@ -20,6 +21,7 @@ import PPLService from '../../../services/requests/ppl'; import { coreRefs } from '../../../framework/core_refs'; import { uiSettingsService } from '../../../../common/utils'; import { Plt } from '../../../components/visualizations/plotly/plot'; +import { PLOTLY_COLOR } from '../../../../common/constants/shared'; const ResponsiveGridLayout = WidthProvider(Responsive); @@ -52,19 +54,19 @@ const layouts = { lg: [ // Top row with global usage and counts { i: 'clusterCPUUsage', x: 0, y: 0, w: 5, h: 2 }, - { i: 'clusterMemoryUsage', x: 2, y: 0, w: 2, h: 2 }, - { i: 'nodesCount', x: 4, y: 0, w: 1, h: 1 }, - { i: 'namespacesCount', x: 5, y: 0, w: 1, h: 2 }, - { i: 'runningPodsCount', x: 6, y: 0, w: 2, h: 2 }, + { i: 'clusterMemoryUsage', x: 0, y: 3, w: 5, h: 2 }, + { i: 'nodesCount', x: 5, y: 0, w: 1, h: 1 }, + { i: 'namespacesCount', x: 5, y: 1, w: 1, h: 1 }, + { i: 'runningPodsCount', x: 5, y: 2, w: 1, h: 1 }, // Middle row with CPU and RAM utilization - // { i: 'cpuUsage', x: 0, y: 2, w: 2, h: 1 }, - // { i: 'ramUsage', x: 2, y: 2, w: 2, h: 1 }, + // { i: 'ramUsage', x: 5, y: 4, w: 2, h: 1 }, // { i: 'clusterCPUUtilization', x: 4, y: 2, w: 2, h: 1 }, // { i: 'clusterMemoryUtilization', x: 6, y: 2, w: 2, h: 1 }, // Additional information panels - { i: 'runningPodsCategory', x: 0, y: 3, w: 3, h: 2 }, + { i: 'runningPodsCategory', x: 7, y: 0, w: 2, h: 2 }, + { i: 'cpuCoreCount', x: 5, y: 3, w: 1, h: 1 }, { i: 'totalNetworkTraffic', x: 3, y: 3, w: 5, h: 2 }, // Visualization row with detailed charts @@ -85,7 +87,7 @@ const layouts = { { i: 'nodesCount', x: 4, y: 0, w: 1, h: 2 }, { i: 'namespacesCount', x: 5, y: 0, w: 1, h: 2 }, { i: 'runningPodsCount', x: 6, y: 0, w: 2, h: 2 }, - { i: 'cpuUsage', x: 0, y: 2, w: 2, h: 1 }, + { i: 'cpuCoreCount', x: 0, y: 2, w: 2, h: 1 }, { i: 'ramUsage', x: 2, y: 2, w: 2, h: 1 }, { i: 'clusterCPUUtilization', x: 4, y: 2, w: 2, h: 1 }, { i: 'clusterMemoryUtilization', x: 6, y: 2, w: 2, h: 1 }, @@ -104,7 +106,7 @@ const layouts = { { i: 'nodesCount', x: 0, y: 2, w: 2, h: 2 }, { i: 'namespacesCount', x: 2, y: 2, w: 2, h: 2 }, { i: 'runningPodsCount', x: 0, y: 4, w: 2, h: 2 }, - { i: 'cpuUsage', x: 2, y: 4, w: 2, h: 1 }, + { i: 'cpuCoreCount', x: 2, y: 4, w: 2, h: 1 }, { i: 'ramUsage', x: 0, y: 5, w: 2, h: 1 }, { i: 'clusterCPUUtilization', x: 2, y: 5, w: 2, h: 1 }, { i: 'clusterMemoryUtilization', x: 0, y: 6, w: 2, h: 1 }, @@ -149,10 +151,22 @@ export const GroupDashboards = ({ gKeys: string[], traceConfig: {} ) => { - console.log('data: ', data); - console.log('xKeys[0]: ', xKeys[0]); - console.log('yKeys[0]: ', yKeys[0]); - if (gKeys.length === 0) { + if (traceConfig.type === 'pie') { + return [ + { + labels: data.data[xKeys[0]], + values: data.data[yKeys[0]], + ...traceConfig, + }, + ]; + } else if (traceConfig.type === 'indicator') { + return [ + { + value: data?.data[yKeys[0]][0] || 0, + ...traceConfig, + }, + ]; + } else if (gKeys.length === 0) { return [ { x: data.data[xKeys[0]], @@ -187,14 +201,13 @@ export const GroupDashboards = ({ const renderPanel = (metrics: IMetrics[]) => { console.log('metrics', metrics); return metrics.map((metric, index) => { - if (!metric) { - return null; - } + // if (!metric) { + // return []; + // } const visXaxisKeys = prometheusQueries[index].vis.xKeys; const visYaxisKeys = prometheusQueries[index].vis.yKeys; const visGroupingKeys = prometheusQueries[index].vis.gKeys; - console.log('metric: ', metric); const visualizationData = getProcessedMetricsVizData( metric, visXaxisKeys, @@ -202,7 +215,6 @@ export const GroupDashboards = ({ visGroupingKeys, prometheusQueries[index].vis.config ); - console.log('visualizationData: ', visualizationData); // metric // jdbc @@ -214,6 +226,9 @@ export const GroupDashboards = ({ return (
+ +

{prometheusQueries[index].title}

+
- {/* */}
+
); }); diff --git a/public/components/kubernetes/home.tsx b/public/components/kubernetes/home.tsx index 0a5abdd21f..79c06ebc32 100644 --- a/public/components/kubernetes/home.tsx +++ b/public/components/kubernetes/home.tsx @@ -7,29 +7,30 @@ import React from 'react'; import { GroupDashboards } from './common/gourp_dashboards'; import { Line } from '../visualizations/charts/lines/line'; import { convertDateTime } from '../common/query_utils'; +import { Pie } from '../visualizations/charts/pie/pie'; -// function determineExactSpan(startTime: number, endTime: number): string { -// const durationInSeconds = Math.round( -// (new Date(endTime).getTime() - new Date(startTime).getTime()) / 1000 -// ); // Round to nearest second +function determineExactSpan(startTime: number, endTime: number): string { + const durationInSeconds = Math.round( + (new Date(endTime).getTime() - new Date(startTime).getTime()) / 1000 + ); // Round to nearest second -// // Convert the duration into the appropriate PPL-compatible span format -// if (durationInSeconds < 60) { -// return `${durationInSeconds}s`; // Duration in seconds -// } else if (durationInSeconds < 3600) { -// return `${Math.floor(durationInSeconds / 60)}m`; // Duration in minutes -// } else if (durationInSeconds < 86400) { -// return `${Math.floor(durationInSeconds / 3600)}h`; // Duration in hours -// } else if (durationInSeconds < 604800) { -// return `${Math.floor(durationInSeconds / 86400)}d`; // Duration in days -// } else if (durationInSeconds < 2592000) { -// return `${Math.floor(durationInSeconds / 604800)}w`; // Duration in weeks -// } else if (durationInSeconds < 31536000) { -// return `${Math.floor(durationInSeconds / 2592000)}M`; // Duration in months -// } else { -// return `${Math.floor(durationInSeconds / 31536000)}y`; // Duration in years -// } -// } + // Convert the duration into the appropriate PPL-compatible span format + if (durationInSeconds < 60) { + return `${durationInSeconds}s`; // Duration in seconds + } else if (durationInSeconds < 3600) { + return `${Math.floor(durationInSeconds / 60)}m`; // Duration in minutes + } else if (durationInSeconds < 86400) { + return `${Math.floor(durationInSeconds / 3600)}h`; // Duration in hours + } else if (durationInSeconds < 604800) { + return `${Math.floor(durationInSeconds / 86400)}d`; // Duration in days + } else if (durationInSeconds < 2592000) { + return `${Math.floor(durationInSeconds / 604800)}w`; // Duration in weeks + } else if (durationInSeconds < 31536000) { + return `${Math.floor(durationInSeconds / 2592000)}M`; // Duration in months + } else { + return `${Math.floor(durationInSeconds / 31536000)}y`; // Duration in years + } +} function getSpanFromTimeRange(startTime, endTime) { const durationInSeconds = new Date(endTime).getTime() - new Date(startTime).getTime(); @@ -52,7 +53,7 @@ export const Home = () => { const start = convertDateTime(startTime, true); const end = convertDateTime(endTime, false); - // const exactTimeInterval = determineExactSpan(start, end); + const exactTimeInterval = determineExactSpan(start, end); const timeInterval = getSpanFromTimeRange(start, end); return ( @@ -65,9 +66,9 @@ export const Home = () => { { cluster: 'eks-cluster-with-vpc', name: 'clusterCPUUsage', - title: 'Cluster CPU Usage', + title: 'Cluster Total CPU Usage', // query: `source = prometheus_k8s_cluster.\`cluster:node_cpu:sum_rate5m\` | where @timestamp >= '${start}' and @timestamp <= '${end}'`, - query: `source = prometheus_k8s_cluster.container_cpu_usage_seconds_total | where @timestamp >= '${start}' and @timestamp <= '${end}' | where \`cluster\` = 'eks-cluster-with-vpc' | stats sum(@value) as total_cpu_usage by span(@timestamp,${timeInterval})`, + query: `source = prometheus_k8s_cluster.container_cpu_usage_seconds_total | where @timestamp >= '${start}' and @timestamp <= '${end}' | where \`cluster\` = 'eks-cluster-with-vpc' | stats sum(@value) as total_cpu_usage by container, span(@timestamp,${timeInterval})`, endpoint: 'prometheus', datasource: { name: 'prometheus_k8s', type: 'prometheus' }, vis: { @@ -85,6 +86,168 @@ export const Home = () => { }, xKeys: [`span(@timestamp,${timeInterval})`], yKeys: ['total_cpu_usage'], + gKeys: ['container'], + }, + }, + { + cluster: 'eks-cluster-with-vpc', + name: 'clusterMemoryUsage', + title: 'Cluster Total Memory Usage', + // query: `source = prometheus_k8s_cluster.\`cluster:node_cpu:sum_rate5m\` | where @timestamp >= '${start}' and @timestamp <= '${end}'`, + query: `source = prometheus_k8s_cluster.node_memory_MemTotal_bytes | where cluster = 'eks-cluster-with-vpc' | stats sum(@value) as total_memory_usage by nodename, span(@timestamp, ${timeInterval})`, + endpoint: 'prometheus', + datasource: { name: 'prometheus_k8s', type: 'prometheus' }, + vis: { + component: Line, + x: 'timestamp', + y: 'total_memory_usage', + xaxisKey: '@timestamp', + yaxisKey: 'total_memory_usage', + config: { + type: 'line', + mode: 'lines', + line: { + shape: 'spline', // This makes the line smooth + }, + }, + xKeys: [`span(@timestamp,${timeInterval})`], + yKeys: ['total_memory_usage'], + gKeys: ['nodename'], + }, + }, + { + cluster: 'eks-cluster-with-vpc', + name: 'nodesCount', + title: 'Nodes', + query: `source = prometheus_k8s_cluster.\`count:up1\` | where @timestamp >= '${start}' and @timestamp <= '${end}' and cluster = 'eks-cluster-with-vpc' and @value=1 | stats count() as running_node_count by span(@timestamp, ${exactTimeInterval})`, + //query: `source = prometheus_k8s_cluster.\`count:up1\` | where \`cluster\` = 'eks-cluster-with-vpc' | dedup k8s_node_name | stats count() as running_node_count by k8s_node_name`, + endpoint: 'prometheus', + datasource: { name: 'prometheus_k8s', type: 'prometheus' }, + vis: { + component: Pie, + x: 'timestamp', + y: 'Number of Nodes', + xaxisKey: '@timestamp', + yaxisKey: 'total_memory_usage', + config: { + type: 'indicator', + mode: "number" + }, + layout: { + width: 250, + height: 190 + }, + xKeys: [`k8s_node_name`], + yKeys: ['running_node_count'], + gKeys: [], + }, + }, + { + cluster: 'eks-cluster-with-vpc', + name: 'namespacesCount', + title: 'Namespaces', + query: `source = prometheus_k8s_cluster.\`count:up1\` | where @timestamp >= '${start}' and @timestamp <= '${end}' and cluster = 'eks-cluster-with-vpc' and @value=1 | stats count() as running_node_count by span(@timestamp, ${exactTimeInterval})`, + //query: `source = prometheus_k8s_cluster.\`count:up1\` | where \`cluster\` = 'eks-cluster-with-vpc' | dedup k8s_node_name | stats count() as running_node_count by k8s_node_name`, + endpoint: 'prometheus', + datasource: { name: 'prometheus_k8s', type: 'prometheus' }, + vis: { + component: Pie, + x: 'timestamp', + y: 'Number of Nodes', + xaxisKey: '@timestamp', + yaxisKey: 'total_memory_usage', + config: { + type: 'indicator', + mode: "number" + }, + layout: { + width: 250, + height: 190 + }, + xKeys: [`k8s_node_name`], + yKeys: ['running_node_count'], + gKeys: [], + }, + }, + { + cluster: 'eks-cluster-with-vpc', + name: 'runningPodsCount', + title: 'Running Pods', + query: `source = prometheus_k8s_cluster.\`count:up1\` | where @timestamp >= '${start}' and @timestamp <= '${end}' and cluster = 'eks-cluster-with-vpc' and @value=1 | stats count() as running_node_count by span(@timestamp, ${exactTimeInterval})`, + //query: `source = prometheus_k8s_cluster.\`count:up1\` | where \`cluster\` = 'eks-cluster-with-vpc' | dedup k8s_node_name | stats count() as running_node_count by k8s_node_name`, + endpoint: 'prometheus', + datasource: { name: 'prometheus_k8s', type: 'prometheus' }, + vis: { + component: Pie, + x: 'timestamp', + y: 'Number of Nodes', + xaxisKey: '@timestamp', + yaxisKey: 'total_memory_usage', + config: { + type: 'indicator', + mode: "number" + }, + layout: { + width: 250, + height: 190 + }, + xKeys: [`k8s_node_name`], + yKeys: ['running_node_count'], + gKeys: [], + }, + }, + { + cluster: 'eks-cluster-with-vpc', + name: 'runningPodsCategory', + title: 'Failed Pods', + query: `source = prometheus_k8s_cluster.kube_pod_status_phase | where @timestamp >= '${start}' and @timestamp <= '${end}' and cluster = 'eks-cluster-with-vpc' | fields service_name | stats count() as node_count by service_name`, + //query: `source = prometheus_k8s_cluster.\`count:up1\` | where \`cluster\` = 'eks-cluster-with-vpc' | dedup k8s_node_name | stats count() as running_node_count by k8s_node_name`, + endpoint: 'prometheus', + datasource: { name: 'prometheus_k8s', type: 'prometheus' }, + vis: { + component: Pie, + x: 'timestamp', + y: 'Number of Nodes', + xaxisKey: '@timestamp', + yaxisKey: 'total_memory_usage', + config: { + type: 'pie', + hole: .4, + textposition: 'inside', + showlegend: false + }, + // layout: { + // width: 267, + // height: 190 + // }, + xKeys: [`pod`], + yKeys: ['running_node_count'], + gKeys: [], + }, + }, + { + cluster: 'eks-cluster-with-vpc', + name: 'cpuCoreCount', + title: 'Cluster CPU Cores', + query: `source = prometheus_k8s_cluster.machine_cpu_cores | where @timestamp >= '${start}' and @timestamp <= '${end}' and cluster = 'eks-cluster-with-vpc' | fields @value | stats count() as core_count`, + endpoint: 'prometheus', + datasource: { name: 'prometheus_k8s', type: 'prometheus' }, + vis: { + component: Pie, + x: 'timestamp', + y: 'Number of cores', + xaxisKey: '@timestamp', + yaxisKey: 'total_memory_usage', + config: { + type: 'indicator', + mode: "number" + }, + layout: { + width: 250, + height: 190 + }, + xKeys: [`k8s_node_name`], + yKeys: ['core_count'], gKeys: [], }, }, diff --git a/public/components/visualizations/visualization.tsx b/public/components/visualizations/visualization.tsx index dc963ec9c8..c0ed1d0062 100644 --- a/public/components/visualizations/visualization.tsx +++ b/public/components/visualizations/visualization.tsx @@ -55,6 +55,7 @@ export const Visualization = ({ }; const [isValid, erroInfo] = isVisDataValid(visualizations); + console.log('isValid', isValid); return ( <> {isValid ? ( diff --git a/public/components/visualizations/visualization_chart.tsx b/public/components/visualizations/visualization_chart.tsx index 3700f758db..232ca48673 100644 --- a/public/components/visualizations/visualization_chart.tsx +++ b/public/components/visualizations/visualization_chart.tsx @@ -9,6 +9,7 @@ export const VisualizationChart = ({ visualizations }) => { const { vis } = visualizations; const { layout = {}, config = {} } = visualizations?.data?.userConfigs; const Visualization = visualizations?.vis?.component; + console.log('Visualization: ', Visualization); const finalFigureConfig = useMemo(() => { return { ...vis.visconfig?.config, diff --git a/public/index.scss b/public/index.scss index 3188643388..9b0f5a7449 100644 --- a/public/index.scss +++ b/public/index.scss @@ -11,6 +11,7 @@ // event analytics @import 'components/event_analytics/index'; +@import 'components/kubernetes/index'; .synopsisIcon { height: 40px; diff --git a/public/plugin.tsx b/public/plugin.tsx index 196f8d61d1..5fbe11b53d 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -12,6 +12,7 @@ import { CoreSetup, CoreStart, DEFAULT_APP_CATEGORIES, + DEFAULT_NAV_GROUPS, Plugin, PluginInitializerContext, SavedObject, @@ -119,6 +120,7 @@ import { ObservabilityStart, SetupDependencies, } from './types'; +import cluster from 'cluster'; interface PublicConfig { query_assist: { @@ -252,6 +254,34 @@ export class ObservabilityPlugin }), order: observabilityPluginOrder, }, + kubernetes: { + id: 'observability-kubernetes', + label: i18n.translate('core.ui.observabilityNavList.label', { + defaultMessage: 'Kubenetes', + }), + order: 5095, + }, + cluster: { + id: 'observability-kubernetes-cluster', + label: i18n.translate('core.ui.observabilityNavList.label', { + defaultMessage: 'Cluster', + }), + order: 5096, + }, + namespaces: { + id: 'observability-kubernetes-namespaces', + label: i18n.translate('core.ui.observabilityNavList.label', { + defaultMessage: 'Namespaces', + }), + order: 5097, + }, + pod: { + id: 'observability-kubernetes-pod', + label: i18n.translate('core.ui.observabilityNavList.label', { + defaultMessage: 'Pod', + }), + order: 5098, + } }); // Adding a variation entails associating a key-value pair, where a change in the key results in @@ -331,6 +361,39 @@ export class ObservabilityPlugin ); }; + // core.application.register({ + // id: 'observability-kubernetes', + // title: 'Kubernetes', + // description: 'OpenSearch Dashboards Kubernetes Plugin', + // category: { + // id: 'opensearch', + // label: 'OpenSearch Plugins', + // order: 2000, + // }, + // order: 4000, + // mount: async (params) => { + // const { renderApp } = await import('./components/kubernetes/home'); + // const [coreStart] = await core.getStartServices(); + // return renderApp(coreStart, params); + // }, + // }); + + core.chrome.navGroup.addNavLinksToGroup(OBSERVABILITY_APP_CATEGORIES.kubernetes, [ + { + id: 'observability-kubernetes', + category: DEFAULT_APP_CATEGORIES.observability, + parentNavLinkId: DEFAULT_APP_CATEGORIES.observability.id, + }, + ]); + + // core.chrome.navGroup.addNavLinksToGroup(OBSERVABILITY_APP_CATEGORIES.cluster, [ + // { + // id: 'observability-kubernetes-cluster', + // category: DEFAULT_APP_CATEGORIES.observability, + // parentNavLinkId: DEFAULT_APP_CATEGORIES.observability.id, + // }, + // ]); + core.application.register({ id: observabilityMetricsID, title: observabilityMetricsTitle, @@ -356,7 +419,7 @@ export class ObservabilityPlugin order: observabilityIntegrationsPluginOrder, mount: appMountWithStartPage('integrations'), }); - + console.log('core.chrome.navGroup.getNavGroupEnabled(): ', core.chrome.navGroup.getNavGroupEnabled()); if (core.chrome.navGroup.getNavGroupEnabled()) { core.application.register({ id: observabilityOverviewID, @@ -389,6 +452,20 @@ export class ObservabilityPlugin category: DEFAULT_APP_CATEGORIES.investigate, mount: appMountWithStartPage('traces', '/services'), }); + + // core.application.register({ + // id: 'observability-kubernetes', + // title: 'Kubernetes', + // category: DEFAULT_APP_CATEGORIES.observability, + // // category: { + // // id: 'observability', + // // label: 'Observability', + // // order: 3000, + // // }, + // order: 5095, + // mount: appMountWithStartPage('kubernetes'), + // }); + } else { core.application.register({ id: observabilityTracesID, @@ -427,11 +504,67 @@ export class ObservabilityPlugin core.application.register({ id: 'observability-kubernetes', title: 'Kubernetes', - category: OBSERVABILITY_APP_CATEGORIES.observability, + category: DEFAULT_APP_CATEGORIES.observability, order: 5095, mount: appMountWithStartPage('kubernetes'), }); + core.application.register({ + id: 'kubernetes-overview', + title: 'Overview', + category: OBSERVABILITY_APP_CATEGORIES.kubernetes, + order: 5096, + mount: appMountWithStartPage('kubernetes-overview'), + }); + + core.application.register({ + id: 'kubernetes-cluster', + title: 'Cluster', + category: OBSERVABILITY_APP_CATEGORIES.kubernetes, + order: 5096, + mount: appMountWithStartPage('kubernetes-cluster'), + }); + + core.application.register({ + id: 'kubernetes-namespaces', + title: 'Namespace', + category: OBSERVABILITY_APP_CATEGORIES.kubernetes, + order: 5096, + mount: appMountWithStartPage('kubernetes-namespaces'), + }); + + core.application.register({ + id: 'kubernetes-pod', + title: 'Pod', + category: OBSERVABILITY_APP_CATEGORIES.kubernetes, + order: 5096, + mount: appMountWithStartPage('kubernetes-pod'), + }); + + const navLinks = [ + { + id: 'kubernetes-overview', + parentNavLinkId: OBSERVABILITY_APP_CATEGORIES.kubernetes.id, + }, + { + id: 'kubernetes-cluster', + parentNavLinkId: OBSERVABILITY_APP_CATEGORIES.kubernetes.id, + }, + { + id: 'kubernetes-namespaces', + parentNavLinkId: OBSERVABILITY_APP_CATEGORIES.kubernetes.id, + }, + { + id: 'kubernetes-pod', + parentNavLinkId: OBSERVABILITY_APP_CATEGORIES.kubernetes.id, + }, + ]; + + core.chrome.navGroup.addNavLinksToGroup( + OBSERVABILITY_APP_CATEGORIES.kubernetes, + navLinks + ); + registerAllPluginNavGroups(core); const embeddableFactory = new ObservabilityEmbeddableFactoryDefinition(async () => ({