diff --git a/web/locales/en/plugin__netobserv-plugin.json b/web/locales/en/plugin__netobserv-plugin.json index ed63c2bd8..020765727 100644 --- a/web/locales/en/plugin__netobserv-plugin.json +++ b/web/locales/en/plugin__netobserv-plugin.json @@ -6,6 +6,7 @@ "n/a": "n/a", "View alert details": "View alert details", "View health dashboard": "View health dashboard", + "View FlowCollector status": "View FlowCollector status", "Name": "Name", "Subnet label": "Subnet label", "IP": "IP", @@ -202,9 +203,13 @@ "Cancel": "Cancel", "Delete": "Delete", "Network Observability FlowCollector status": "Network Observability FlowCollector status", + "FlowCollector status": "FlowCollector status", + "Configuration error": "Configuration error", + "Configuration warnings": "Configuration warnings", "Network Observability is on hold": "Network Observability is on hold", "Execution mode is set to OnHold. All operator-managed workloads have been deleted, while preserving other resources. To change execution mode, update or remove \"spec.execution.mode\" in the FlowCollector resource.": "Execution mode is set to OnHold. All operator-managed workloads have been deleted, while preserving other resources. To change execution mode, update or remove \"spec.execution.mode\" in the FlowCollector resource.", "Edit FlowCollector": "Edit FlowCollector", + "FlowCollector must be ready to open Network Traffic": "FlowCollector must be ready to open Network Traffic", "Open Network Traffic page": "Open Network Traffic page", "An error occured while retreiving FlowCollector: {{error}}": "An error occured while retreiving FlowCollector: {{error}}", "Create FlowCollector": "Create FlowCollector", @@ -226,6 +231,10 @@ "Metric": "Metric", "Data": "Data", "Review": "Review", + "eBPF agents": "eBPF agents", + "Flowlogs pipeline": "Flowlogs pipeline", + "Monitoring": "Monitoring", + "Console plugin": "Console plugin", "It will disable flow collection globally. All resources managed by the FlowCollector will be deleted, such as pods and services.": "It will disable flow collection globally. All resources managed by the FlowCollector will be deleted, such as pods and services.", "The following metric will stop being collected: ": "The following metric will stop being collected: ", "Delete {{kind}} {{name}}?": "Delete {{kind}} {{name}}?", @@ -234,7 +243,16 @@ "Create {{kind}}": "Create {{kind}}", "Update by completing the form. Current values are from the existing resource.": "Update by completing the form. Current values are from the existing resource.", "Create by completing the form. Default values are provided as example.": "Create by completing the form. Default values are provided as example.", + "Component statuses": "Component statuses", + "Component": "Component", + "State": "State", + "Replicas": "Replicas", + "Unused: {{list}}": "Unused: {{list}}", "{{kind}} resource doesn't exists yet.": "{{kind}} resource doesn't exists yet.", + "eBPF Agent": "eBPF Agent", + "Flowlogs Pipeline": "Flowlogs Pipeline", + "Console Plugin": "Console Plugin", + "Conditions": "Conditions", "Type": "Type", "Status": "Status", "Reason": "Reason", @@ -343,7 +361,6 @@ "Mode": "Mode", "alert": "alert", "recording": "recording", - "State": "State", "Severity": "Severity", "Active since": "Active since", "Labels": "Labels", @@ -500,6 +517,12 @@ "Results": "Results", "Query summary": "Query summary", "Find in view": "Find in view", + "FlowCollector is ready": "FlowCollector is ready", + "FlowCollector is degraded": "FlowCollector is degraded", + "FlowCollector is pending": "FlowCollector is pending", + "FlowCollector has errors": "FlowCollector has errors", + "FlowCollector is on hold": "FlowCollector is on hold", + "Loading FlowCollector status...": "Loading FlowCollector status...", "Show all graphs": "Show all graphs", "Focus on this graph": "Focus on this graph", "Show total": "Show total", diff --git a/web/moduleMapper/dummy.tsx b/web/moduleMapper/dummy.tsx index eac27bca3..3868e1cfd 100644 --- a/web/moduleMapper/dummy.tsx +++ b/web/moduleMapper/dummy.tsx @@ -196,76 +196,97 @@ export function useK8sWatchResource(req: any) { { "lastTransitionTime": "2025-04-08T09:01:44Z", "message": "4 ready components, 0 with failure, 1 pending", - "reason": "Pending", - "status": "False", + "reason": "Ready,Degraded", + "status": "True", "type": "Ready" }, { - "lastTransitionTime": "2025-04-08T09:01:44Z", - "message": "Deployment netobserv-plugin not ready: 1/1 (Deployment does not have minimum availability.)", - "reason": "DeploymentNotReady", - "status": "True", - "type": "WaitingFlowCollectorLegacy" + "lastTransitionTime": "2025-04-08T09:01:43Z", + "message": "", + "reason": "Valid", + "status": "False", + "type": "ConfigurationIssue" }, { "lastTransitionTime": "2025-04-08T09:01:44Z", "message": "", "reason": "Ready", - "status": "False", - "type": "WaitingMonitoring" + "status": "True", + "type": "AgentReady" }, { - "lastTransitionTime": "2025-04-08T09:01:43Z", + "lastTransitionTime": "2025-04-08T09:01:45Z", "message": "", "reason": "Ready", - "status": "False", - "type": "WaitingNetworkPolicy" + "status": "True", + "type": "ProcessorReady" }, { - "lastTransitionTime": "2025-04-08T09:01:43Z", - "message": "", - "reason": "Valid", + "lastTransitionTime": "2025-04-08T09:01:44Z", + // eslint-disable-next-line max-len + "message": "Deployment netobserv-plugin not ready: 1/1 (Deployment does not have minimum availability.)", + "reason": "DeploymentNotReady", "status": "False", - "type": "ConfigurationIssue" + "type": "PluginReady" }, { - "lastTransitionTime": "2026-01-15T15:44:51Z", - // eslint-disable-next-line max-len - "message": "LokiStack has issues [name: loki, namespace: netobserv]: Warning: The schema configuration does not contain the most recent schema version and needs an update; Degraded: Missing object storage secret", - "reason": "LokiStackIssues", - "status": "False", - "type": "LokiIssue" + "lastTransitionTime": "2025-04-08T09:01:44Z", + "message": "", + "reason": "Ready", + "status": "True", + "type": "MonitoringReady" }, { "lastTransitionTime": "2026-01-15T16:05:26Z", // eslint-disable-next-line max-len - "message": "LokiStack has warnings [name: loki, namespace: netobserv]: Warning: The schema configuration does not contain the most recent schema version and needs an update", + "message": "LokiStack has warnings [name: loki, namespace: netobserv]: Warning: schema needs update", "reason": "LokiStackWarnings", "status": "True", "type": "LokiWarning" + } + ], + "components": { + "agent": { + "state": "Ready", + "desiredReplicas": 3, + "readyReplicas": 3 }, - { - "lastTransitionTime": "2025-04-08T09:01:45Z", - "message": "", - "reason": "Ready", - "status": "False", - "type": "WaitingFLPParent" + "processor": { + "state": "Ready", + "desiredReplicas": 1, + "readyReplicas": 1 }, - { - "lastTransitionTime": "2025-04-08T09:01:45Z", - "message": "", - "reason": "Ready", - "status": "False", - "type": "WaitingFLPMonolith" - }, - { - "lastTransitionTime": "2025-04-08T09:01:44Z", - "message": "Transformer only used with Kafka", - "reason": "ComponentUnused", - "status": "Unknown", - "type": "WaitingFLPTransformer" + "plugin": { + "state": "InProgress", + "reason": "DeploymentNotReady", + // eslint-disable-next-line max-len + "message": "Deployment netobserv-plugin not ready: 1/1", + "desiredReplicas": 1, + "readyReplicas": 0 } - ] + }, + "integrations": { + "monitoring": { + "state": "Ready" + }, + "loki": { + "state": "Unused" + }, + "exporters": [ + { + "name": "kafka-exporter", + "type": "Kafka", + "state": "Ready", + "reason": "Configured" + }, + { + "name": "otel-exporter", + "type": "OpenTelemetry", + "state": "Ready", + "reason": "Configured" + } + ] + } } setResource(fc); break; @@ -289,15 +310,19 @@ export function useK8sWatchResource(req: any) { // simulate an update setTimeout(() => { const fc = _.cloneDeep(resource); - // 70% chance to update, 30% chance to skip - if (Math.random() < 0.7 && fc.status && fc.status.conditions) { - const randomIndex = Math.floor(Math.random() * fc.status.conditions.length); - const condition = fc.status.conditions[randomIndex]; - condition.status = condition.status === 'True' ? 'False' : 'True'; - - // Enable/disable Loki based on LokiIssue status - if (condition.type === 'LokiIssue' || condition.type === 'LokiWarning') { - fc.spec!.loki.enable = condition.status === 'True'; + if (Math.random() < 0.7 && fc.status) { + const states = ['Ready', 'InProgress', 'Degraded', 'Failure']; + const r = Math.random(); + if (r < 0.5 && fc.status.components) { + const comps = ['agent', 'processor', 'plugin']; + const comp = comps[Math.floor(Math.random() * comps.length)]; + if (fc.status.components[comp]) { + fc.status.components[comp].state = states[Math.floor(Math.random() * states.length)]; + } + } else if (fc.status.integrations) { + if (fc.status.integrations.monitoring) { + fc.status.integrations.monitoring.state = states[Math.floor(Math.random() * states.length)]; + } } } setResource(fc); diff --git a/web/setup-tests.tsx b/web/setup-tests.tsx index d050766a4..24d575aee 100644 --- a/web/setup-tests.tsx +++ b/web/setup-tests.tsx @@ -28,6 +28,7 @@ jest.mock('@openshift-console/dynamic-plugin-sdk', () => { return null; }, useResolvedExtensions: jest.fn(), + useK8sWatchResource: jest.fn(() => [null, false, null]), useK8sModels: () => { return [{}, false]; }, diff --git a/web/src/components/alerts/banner.tsx b/web/src/components/alerts/banner.tsx index 0d3882f1e..ffc92a179 100644 --- a/web/src/components/alerts/banner.tsx +++ b/web/src/components/alerts/banner.tsx @@ -9,7 +9,7 @@ import { } from '@patternfly/react-core'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from '../../utils/url'; +import { flowCollectorStatusPath, useNavigate } from '../../utils/url'; import './banner.css'; export interface AlertBannerProps { @@ -41,6 +41,9 @@ export const AlertBanner: React.FC = ({ rule, onDelete }) => { {t('View alert details')} {t('View health dashboard')} + navigate(flowCollectorStatusPath)}> + {t('View FlowCollector status')} + } > diff --git a/web/src/components/forms/flowCollector-status.tsx b/web/src/components/forms/flowCollector-status.tsx index 15e8397b2..77b4048a6 100644 --- a/web/src/components/forms/flowCollector-status.tsx +++ b/web/src/components/forms/flowCollector-status.tsx @@ -1,12 +1,24 @@ import React, { FC } from 'react'; -import { Alert, AlertVariant, Button, Flex, FlexItem, PageSection, Text, Title } from '@patternfly/react-core'; +import { + Alert, + AlertVariant, + Button, + Flex, + FlexItem, + PageSection, + TextContent, + Title, + Tooltip +} from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { flowCollectorEditPath, flowCollectorNewPath, netflowTrafficPath, useNavigate } from '../../utils/url'; +import { FlowCollectorStatusIcon } from '../status/flowcollector-status-icon'; import './forms.css'; import { Pipeline } from './pipeline'; import { ResourceStatus } from './resource-status'; import { Consumer, ResourceWatcher } from './resource-watcher'; +import { getFlowCollectorOverallStatus } from './utils'; export type FlowCollectorStatusProps = {}; @@ -26,20 +38,48 @@ export const FlowCollectorStatus: FC = () => { > {ctx => { - // Check if operator is in hold mode - const isOnHold = ctx.data?.spec?.execution?.mode === 'OnHold'; + const status = getFlowCollectorOverallStatus(ctx.data, ctx.loadError); + const showTrafficButton = status === 'ready' || status === 'degraded'; + const configIssue = ( + (ctx.data?.status?.conditions as Array<{ + type: string; + status: string; + reason?: string; + message?: string; + }>) || [] + ).find(c => c.type === 'ConfigurationIssue' && c.status === 'True'); return ( {ctx.data && ( + {configIssue && ( + + + {configIssue.message} + + + )} - {isOnHold ? ( + {status === 'onHold' ? ( {t( // eslint-disable-next-line max-len @@ -72,18 +112,22 @@ export const FlowCollectorStatus: FC = () => { {t('Edit FlowCollector')} - {!isOnHold && ( - + + - - )} + + @@ -91,9 +135,9 @@ export const FlowCollectorStatus: FC = () => { {ctx.loadError && ( - + {t('An error occured while retreiving FlowCollector: {{error}}', { error: ctx.loadError })} - + + ); +}; + +export default FlowCollectorStatusIndicator;