@oneuptime/common 13.0.8 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/AuditLog.ts +23 -15
- package/Models/DatabaseModels/GlobalConfig.ts +24 -0
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/Monitor.ts +3 -2
- package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- package/Scripts/generate-service-worker.js +65 -4
- package/Server/API/BaseAPI.ts +26 -5
- package/Server/API/GlobalConfigAPI.ts +228 -462
- package/Server/API/ProjectOIDC.ts +25 -16
- package/Server/API/ProjectSSO.ts +24 -16
- package/Server/API/StatusPageAPI.ts +100 -42
- package/Server/API/TeamMemberAPI.ts +10 -1
- package/Server/Enterprise/EnterpriseEdition.ts +649 -0
- package/Server/Enterprise/EnterpriseFeature.ts +48 -0
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
- package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
- package/Server/EnvironmentConfig.ts +147 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/UserAuthorization.ts +38 -16
- package/Server/Services/AuditLogService.ts +84 -1079
- package/Server/Services/DatabaseService.ts +10 -0
- package/Server/Services/EnterpriseLicenseService.ts +38 -0
- package/Server/Services/GlobalConfigService.ts +80 -1
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorProbeService.ts +116 -20
- package/Server/Services/MonitorService.ts +59 -6
- package/Server/Services/MonitorStatusTimelineService.ts +284 -0
- package/Server/Services/MonitorTemplateService.ts +26 -0
- package/Server/Services/ProjectService.ts +284 -0
- package/Server/Services/RumSessionPinService.ts +25 -6
- package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
- package/Server/Services/StatusPagePrivateUserService.ts +7 -3
- package/Server/Services/StatusPageService.ts +78 -1
- package/Server/Services/TeamMemberService.ts +11 -0
- package/Server/Services/TeamService.ts +10 -0
- package/Server/Services/UserService.ts +10 -17
- package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
- package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
- package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
- package/Server/Types/Markdown.ts +41 -0
- package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
- package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
- package/Server/Utils/EditionEnforcement.ts +233 -0
- package/Server/Utils/FrontendEnvironment.ts +41 -2
- package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
- package/Server/Utils/Monitor/AffectedResourceList.ts +148 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +720 -465
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- package/Server/Utils/Monitor/RootCauseList.ts +139 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
- package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
- package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
- package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
- package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
- package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
- package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
- package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
- package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
- package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
- package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
- package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
- package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
- package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
- package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
- package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
- package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
- package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
- package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
- package/Tests/Server/Services/AuditLogService.test.ts +460 -990
- package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
- package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
- package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
- package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
- package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
- package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- package/Tests/Server/Types/Markdown.test.ts +183 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
- package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
- package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
- package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +1339 -96
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
- package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
- package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
- package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
- package/Tests/Types/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
- package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
- package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
- package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
- package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
- package/Tests/UI/Components/Icon.test.tsx +26 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
- package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
- package/Tests/Utils/HostAddressUtil.test.ts +138 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
- package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
- package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
- package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- package/Types/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
- package/Types/Database/CanonicalModelInstance.ts +102 -0
- package/Types/Database/TableColumn.ts +20 -6
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
- package/Types/IP/IPv6.ts +6 -1
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Monitor/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
- package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
- package/UI/Components/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/ModelList/ModelList.tsx +11 -1
- package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
- package/UI/esbuild-enterprise.js +263 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
- package/Utils/HostAddressUtil.ts +94 -0
- package/Utils/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
- package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
- package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
- package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- package/build/dist/Server/API/BaseAPI.js +26 -5
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectOIDC.js +24 -16
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +24 -16
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +93 -43
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +8 -1
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +84 -2
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AuditLogService.js +71 -740
- package/build/dist/Server/Services/AuditLogService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +63 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +90 -17
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +48 -6
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +214 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +24 -6
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +71 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +10 -0
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamService.js +9 -0
- package/build/dist/Server/Services/TeamService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -16
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +31 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
- package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +67 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +585 -367
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +59 -11
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
- package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
- package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js +19 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
- package/build/dist/Types/IP/IPv6.js +6 -1
- package/build/dist/Types/IP/IPv6.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +5 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
- package/build/dist/Types/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
- package/build/dist/Utils/HostAddressUtil.js +80 -0
- package/build/dist/Utils/HostAddressUtil.js.map +1 -0
- package/build/dist/Utils/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
- package/jest.config.json +3 -1
- package/package.json +1 -1
- package/test-setup.sh +103 -34
- package/tsconfig.json +13 -0
- package/Server/API/EnterpriseLicenseAPI.ts +0 -925
- package/Server/API/TeamComplianceAPI.ts +0 -158
- package/Server/Middleware/SCIMAuthorization.ts +0 -141
- package/Server/Services/TeamComplianceService.ts +0 -733
- package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
- package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
- package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
- package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
- package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
- package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
- package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
- package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
- package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
- package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
- package/build/dist/Server/Services/TeamComplianceService.js +0 -537
- package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
|
@@ -26,6 +26,8 @@ import ExternalStatusPageMonitorCriteria from "./Criteria/ExternalStatusPageMoni
|
|
|
26
26
|
import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
|
|
27
27
|
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
|
|
28
28
|
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
|
|
29
|
+
import AffectedResourceList from "./AffectedResourceList";
|
|
30
|
+
import RootCauseList from "./RootCauseList";
|
|
29
31
|
import MonitorCriteriaInstance from "../../../Types/Monitor/MonitorCriteriaInstance";
|
|
30
32
|
import MonitorStep from "../../../Types/Monitor/MonitorStep";
|
|
31
33
|
import FilterCondition from "../../../Types/Filter/FilterCondition";
|
|
@@ -1113,9 +1115,7 @@ ${contextBlock}
|
|
|
1113
1115
|
samples: breachingSamples,
|
|
1114
1116
|
totalSamples: ctx.totalSamplesInWindow,
|
|
1115
1117
|
unit: ctx.unit,
|
|
1116
|
-
metricName: ctx.metricName,
|
|
1117
1118
|
unitHeuristicMetricName: unitHeuristicMetricName,
|
|
1118
|
-
alias: ctx.alias,
|
|
1119
1119
|
components: ctx.components || [],
|
|
1120
1120
|
})}`);
|
|
1121
1121
|
}
|
|
@@ -1151,24 +1151,41 @@ ${contextBlock}
|
|
|
1151
1151
|
return sections.join("\n");
|
|
1152
1152
|
}
|
|
1153
1153
|
/**
|
|
1154
|
-
* Build the **Breaching Samples** markdown section — a
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1154
|
+
* Build the **Breaching Samples** markdown section — a numbered list of
|
|
1155
|
+
* the samples that breached, oldest first. Caps the list so a 30-minute
|
|
1156
|
+
* window at 1-second granularity doesn't dump thousands of lines onto
|
|
1157
|
+
* the root-cause page; the caller can always drill in via the metric
|
|
1158
|
+
* explorer link.
|
|
1159
|
+
*
|
|
1160
|
+
* 1. `2026-08-14T10:30:00.000Z` — **1.07 GB**
|
|
1161
|
+
* - `a`: 537 MB
|
|
1162
|
+
* - `b`: 1.5 sec
|
|
1163
|
+
* - `k8s.pod.name`: `web-1`
|
|
1164
|
+
*
|
|
1165
|
+
* It used to be a table — Timestamp | Metric | Alias | Value, a column
|
|
1166
|
+
* per formula component and one per attribute key — which grew wider
|
|
1167
|
+
* with every attribute and read badly in the email card, in Slack and on
|
|
1168
|
+
* the dashboard (see RootCauseList). Its Metric and Alias columns
|
|
1169
|
+
* repeated the same two values on every row, the ones the Metric
|
|
1170
|
+
* Details list directly above already states, so the list leaves them
|
|
1171
|
+
* out; and a component or attribute a sample has no value for is left
|
|
1172
|
+
* out rather than printed as "-".
|
|
1159
1173
|
*
|
|
1160
1174
|
* Timestamps are emitted as inline code wrapping ISO 8601 strings so
|
|
1161
1175
|
* the client-side markdown viewer can localize them to the viewer's
|
|
1162
1176
|
* timezone (without losing the canonical instant).
|
|
1163
1177
|
*/
|
|
1164
1178
|
static formatBreachingSamplesSection(input) {
|
|
1165
|
-
const
|
|
1179
|
+
const MAX_SAMPLES_SHOWN = 20;
|
|
1166
1180
|
// Sort chronologically and de-duplicate any accidental repeats
|
|
1167
1181
|
const sorted = [...input.samples].sort((a, b) => {
|
|
1168
1182
|
return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
1169
1183
|
});
|
|
1170
|
-
const displayedSamples = sorted.slice(0,
|
|
1171
|
-
|
|
1184
|
+
const displayedSamples = sorted.slice(0, MAX_SAMPLES_SHOWN);
|
|
1185
|
+
/*
|
|
1186
|
+
* Collect attribute keys that appear on any displayed sample, so every
|
|
1187
|
+
* item lists the attributes it has in the same order.
|
|
1188
|
+
*/
|
|
1172
1189
|
const attrKeySet = new Set();
|
|
1173
1190
|
for (const s of displayedSamples) {
|
|
1174
1191
|
for (const k of Object.keys(s.attributes || {})) {
|
|
@@ -1176,94 +1193,64 @@ ${contextBlock}
|
|
|
1176
1193
|
}
|
|
1177
1194
|
}
|
|
1178
1195
|
const attrKeys = Array.from(attrKeySet);
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
};
|
|
1187
|
-
/*
|
|
1188
|
-
* Column layout:
|
|
1189
|
-
* Timestamp | Metric | Alias | Value | <component_1> | ... | <attr_1> | ...
|
|
1190
|
-
*
|
|
1191
|
-
* The component columns let the reader see what each variable of a
|
|
1192
|
-
* formula resolved to at the breach time — e.g. "when c = a + b
|
|
1193
|
-
* breached 100, a was 55, b was 46". They are omitted for plain
|
|
1194
|
-
* metric criteria.
|
|
1195
|
-
*/
|
|
1196
|
-
const headerCells = [
|
|
1197
|
-
"Timestamp",
|
|
1198
|
-
"Metric",
|
|
1199
|
-
"Alias",
|
|
1200
|
-
"Value",
|
|
1201
|
-
];
|
|
1202
|
-
/*
|
|
1203
|
-
* The component columns carry no unit in their header any more. Each
|
|
1204
|
-
* cell now names the unit it actually landed on, and auto-scaling
|
|
1205
|
-
* means neighbouring rows can land on different ones — a header that
|
|
1206
|
-
* read "a (By)" over cells reading "900 KB" and "1.2 MB" states a
|
|
1207
|
-
* unit that neither row uses. The configured unit is not lost: the
|
|
1208
|
-
* Components bullet list above the table still records it.
|
|
1209
|
-
*/
|
|
1210
|
-
for (const component of input.components) {
|
|
1211
|
-
headerCells.push(component.alias);
|
|
1212
|
-
}
|
|
1213
|
-
headerCells.push(...attrKeys);
|
|
1214
|
-
const headerRow = `| ${headerCells.join(" | ")} |`;
|
|
1215
|
-
const dividerRow = `| ${headerCells
|
|
1216
|
-
.map(() => {
|
|
1217
|
-
return "---";
|
|
1218
|
-
})
|
|
1219
|
-
.join(" | ")} |`;
|
|
1220
|
-
const metricCell = `\`${escapeCell(input.metricName)}\``;
|
|
1221
|
-
const aliasCell = input.alias
|
|
1222
|
-
? `\`${escapeCell(input.alias)}\``
|
|
1223
|
-
: "-";
|
|
1224
|
-
const dataRows = displayedSamples.map((s) => {
|
|
1225
|
-
const timestampIso = new Date(s.timestamp).toISOString();
|
|
1226
|
-
const cells = [
|
|
1227
|
-
`\`${timestampIso}\``,
|
|
1228
|
-
metricCell,
|
|
1229
|
-
aliasCell,
|
|
1230
|
-
MetricValueFormatter.format({
|
|
1231
|
-
value: s.value,
|
|
1232
|
-
unit: input.unit,
|
|
1233
|
-
metricName: input.unitHeuristicMetricName,
|
|
1234
|
-
}),
|
|
1235
|
-
];
|
|
1196
|
+
const items = displayedSamples.map((s) => {
|
|
1197
|
+
const details = [];
|
|
1198
|
+
/*
|
|
1199
|
+
* What each variable of a formula resolved to at the breach time —
|
|
1200
|
+
* e.g. "when c = a + b breached 100, a was 55, b was 46". Absent
|
|
1201
|
+
* for plain metric criteria.
|
|
1202
|
+
*/
|
|
1236
1203
|
for (const component of input.components) {
|
|
1237
1204
|
const match = (s.componentValues || []).find((cv) => {
|
|
1238
1205
|
return cv.alias === component.alias;
|
|
1239
1206
|
});
|
|
1240
|
-
if (match
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1207
|
+
if (!match || typeof match.value !== "number") {
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
/*
|
|
1211
|
+
* Each component keeps its OWN unit. Component values are
|
|
1212
|
+
* indexed off the per-query series and never go through the
|
|
1213
|
+
* sample→threshold-unit conversion the sample's value does, so
|
|
1214
|
+
* they are in the component's legendUnit — which is why each
|
|
1215
|
+
* one names the unit it is in. Its configured unit is in the
|
|
1216
|
+
* Components list of the Metric Details above.
|
|
1217
|
+
*/
|
|
1218
|
+
details.push({
|
|
1219
|
+
label: RootCauseList.code(component.alias),
|
|
1220
|
+
value: MetricValueFormatter.format({
|
|
1250
1221
|
value: match.value,
|
|
1251
1222
|
unit: component.unit,
|
|
1252
1223
|
metricName: MonitorCriteriaEvaluator.metricNameForUnitHeuristics({
|
|
1253
1224
|
metricName: component.name,
|
|
1254
1225
|
isFormula: component.isFormula,
|
|
1255
1226
|
}),
|
|
1256
|
-
})
|
|
1257
|
-
}
|
|
1258
|
-
else {
|
|
1259
|
-
cells.push("-");
|
|
1260
|
-
}
|
|
1227
|
+
}),
|
|
1228
|
+
});
|
|
1261
1229
|
}
|
|
1230
|
+
/*
|
|
1231
|
+
* The attributes identify the series the sample came from. Keys
|
|
1232
|
+
* and values are telemetry, so both go in code spans:
|
|
1233
|
+
* RootCauseList.code keeps whatever they contain inside the span.
|
|
1234
|
+
*/
|
|
1262
1235
|
for (const k of attrKeys) {
|
|
1263
1236
|
const v = s.attributes[k];
|
|
1264
|
-
|
|
1237
|
+
if (v === undefined || v === null) {
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
details.push({
|
|
1241
|
+
label: RootCauseList.code(k),
|
|
1242
|
+
value: RootCauseList.code(String(v)),
|
|
1243
|
+
});
|
|
1265
1244
|
}
|
|
1266
|
-
return
|
|
1245
|
+
return {
|
|
1246
|
+
title: RootCauseList.code(new Date(s.timestamp).toISOString()),
|
|
1247
|
+
value: `**${MetricValueFormatter.format({
|
|
1248
|
+
value: s.value,
|
|
1249
|
+
unit: input.unit,
|
|
1250
|
+
metricName: input.unitHeuristicMetricName,
|
|
1251
|
+
})}**`,
|
|
1252
|
+
details: details,
|
|
1253
|
+
};
|
|
1267
1254
|
});
|
|
1268
1255
|
const lines = [
|
|
1269
1256
|
`**Breaching Samples**`,
|
|
@@ -1272,12 +1259,14 @@ ${contextBlock}
|
|
|
1272
1259
|
totalSamples: input.totalSamples,
|
|
1273
1260
|
}),
|
|
1274
1261
|
"",
|
|
1275
|
-
|
|
1276
|
-
dividerRow,
|
|
1277
|
-
...dataRows,
|
|
1262
|
+
RootCauseList.render(items),
|
|
1278
1263
|
];
|
|
1279
1264
|
if (sorted.length > displayedSamples.length) {
|
|
1280
|
-
|
|
1265
|
+
/*
|
|
1266
|
+
* The blank line matters: without it the note is a lazy
|
|
1267
|
+
* continuation of the last item, not a paragraph of its own.
|
|
1268
|
+
*/
|
|
1269
|
+
lines.push("", `_Showing the first ${displayedSamples.length} of ${sorted.length} breaching samples._`);
|
|
1281
1270
|
}
|
|
1282
1271
|
return lines.join("\n");
|
|
1283
1272
|
}
|
|
@@ -1436,7 +1425,7 @@ ${contextBlock}
|
|
|
1436
1425
|
return ` _(scope partially carried — not applied: ${dropped.join(", ")})_`;
|
|
1437
1426
|
}
|
|
1438
1427
|
/**
|
|
1439
|
-
* The unit for a value in a per-platform "Affected Resources"
|
|
1428
|
+
* The unit for a value in a per-platform "Affected Resources" list.
|
|
1440
1429
|
*
|
|
1441
1430
|
* These rows are NOT the same numbers as the criteria's breaching
|
|
1442
1431
|
* samples: the worker collects them with a raw `MetricService.findBy`
|
|
@@ -1475,10 +1464,11 @@ ${contextBlock}
|
|
|
1475
1464
|
}
|
|
1476
1465
|
}
|
|
1477
1466
|
/**
|
|
1478
|
-
* Render
|
|
1467
|
+
* Render the value of one resource in a platform "Affected Resources"
|
|
1468
|
+
* list.
|
|
1479
1469
|
*
|
|
1480
|
-
* Bold, because that is how
|
|
1481
|
-
* it is the one number
|
|
1470
|
+
* Bold, because that is how the breakdown has always drawn the value —
|
|
1471
|
+
* it is the one number on the line a reader scans for.
|
|
1482
1472
|
*/
|
|
1483
1473
|
static formatPlatformResourceValue(input) {
|
|
1484
1474
|
return `**${MetricValueFormatter.format({
|
|
@@ -1490,6 +1480,78 @@ ${contextBlock}
|
|
|
1490
1480
|
metricName: input.metricName,
|
|
1491
1481
|
})}**`;
|
|
1492
1482
|
}
|
|
1483
|
+
/*
|
|
1484
|
+
* One Kubernetes resource as an "Affected Resources" list item.
|
|
1485
|
+
*
|
|
1486
|
+
* The item is titled by the most specific object the series names —
|
|
1487
|
+
* container, then pod, then workload, then node, then namespace — because
|
|
1488
|
+
* that is the thing that is actually breaching; everything above it in
|
|
1489
|
+
* the hierarchy is listed underneath as context. A series with no
|
|
1490
|
+
* Kubernetes identity at all is a cluster-level series, so it is titled
|
|
1491
|
+
* by the cluster.
|
|
1492
|
+
*
|
|
1493
|
+
* A container is titled together with its pod. Every replica of a
|
|
1494
|
+
* workload runs a container of the same name, so "Container `checkout`"
|
|
1495
|
+
* alone would title three crash-looping replicas identically and leave
|
|
1496
|
+
* the pod that tells them apart buried in the bullets.
|
|
1497
|
+
*/
|
|
1498
|
+
static getKubernetesAffectedResourceEntry(input) {
|
|
1499
|
+
const resource = input.resource;
|
|
1500
|
+
// "Deployment", "StatefulSet", ... — the worker only ever sets one of those.
|
|
1501
|
+
const workloadLabel = resource.workloadType || "Workload";
|
|
1502
|
+
let kind = "Cluster";
|
|
1503
|
+
let name = AffectedResourceList.code(input.clusterName);
|
|
1504
|
+
if (resource.containerName) {
|
|
1505
|
+
kind = "Container";
|
|
1506
|
+
name = resource.podName
|
|
1507
|
+
? `${AffectedResourceList.code(resource.containerName)} in pod ${AffectedResourceList.code(resource.podName)}`
|
|
1508
|
+
: AffectedResourceList.code(resource.containerName);
|
|
1509
|
+
}
|
|
1510
|
+
else if (resource.podName) {
|
|
1511
|
+
kind = "Pod";
|
|
1512
|
+
name = AffectedResourceList.code(resource.podName);
|
|
1513
|
+
}
|
|
1514
|
+
else if (resource.workloadName) {
|
|
1515
|
+
kind = workloadLabel;
|
|
1516
|
+
name = AffectedResourceList.code(resource.workloadName);
|
|
1517
|
+
}
|
|
1518
|
+
else if (resource.nodeName) {
|
|
1519
|
+
kind = "Node";
|
|
1520
|
+
name = AffectedResourceList.code(resource.nodeName);
|
|
1521
|
+
}
|
|
1522
|
+
else if (resource.namespace) {
|
|
1523
|
+
kind = "Namespace";
|
|
1524
|
+
name = AffectedResourceList.code(resource.namespace);
|
|
1525
|
+
}
|
|
1526
|
+
const details = [];
|
|
1527
|
+
if (resource.namespace && kind !== "Namespace") {
|
|
1528
|
+
details.push({
|
|
1529
|
+
label: "Namespace",
|
|
1530
|
+
value: AffectedResourceList.code(resource.namespace),
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
const titledByWorkload = !resource.containerName &&
|
|
1534
|
+
!resource.podName &&
|
|
1535
|
+
Boolean(resource.workloadName);
|
|
1536
|
+
if (resource.workloadName && !titledByWorkload) {
|
|
1537
|
+
details.push({
|
|
1538
|
+
label: workloadLabel,
|
|
1539
|
+
value: AffectedResourceList.code(resource.workloadName),
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
if (resource.nodeName && kind !== "Node") {
|
|
1543
|
+
details.push({
|
|
1544
|
+
label: "Node",
|
|
1545
|
+
value: AffectedResourceList.code(resource.nodeName),
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
return {
|
|
1549
|
+
kind: kind,
|
|
1550
|
+
name: name,
|
|
1551
|
+
value: input.value,
|
|
1552
|
+
details: details,
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1493
1555
|
static async buildKubernetesRootCauseContext(input) {
|
|
1494
1556
|
var _a;
|
|
1495
1557
|
const metricResponse = input.dataToProcess;
|
|
@@ -1508,94 +1570,56 @@ ${contextBlock}
|
|
|
1508
1570
|
sections.push(`**Kubernetes Cluster Details**\n${clusterDetails.join("\n")}`);
|
|
1509
1571
|
// Affected resources
|
|
1510
1572
|
if (breakdown.affectedResources && breakdown.affectedResources.length > 0) {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1573
|
+
/*
|
|
1574
|
+
* Keep the rows that satisfy the criteria that just matched, worst
|
|
1575
|
+
* first — NOT simply the non-zero rows. k8s-node-not-ready fires on
|
|
1576
|
+
* `k8s.node.condition_ready = 0`, so the zero rows are the NotReady
|
|
1577
|
+
* nodes; the old hardcoded `> 0` dropped exactly those and named a
|
|
1578
|
+
* healthy node in the analysis below.
|
|
1579
|
+
*/
|
|
1580
|
+
const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
|
|
1581
|
+
criteriaInstance: input.criteriaInstance,
|
|
1582
|
+
floorEqualityFiresOnFall: true,
|
|
1583
|
+
});
|
|
1584
|
+
const sortedResources = breakdown.affectedResources
|
|
1585
|
+
.map((r) => {
|
|
1586
|
+
var _a;
|
|
1587
|
+
/*
|
|
1588
|
+
* A fall criteria breached on the resource's lowest sample
|
|
1589
|
+
* in the window, not its highest.
|
|
1590
|
+
*/
|
|
1591
|
+
return breach.worstIsLowest
|
|
1592
|
+
? Object.assign(Object.assign({}, r), { metricValue: (_a = r.lowestMetricValue) !== null && _a !== void 0 ? _a : r.metricValue }) : r;
|
|
1593
|
+
})
|
|
1516
1594
|
.filter((r) => {
|
|
1517
|
-
return r.metricValue
|
|
1595
|
+
return breach.matches(r.metricValue);
|
|
1518
1596
|
})
|
|
1519
1597
|
.sort((a, b) => {
|
|
1520
|
-
return
|
|
1598
|
+
return breach.worstIsLowest
|
|
1599
|
+
? a.metricValue - b.metricValue
|
|
1600
|
+
: b.metricValue - a.metricValue;
|
|
1521
1601
|
});
|
|
1522
1602
|
if (sortedResources.length === 0) {
|
|
1523
1603
|
return sections.join("\n");
|
|
1524
1604
|
}
|
|
1525
1605
|
// Show top 10 affected resources
|
|
1526
|
-
const resourcesToShow = sortedResources.slice(0,
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
});
|
|
1543
|
-
// Build table header
|
|
1544
|
-
const headerCells = [];
|
|
1545
|
-
if (hasNamespace) {
|
|
1546
|
-
headerCells.push("Namespace");
|
|
1547
|
-
}
|
|
1548
|
-
if (hasWorkload) {
|
|
1549
|
-
headerCells.push("Workload Type");
|
|
1550
|
-
headerCells.push("Workload");
|
|
1551
|
-
}
|
|
1552
|
-
if (hasPod) {
|
|
1553
|
-
headerCells.push("Pod");
|
|
1554
|
-
}
|
|
1555
|
-
if (hasContainer) {
|
|
1556
|
-
headerCells.push("Container");
|
|
1557
|
-
}
|
|
1558
|
-
if (hasNode) {
|
|
1559
|
-
headerCells.push("Node");
|
|
1560
|
-
}
|
|
1561
|
-
headerCells.push("Value");
|
|
1562
|
-
const headerRow = `| ${headerCells.join(" | ")} |`;
|
|
1563
|
-
const separatorRow = `| ${headerCells
|
|
1564
|
-
.map(() => {
|
|
1565
|
-
return "---";
|
|
1566
|
-
})
|
|
1567
|
-
.join(" | ")} |`;
|
|
1568
|
-
resourceLines.push(headerRow);
|
|
1569
|
-
resourceLines.push(separatorRow);
|
|
1570
|
-
for (const resource of resourcesToShow) {
|
|
1571
|
-
const cells = [];
|
|
1572
|
-
if (hasNamespace) {
|
|
1573
|
-
cells.push(resource.namespace ? `\`${resource.namespace}\`` : "-");
|
|
1574
|
-
}
|
|
1575
|
-
if (hasWorkload) {
|
|
1576
|
-
cells.push(resource.workloadType ? `${resource.workloadType}` : "-");
|
|
1577
|
-
cells.push(resource.workloadName ? `\`${resource.workloadName}\`` : "-");
|
|
1578
|
-
}
|
|
1579
|
-
if (hasPod) {
|
|
1580
|
-
cells.push(resource.podName ? `\`${resource.podName}\`` : "-");
|
|
1581
|
-
}
|
|
1582
|
-
if (hasContainer) {
|
|
1583
|
-
cells.push(resource.containerName ? `\`${resource.containerName}\`` : "-");
|
|
1584
|
-
}
|
|
1585
|
-
if (hasNode) {
|
|
1586
|
-
cells.push(resource.nodeName ? `\`${resource.nodeName}\`` : "-");
|
|
1587
|
-
}
|
|
1588
|
-
cells.push(MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
1589
|
-
platform: "kubernetes",
|
|
1590
|
-
metricName: breakdown.metricName,
|
|
1591
|
-
value: resource.metricValue,
|
|
1592
|
-
}));
|
|
1593
|
-
resourceLines.push(`| ${cells.join(" | ")} |`);
|
|
1594
|
-
}
|
|
1595
|
-
if (sortedResources.length > 10) {
|
|
1596
|
-
resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
|
|
1597
|
-
}
|
|
1598
|
-
sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
|
|
1606
|
+
const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
|
|
1607
|
+
sections.push(AffectedResourceList.render({
|
|
1608
|
+
heading: "Affected Resources",
|
|
1609
|
+
overflowNoun: "affected resources",
|
|
1610
|
+
totalCount: sortedResources.length,
|
|
1611
|
+
entries: resourcesToShow.map((resource) => {
|
|
1612
|
+
return MonitorCriteriaEvaluator.getKubernetesAffectedResourceEntry({
|
|
1613
|
+
resource: resource,
|
|
1614
|
+
clusterName: breakdown.clusterName,
|
|
1615
|
+
value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
1616
|
+
platform: "kubernetes",
|
|
1617
|
+
metricName: breakdown.metricName,
|
|
1618
|
+
value: resource.metricValue,
|
|
1619
|
+
}),
|
|
1620
|
+
});
|
|
1621
|
+
}),
|
|
1622
|
+
}));
|
|
1599
1623
|
// Add root cause analysis based on metric type
|
|
1600
1624
|
const analysis = MonitorCriteriaEvaluator.buildKubernetesRootCauseAnalysis({
|
|
1601
1625
|
breakdown: breakdown,
|
|
@@ -1771,6 +1795,75 @@ ${contextBlock}
|
|
|
1771
1795
|
}
|
|
1772
1796
|
return sections.length > 0 ? sections.join("\n") : null;
|
|
1773
1797
|
}
|
|
1798
|
+
/*
|
|
1799
|
+
* What a Proxmox resource is, from the `pve.type` / `pve.scope` the
|
|
1800
|
+
* agent's transform/pve-identity processor derives from its `id`
|
|
1801
|
+
* (node/…, qemu/…, lxc/…, storage/…, cluster/…). A value that processor
|
|
1802
|
+
* does not produce is shown as it arrived rather than guessed at.
|
|
1803
|
+
*/
|
|
1804
|
+
static getProxmoxAffectedResourceKind(resource) {
|
|
1805
|
+
switch (resource.resourceType) {
|
|
1806
|
+
case "qemu":
|
|
1807
|
+
return "Virtual Machine";
|
|
1808
|
+
case "lxc":
|
|
1809
|
+
return "Container";
|
|
1810
|
+
case "node":
|
|
1811
|
+
return "Node";
|
|
1812
|
+
case "storage":
|
|
1813
|
+
return "Storage";
|
|
1814
|
+
default:
|
|
1815
|
+
break;
|
|
1816
|
+
}
|
|
1817
|
+
switch (resource.scope) {
|
|
1818
|
+
case "guest":
|
|
1819
|
+
return "Guest";
|
|
1820
|
+
case "node":
|
|
1821
|
+
return "Node";
|
|
1822
|
+
case "storage":
|
|
1823
|
+
return "Storage";
|
|
1824
|
+
case "cluster":
|
|
1825
|
+
return "Cluster";
|
|
1826
|
+
default:
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
return resource.resourceType || resource.scope || "Resource";
|
|
1830
|
+
}
|
|
1831
|
+
/*
|
|
1832
|
+
* One Proxmox resource as an "Affected Resources" list item: its kind,
|
|
1833
|
+
* its name next to its `qemu/100`-style id, and the node it runs on.
|
|
1834
|
+
*/
|
|
1835
|
+
static getProxmoxAffectedResourceEntry(input) {
|
|
1836
|
+
const resource = input.resource;
|
|
1837
|
+
let kind = MonitorCriteriaEvaluator.getProxmoxAffectedResourceKind(resource);
|
|
1838
|
+
let name = AffectedResourceList.codeWithId({
|
|
1839
|
+
name: resource.resourceName,
|
|
1840
|
+
id: resource.resourceId,
|
|
1841
|
+
});
|
|
1842
|
+
if (!name && resource.nodeName) {
|
|
1843
|
+
// A per-node series that carries no id of its own.
|
|
1844
|
+
kind = "Node";
|
|
1845
|
+
name = AffectedResourceList.code(resource.nodeName);
|
|
1846
|
+
}
|
|
1847
|
+
if (!name) {
|
|
1848
|
+
// Nothing narrower than the cluster — a cluster-wide series.
|
|
1849
|
+
kind = "Cluster";
|
|
1850
|
+
name = AffectedResourceList.code(input.clusterName);
|
|
1851
|
+
}
|
|
1852
|
+
const details = [];
|
|
1853
|
+
// A node's own row would only repeat its name as "Node: …".
|
|
1854
|
+
if (resource.nodeName && kind !== "Node") {
|
|
1855
|
+
details.push({
|
|
1856
|
+
label: "Node",
|
|
1857
|
+
value: AffectedResourceList.code(resource.nodeName),
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
return {
|
|
1861
|
+
kind: kind,
|
|
1862
|
+
name: name,
|
|
1863
|
+
value: input.value,
|
|
1864
|
+
details: details,
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1774
1867
|
static buildProxmoxRootCauseContext(input) {
|
|
1775
1868
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
1776
1869
|
const metricResponse = input.dataToProcess;
|
|
@@ -1807,15 +1900,15 @@ ${contextBlock}
|
|
|
1807
1900
|
}
|
|
1808
1901
|
sections.push(`**Proxmox Cluster Details**\n${clusterDetails.join("\n")}`);
|
|
1809
1902
|
}
|
|
1810
|
-
// Affected resources
|
|
1811
|
-
let
|
|
1903
|
+
// Affected resources: a ranked list of kind, name and node
|
|
1904
|
+
let renderedBreakdownList = false;
|
|
1812
1905
|
if (breakdown && breakdown.affectedResources.length > 0) {
|
|
1813
1906
|
/*
|
|
1814
1907
|
* K8s-parity render: drop zero-value rows, worst (highest)
|
|
1815
1908
|
* first, top 10. Note that for availability metrics (pve_up)
|
|
1816
1909
|
* zero-valued rows ARE the down resources — those still drive
|
|
1817
1910
|
* alerting through the per-series criteria evaluation; this
|
|
1818
|
-
*
|
|
1911
|
+
* list is supplementary context only.
|
|
1819
1912
|
*/
|
|
1820
1913
|
const sortedResources = [
|
|
1821
1914
|
...breakdown.affectedResources,
|
|
@@ -1827,47 +1920,35 @@ ${contextBlock}
|
|
|
1827
1920
|
return b.metricValue - a.metricValue;
|
|
1828
1921
|
});
|
|
1829
1922
|
/*
|
|
1830
|
-
* Skip the
|
|
1923
|
+
* Skip the list when no row carries any identity label
|
|
1831
1924
|
* (cluster-wide series) — it would add nothing.
|
|
1832
1925
|
*/
|
|
1833
1926
|
const hasIdentity = sortedResources.some((r) => {
|
|
1834
1927
|
return r.resourceId || r.resourceName || r.nodeName;
|
|
1835
1928
|
});
|
|
1836
1929
|
if (sortedResources.length > 0 && hasIdentity) {
|
|
1837
|
-
const resourcesToShow = sortedResources.slice(0,
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
: "-";
|
|
1856
|
-
resourceLines.push(`| ${resourceCell} | ${typeCell} | ${nodeCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
1857
|
-
platform: "proxmox",
|
|
1858
|
-
metricName: breakdown.metricName,
|
|
1859
|
-
value: resource.metricValue,
|
|
1860
|
-
})} |`);
|
|
1861
|
-
}
|
|
1862
|
-
if (sortedResources.length > 10) {
|
|
1863
|
-
resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
|
|
1864
|
-
}
|
|
1865
|
-
sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
|
|
1866
|
-
renderedBreakdownTable = true;
|
|
1930
|
+
const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
|
|
1931
|
+
sections.push(AffectedResourceList.render({
|
|
1932
|
+
heading: "Affected Resources",
|
|
1933
|
+
overflowNoun: "affected resources",
|
|
1934
|
+
totalCount: sortedResources.length,
|
|
1935
|
+
entries: resourcesToShow.map((resource) => {
|
|
1936
|
+
return MonitorCriteriaEvaluator.getProxmoxAffectedResourceEntry({
|
|
1937
|
+
resource: resource,
|
|
1938
|
+
clusterName: breakdown.clusterName,
|
|
1939
|
+
value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
1940
|
+
platform: "proxmox",
|
|
1941
|
+
metricName: breakdown.metricName,
|
|
1942
|
+
value: resource.metricValue,
|
|
1943
|
+
}),
|
|
1944
|
+
});
|
|
1945
|
+
}),
|
|
1946
|
+
}));
|
|
1947
|
+
renderedBreakdownList = true;
|
|
1867
1948
|
}
|
|
1868
1949
|
}
|
|
1869
|
-
// Metric results summary (fallback context when no
|
|
1870
|
-
if (!
|
|
1950
|
+
// Metric results summary (fallback context when no list rendered)
|
|
1951
|
+
if (!renderedBreakdownList &&
|
|
1871
1952
|
metricResponse.metricResult &&
|
|
1872
1953
|
metricResponse.metricResult.length > 0) {
|
|
1873
1954
|
const resultDetails = [];
|
|
@@ -1886,8 +1967,8 @@ ${contextBlock}
|
|
|
1886
1967
|
* Which vSphere object one VMware breakdown row describes, resolved
|
|
1887
1968
|
* from the identity attributes the vcenter receiver stamped on it —
|
|
1888
1969
|
* most specific first, in the same order the ingest inventory scan
|
|
1889
|
-
* uses, so the
|
|
1890
|
-
* disagree about the same series. A VM row carries its parent host
|
|
1970
|
+
* uses, so the kind an Affected Resources item is titled with and the
|
|
1971
|
+
* inventory's `kind` never disagree about the same series. A VM row carries its parent host
|
|
1891
1972
|
* (and often a resource pool), so the VM check must run before the
|
|
1892
1973
|
* host and pool checks.
|
|
1893
1974
|
*/
|
|
@@ -1912,6 +1993,84 @@ ${contextBlock}
|
|
|
1912
1993
|
}
|
|
1913
1994
|
return undefined;
|
|
1914
1995
|
}
|
|
1996
|
+
/*
|
|
1997
|
+
* One vSphere object as an "Affected Resources" list item, titled by
|
|
1998
|
+
* the object itself, with the ESXi host and cluster it sits under
|
|
1999
|
+
* listed beneath it.
|
|
2000
|
+
*/
|
|
2001
|
+
static getVMwareAffectedResourceEntry(input) {
|
|
2002
|
+
const resource = input.resource;
|
|
2003
|
+
const kind = MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(resource);
|
|
2004
|
+
let name = "";
|
|
2005
|
+
let showHost = false;
|
|
2006
|
+
switch (kind) {
|
|
2007
|
+
case "Virtual Machine":
|
|
2008
|
+
/*
|
|
2009
|
+
* The VM's display name, falling back to its instance UUID
|
|
2010
|
+
* (`vcenter.vm.id`) when the receiver stamped no name. The Host
|
|
2011
|
+
* detail is the ESXi host the VM is running on.
|
|
2012
|
+
*/
|
|
2013
|
+
name = AffectedResourceList.code(resource.vmName || resource.vmId);
|
|
2014
|
+
showHost = true;
|
|
2015
|
+
break;
|
|
2016
|
+
case "Resource Pool":
|
|
2017
|
+
/*
|
|
2018
|
+
* Pool names are only unique within a parent, so the inventory
|
|
2019
|
+
* path is shown next to the name. The Host detail is the owner
|
|
2020
|
+
* host for a pool on a standalone ESXi host (pools under a
|
|
2021
|
+
* cluster show the cluster).
|
|
2022
|
+
*/
|
|
2023
|
+
name = AffectedResourceList.codeWithId({
|
|
2024
|
+
name: resource.resourcePoolName,
|
|
2025
|
+
id: resource.resourcePoolPath,
|
|
2026
|
+
});
|
|
2027
|
+
showHost = true;
|
|
2028
|
+
break;
|
|
2029
|
+
case "Host":
|
|
2030
|
+
// The host IS the resource; a "Host:" detail would repeat it.
|
|
2031
|
+
name = AffectedResourceList.code(resource.hostName);
|
|
2032
|
+
break;
|
|
2033
|
+
case "Datastore":
|
|
2034
|
+
name = AffectedResourceList.code(resource.datastoreName);
|
|
2035
|
+
break;
|
|
2036
|
+
case "Cluster":
|
|
2037
|
+
name = AffectedResourceList.code(resource.clusterName);
|
|
2038
|
+
break;
|
|
2039
|
+
case "Datacenter":
|
|
2040
|
+
name = AffectedResourceList.code(resource.datacenterName);
|
|
2041
|
+
break;
|
|
2042
|
+
default:
|
|
2043
|
+
break;
|
|
2044
|
+
}
|
|
2045
|
+
const details = [];
|
|
2046
|
+
if (showHost && resource.hostName) {
|
|
2047
|
+
details.push({
|
|
2048
|
+
label: "Host",
|
|
2049
|
+
value: AffectedResourceList.code(resource.hostName),
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
if (kind !== "Cluster" && resource.clusterName) {
|
|
2053
|
+
details.push({
|
|
2054
|
+
label: "Cluster",
|
|
2055
|
+
value: AffectedResourceList.code(resource.clusterName),
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
if (!kind || !name) {
|
|
2059
|
+
// No vSphere identity on the series — it describes the vCenter as a whole.
|
|
2060
|
+
return {
|
|
2061
|
+
kind: "vCenter",
|
|
2062
|
+
name: AffectedResourceList.code(input.vcenterName),
|
|
2063
|
+
value: input.value,
|
|
2064
|
+
details: details,
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
return {
|
|
2068
|
+
kind: kind,
|
|
2069
|
+
name: name,
|
|
2070
|
+
value: input.value,
|
|
2071
|
+
details: details,
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
1915
2074
|
static buildVMwareRootCauseContext(input) {
|
|
1916
2075
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
1917
2076
|
const metricResponse = input.dataToProcess;
|
|
@@ -1959,17 +2118,17 @@ ${contextBlock}
|
|
|
1959
2118
|
}
|
|
1960
2119
|
sections.push(`**vCenter Details**\n${vcenterDetails.join("\n")}`);
|
|
1961
2120
|
}
|
|
1962
|
-
// Affected resources
|
|
1963
|
-
let
|
|
2121
|
+
// Affected resources: a ranked list of object, host and cluster
|
|
2122
|
+
let renderedBreakdownList = false;
|
|
1964
2123
|
if (breakdown && breakdown.affectedResources.length > 0) {
|
|
1965
2124
|
/*
|
|
1966
2125
|
* K8s/Proxmox-parity render: drop zero-value rows, worst (highest)
|
|
1967
2126
|
* first, top 10. For count metrics filtered to an unhealthy state
|
|
1968
2127
|
* (`vcenter.datacenter.host.count{status=red}`) the non-zero rows
|
|
1969
|
-
* ARE the objects with a problem, so the
|
|
2128
|
+
* ARE the objects with a problem, so the list reads correctly;
|
|
1970
2129
|
* a zero row means "nothing in that state" and adds nothing. The
|
|
1971
2130
|
* per-series criteria evaluation is what actually alerts; this
|
|
1972
|
-
*
|
|
2131
|
+
* list is supplementary context only.
|
|
1973
2132
|
*/
|
|
1974
2133
|
const sortedResources = [
|
|
1975
2134
|
...breakdown.affectedResources,
|
|
@@ -1981,7 +2140,7 @@ ${contextBlock}
|
|
|
1981
2140
|
return b.metricValue - a.metricValue;
|
|
1982
2141
|
});
|
|
1983
2142
|
/*
|
|
1984
|
-
* Skip the
|
|
2143
|
+
* Skip the list when no row carries any identity attribute — it
|
|
1985
2144
|
* would add nothing. Every vcenter-receiver series carries at
|
|
1986
2145
|
* least `vcenter.datacenter.name`, so this only trips on a
|
|
1987
2146
|
* breakdown the worker could not attribute at all.
|
|
@@ -1990,77 +2149,28 @@ ${contextBlock}
|
|
|
1990
2149
|
return Boolean(MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(r));
|
|
1991
2150
|
});
|
|
1992
2151
|
if (sortedResources.length > 0 && hasIdentity) {
|
|
1993
|
-
const resourcesToShow = sortedResources.slice(0,
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
: `\`${resource.vmId}\``;
|
|
2012
|
-
hostCell = resource.hostName ? `\`${resource.hostName}\`` : "-";
|
|
2013
|
-
break;
|
|
2014
|
-
case "Resource Pool":
|
|
2015
|
-
/*
|
|
2016
|
-
* Pool names are only unique within a parent, so the
|
|
2017
|
-
* inventory path is shown next to the name. The Host
|
|
2018
|
-
* column is the owner host for a pool on a standalone
|
|
2019
|
-
* ESXi host (pools under a cluster show the cluster).
|
|
2020
|
-
*/
|
|
2021
|
-
if (resource.resourcePoolName && resource.resourcePoolPath) {
|
|
2022
|
-
resourceCell = `\`${resource.resourcePoolName}\` (\`${resource.resourcePoolPath}\`)`;
|
|
2023
|
-
}
|
|
2024
|
-
else {
|
|
2025
|
-
resourceCell = `\`${resource.resourcePoolPath || resource.resourcePoolName}\``;
|
|
2026
|
-
}
|
|
2027
|
-
hostCell = resource.hostName ? `\`${resource.hostName}\`` : "-";
|
|
2028
|
-
break;
|
|
2029
|
-
case "Host":
|
|
2030
|
-
// The host IS the resource; repeating it in the Host column adds nothing.
|
|
2031
|
-
resourceCell = `\`${resource.hostName}\``;
|
|
2032
|
-
break;
|
|
2033
|
-
case "Datastore":
|
|
2034
|
-
resourceCell = `\`${resource.datastoreName}\``;
|
|
2035
|
-
break;
|
|
2036
|
-
case "Cluster":
|
|
2037
|
-
resourceCell = `\`${resource.clusterName}\``;
|
|
2038
|
-
break;
|
|
2039
|
-
case "Datacenter":
|
|
2040
|
-
resourceCell = `\`${resource.datacenterName}\``;
|
|
2041
|
-
break;
|
|
2042
|
-
default:
|
|
2043
|
-
break;
|
|
2044
|
-
}
|
|
2045
|
-
const kindCell = kind || "-";
|
|
2046
|
-
const clusterCell = kind !== "Cluster" && resource.clusterName
|
|
2047
|
-
? `\`${resource.clusterName}\``
|
|
2048
|
-
: "-";
|
|
2049
|
-
resourceLines.push(`| ${resourceCell} | ${kindCell} | ${hostCell} | ${clusterCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
2050
|
-
platform: "vmware",
|
|
2051
|
-
metricName: breakdown.metricName,
|
|
2052
|
-
value: resource.metricValue,
|
|
2053
|
-
})} |`);
|
|
2054
|
-
}
|
|
2055
|
-
if (sortedResources.length > 10) {
|
|
2056
|
-
resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
|
|
2057
|
-
}
|
|
2058
|
-
sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
|
|
2059
|
-
renderedBreakdownTable = true;
|
|
2152
|
+
const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
|
|
2153
|
+
sections.push(AffectedResourceList.render({
|
|
2154
|
+
heading: "Affected Resources",
|
|
2155
|
+
overflowNoun: "affected resources",
|
|
2156
|
+
totalCount: sortedResources.length,
|
|
2157
|
+
entries: resourcesToShow.map((resource) => {
|
|
2158
|
+
return MonitorCriteriaEvaluator.getVMwareAffectedResourceEntry({
|
|
2159
|
+
resource: resource,
|
|
2160
|
+
vcenterName: breakdown.vcenterName,
|
|
2161
|
+
value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
2162
|
+
platform: "vmware",
|
|
2163
|
+
metricName: breakdown.metricName,
|
|
2164
|
+
value: resource.metricValue,
|
|
2165
|
+
}),
|
|
2166
|
+
});
|
|
2167
|
+
}),
|
|
2168
|
+
}));
|
|
2169
|
+
renderedBreakdownList = true;
|
|
2060
2170
|
}
|
|
2061
2171
|
}
|
|
2062
|
-
// Metric results summary (fallback context when no
|
|
2063
|
-
if (!
|
|
2172
|
+
// Metric results summary (fallback context when no list rendered)
|
|
2173
|
+
if (!renderedBreakdownList &&
|
|
2064
2174
|
metricResponse.metricResult &&
|
|
2065
2175
|
metricResponse.metricResult.length > 0) {
|
|
2066
2176
|
const resultDetails = [];
|
|
@@ -2075,6 +2185,46 @@ ${contextBlock}
|
|
|
2075
2185
|
}
|
|
2076
2186
|
return sections.length > 0 ? sections.join("\n") : null;
|
|
2077
2187
|
}
|
|
2188
|
+
/*
|
|
2189
|
+
* One Docker Swarm task as an "Affected Tasks" list item, with the
|
|
2190
|
+
* service it belongs to and the node it is scheduled on beneath it.
|
|
2191
|
+
*/
|
|
2192
|
+
static getDockerSwarmAffectedResourceEntry(input) {
|
|
2193
|
+
const resource = input.resource;
|
|
2194
|
+
let kind = "Cluster";
|
|
2195
|
+
let name = AffectedResourceList.code(input.clusterName);
|
|
2196
|
+
if (resource.containerName) {
|
|
2197
|
+
kind = "Task";
|
|
2198
|
+
name = AffectedResourceList.code(resource.containerName);
|
|
2199
|
+
}
|
|
2200
|
+
else if (resource.serviceName) {
|
|
2201
|
+
kind = "Service";
|
|
2202
|
+
name = AffectedResourceList.code(resource.serviceName);
|
|
2203
|
+
}
|
|
2204
|
+
else if (resource.nodeName) {
|
|
2205
|
+
kind = "Node";
|
|
2206
|
+
name = AffectedResourceList.code(resource.nodeName);
|
|
2207
|
+
}
|
|
2208
|
+
const details = [];
|
|
2209
|
+
if (resource.serviceName && kind !== "Service") {
|
|
2210
|
+
details.push({
|
|
2211
|
+
label: "Service",
|
|
2212
|
+
value: AffectedResourceList.code(resource.serviceName),
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
if (resource.nodeName && kind !== "Node") {
|
|
2216
|
+
details.push({
|
|
2217
|
+
label: "Node",
|
|
2218
|
+
value: AffectedResourceList.code(resource.nodeName),
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
return {
|
|
2222
|
+
kind: kind,
|
|
2223
|
+
name: name,
|
|
2224
|
+
value: input.value,
|
|
2225
|
+
details: details,
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2078
2228
|
static buildDockerSwarmRootCauseContext(input) {
|
|
2079
2229
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
2080
2230
|
const metricResponse = input.dataToProcess;
|
|
@@ -2111,14 +2261,14 @@ ${contextBlock}
|
|
|
2111
2261
|
}
|
|
2112
2262
|
sections.push(`**Docker Swarm Cluster Details**\n${clusterDetails.join("\n")}`);
|
|
2113
2263
|
}
|
|
2114
|
-
// Affected
|
|
2115
|
-
let
|
|
2264
|
+
// Affected tasks: a ranked list of task, service and node
|
|
2265
|
+
let renderedBreakdownList = false;
|
|
2116
2266
|
if (breakdown && breakdown.affectedResources.length > 0) {
|
|
2117
2267
|
/*
|
|
2118
2268
|
* K8s/Proxmox-parity render: drop zero-value rows, worst (highest)
|
|
2119
2269
|
* first, top 10. For the task-down template the breaching rows ARE
|
|
2120
2270
|
* the zero-uptime ones — those still drive alerting through the
|
|
2121
|
-
* per-series criteria evaluation; this
|
|
2271
|
+
* per-series criteria evaluation; this list is supplementary
|
|
2122
2272
|
* context only.
|
|
2123
2273
|
*/
|
|
2124
2274
|
const sortedResources = [
|
|
@@ -2131,42 +2281,35 @@ ${contextBlock}
|
|
|
2131
2281
|
return b.metricValue - a.metricValue;
|
|
2132
2282
|
});
|
|
2133
2283
|
/*
|
|
2134
|
-
* Skip the
|
|
2284
|
+
* Skip the list when no row carries any identity label
|
|
2135
2285
|
* (cluster-wide series) — it would add nothing.
|
|
2136
2286
|
*/
|
|
2137
2287
|
const hasIdentity = sortedResources.some((r) => {
|
|
2138
2288
|
return r.containerName || r.serviceName || r.nodeName;
|
|
2139
2289
|
});
|
|
2140
2290
|
if (sortedResources.length > 0 && hasIdentity) {
|
|
2141
|
-
const resourcesToShow = sortedResources.slice(0,
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
})} |`);
|
|
2160
|
-
}
|
|
2161
|
-
if (sortedResources.length > 10) {
|
|
2162
|
-
resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected tasks*`);
|
|
2163
|
-
}
|
|
2164
|
-
sections.push(`\n\n**Affected Tasks** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
|
|
2165
|
-
renderedBreakdownTable = true;
|
|
2291
|
+
const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
|
|
2292
|
+
sections.push(AffectedResourceList.render({
|
|
2293
|
+
heading: "Affected Tasks",
|
|
2294
|
+
overflowNoun: "affected tasks",
|
|
2295
|
+
totalCount: sortedResources.length,
|
|
2296
|
+
entries: resourcesToShow.map((resource) => {
|
|
2297
|
+
return MonitorCriteriaEvaluator.getDockerSwarmAffectedResourceEntry({
|
|
2298
|
+
resource: resource,
|
|
2299
|
+
clusterName: breakdown.clusterName,
|
|
2300
|
+
value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
2301
|
+
platform: "dockerSwarm",
|
|
2302
|
+
metricName: breakdown.metricName,
|
|
2303
|
+
value: resource.metricValue,
|
|
2304
|
+
}),
|
|
2305
|
+
});
|
|
2306
|
+
}),
|
|
2307
|
+
}));
|
|
2308
|
+
renderedBreakdownList = true;
|
|
2166
2309
|
}
|
|
2167
2310
|
}
|
|
2168
|
-
// Metric results summary (fallback context when no
|
|
2169
|
-
if (!
|
|
2311
|
+
// Metric results summary (fallback context when no list rendered)
|
|
2312
|
+
if (!renderedBreakdownList &&
|
|
2170
2313
|
metricResponse.metricResult &&
|
|
2171
2314
|
metricResponse.metricResult.length > 0) {
|
|
2172
2315
|
const resultDetails = [];
|
|
@@ -2182,31 +2325,49 @@ ${contextBlock}
|
|
|
2182
2325
|
return sections.length > 0 ? sections.join("\n") : null;
|
|
2183
2326
|
}
|
|
2184
2327
|
/*
|
|
2185
|
-
* Which rows of
|
|
2328
|
+
* Which rows of an affected-resources list actually BREACHED.
|
|
2186
2329
|
*
|
|
2187
2330
|
* The blanket `metricValue > 0` below is right for every count- or
|
|
2188
2331
|
* level-style criteria ("> 0 active health checks", "> 85 % used"), but
|
|
2189
|
-
* it is exactly backwards for the availability signals: the OSD
|
|
2190
|
-
* OSD Out / Quorum Degraded criteria fire on `< 1` over
|
|
2191
|
-
* ceph_osd_in / ceph_mon_quorum_status, so the ZERO rows
|
|
2192
|
-
* daemons. Dropping them rendered a
|
|
2193
|
-
* an incident that tells the reader to "check the root
|
|
2194
|
-
* affected ceph_daemon label".
|
|
2332
|
+
* it is exactly backwards for the availability signals: the Ceph OSD
|
|
2333
|
+
* Down / OSD Out / Quorum Degraded criteria fire on `< 1` over
|
|
2334
|
+
* ceph_osd_up / ceph_osd_in / ceph_mon_quorum_status, so the ZERO rows
|
|
2335
|
+
* are the down daemons. Dropping them rendered a list of the HEALTHY
|
|
2336
|
+
* daemons under an incident that tells the reader to "check the root
|
|
2337
|
+
* cause for the affected ceph_daemon label".
|
|
2195
2338
|
*
|
|
2196
2339
|
* Invert only for a criteria that fires when the metric FALLS.
|
|
2197
2340
|
* Everything else — every `> 0` health check, PG count, latency and
|
|
2198
|
-
* capacity ratio, and every `= 0` recovery criteria —
|
|
2199
|
-
* existing `> 0`, worst-highest-first
|
|
2341
|
+
* capacity ratio, and (by default) every `= 0` recovery criteria —
|
|
2342
|
+
* keeps the existing `> 0`, worst-highest-first list byte for byte.
|
|
2343
|
+
*
|
|
2344
|
+
* `floorEqualityFiresOnFall` additionally counts a FIRING `= 0` (or
|
|
2345
|
+
* below) as a fall. Kubernetes needs it: k8s-node-not-ready and
|
|
2346
|
+
* k8s-etcd-no-leader fire on `= 0` over k8s.node.condition_ready /
|
|
2347
|
+
* etcd_server_has_leader, and zero is the floor of those metrics, so
|
|
2348
|
+
* the only way to reach it is down. It is gated on the criteria opening
|
|
2349
|
+
* an incident or alert because the same `= 0` is also how the restart,
|
|
2350
|
+
* failed-pod and unavailable-replica templates express RECOVERY, and
|
|
2351
|
+
* that all-clear must not become a list of healthy zero rows. Ceph
|
|
2352
|
+
* leaves it off: all of its `= 0` criteria are recoveries.
|
|
2200
2353
|
*/
|
|
2201
|
-
static
|
|
2202
|
-
var _a;
|
|
2203
|
-
const metricFilters = (((_a = criteriaInstance === null ||
|
|
2354
|
+
static getAffectedResourceBreachPredicate(input) {
|
|
2355
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2356
|
+
const metricFilters = (((_b = (_a = input.criteriaInstance) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.filters) || []).filter((f) => {
|
|
2204
2357
|
return f.checkOn === CheckOn.MetricValue && typeof f.value === "number";
|
|
2205
2358
|
});
|
|
2359
|
+
const opensIncidentOrAlert = ((_d = (_c = input.criteriaInstance) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.createIncidents) === true ||
|
|
2360
|
+
((_f = (_e = input.criteriaInstance) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.createAlerts) === true;
|
|
2206
2361
|
const firesWhenMetricFalls = metricFilters.length > 0 &&
|
|
2207
2362
|
metricFilters.every((f) => {
|
|
2208
|
-
|
|
2209
|
-
f.filterType === FilterType.LessThanOrEqualTo)
|
|
2363
|
+
if (f.filterType === FilterType.LessThan ||
|
|
2364
|
+
f.filterType === FilterType.LessThanOrEqualTo) {
|
|
2365
|
+
return true;
|
|
2366
|
+
}
|
|
2367
|
+
return (input.floorEqualityFiresOnFall === true &&
|
|
2368
|
+
opensIncidentOrAlert &&
|
|
2369
|
+
f.filterType === FilterType.EqualTo &&
|
|
2370
|
+
f.value <= 0);
|
|
2210
2371
|
});
|
|
2211
2372
|
if (!firesWhenMetricFalls) {
|
|
2212
2373
|
return {
|
|
@@ -2220,6 +2381,9 @@ ${contextBlock}
|
|
|
2220
2381
|
matches: (value) => {
|
|
2221
2382
|
return metricFilters.some((f) => {
|
|
2222
2383
|
const threshold = f.value;
|
|
2384
|
+
if (f.filterType === FilterType.EqualTo) {
|
|
2385
|
+
return value === threshold;
|
|
2386
|
+
}
|
|
2223
2387
|
return f.filterType === FilterType.LessThan
|
|
2224
2388
|
? value < threshold
|
|
2225
2389
|
: value <= threshold;
|
|
@@ -2232,6 +2396,71 @@ ${contextBlock}
|
|
|
2232
2396
|
worstIsLowest: true,
|
|
2233
2397
|
};
|
|
2234
2398
|
}
|
|
2399
|
+
/*
|
|
2400
|
+
* One Ceph daemon or pool as an "Affected Resources" list item, with
|
|
2401
|
+
* the pool and host it belongs to beneath it.
|
|
2402
|
+
*/
|
|
2403
|
+
static getCephAffectedResourceEntry(input) {
|
|
2404
|
+
const resource = input.resource;
|
|
2405
|
+
/*
|
|
2406
|
+
* The worker stores every series' `name` label as poolName, but only
|
|
2407
|
+
* ceph_pool_metadata's `name` is a pool — and that series always
|
|
2408
|
+
* carries pool_id too. On ceph_health_detail, `name` is the health
|
|
2409
|
+
* check (RECENT_CRASH, OSD_NEARFULL, ...), and titling it "Pool" told
|
|
2410
|
+
* the reader a pool called OSD_NEARFULL was the problem. So a name is
|
|
2411
|
+
* treated as a pool only when it arrives with a pool id.
|
|
2412
|
+
*/
|
|
2413
|
+
const pool = resource.poolId
|
|
2414
|
+
? AffectedResourceList.codeWithId({
|
|
2415
|
+
name: resource.poolName,
|
|
2416
|
+
id: resource.poolId,
|
|
2417
|
+
})
|
|
2418
|
+
: "";
|
|
2419
|
+
const otherName = resource.poolId
|
|
2420
|
+
? ""
|
|
2421
|
+
: AffectedResourceList.code(resource.poolName);
|
|
2422
|
+
const otherNameKind = input.metricName === "ceph_health_detail" ? "Health Check" : "Resource";
|
|
2423
|
+
let kind = "Cluster";
|
|
2424
|
+
let name = AffectedResourceList.code(input.clusterName);
|
|
2425
|
+
if (resource.daemon) {
|
|
2426
|
+
kind = "Daemon";
|
|
2427
|
+
name = AffectedResourceList.code(resource.daemon);
|
|
2428
|
+
}
|
|
2429
|
+
else if (pool) {
|
|
2430
|
+
kind = "Pool";
|
|
2431
|
+
name = pool;
|
|
2432
|
+
}
|
|
2433
|
+
else if (otherName) {
|
|
2434
|
+
kind = otherNameKind;
|
|
2435
|
+
name = otherName;
|
|
2436
|
+
}
|
|
2437
|
+
else if (resource.hostname) {
|
|
2438
|
+
kind = "Host";
|
|
2439
|
+
name = AffectedResourceList.code(resource.hostname);
|
|
2440
|
+
}
|
|
2441
|
+
const details = [];
|
|
2442
|
+
if (pool && name !== pool) {
|
|
2443
|
+
details.push({ label: "Pool", value: pool });
|
|
2444
|
+
}
|
|
2445
|
+
if (otherName && name !== otherName) {
|
|
2446
|
+
details.push({
|
|
2447
|
+
label: otherNameKind === "Resource" ? "Name" : otherNameKind,
|
|
2448
|
+
value: otherName,
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2451
|
+
if (resource.hostname && kind !== "Host") {
|
|
2452
|
+
details.push({
|
|
2453
|
+
label: "Host",
|
|
2454
|
+
value: AffectedResourceList.code(resource.hostname),
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
return {
|
|
2458
|
+
kind: kind,
|
|
2459
|
+
name: name,
|
|
2460
|
+
value: input.value,
|
|
2461
|
+
details: details,
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2235
2464
|
static buildCephRootCauseContext(input) {
|
|
2236
2465
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
2237
2466
|
const metricResponse = input.dataToProcess;
|
|
@@ -2262,8 +2491,8 @@ ${contextBlock}
|
|
|
2262
2491
|
}
|
|
2263
2492
|
sections.push(`**Ceph Cluster Details**\n${clusterDetails.join("\n")}`);
|
|
2264
2493
|
}
|
|
2265
|
-
// Affected resources
|
|
2266
|
-
let
|
|
2494
|
+
// Affected resources: a ranked list of daemon, pool and host
|
|
2495
|
+
let renderedBreakdownList = false;
|
|
2267
2496
|
if (breakdown && breakdown.affectedResources.length > 0) {
|
|
2268
2497
|
/*
|
|
2269
2498
|
* Keep the rows that satisfy the criteria that just matched, worst
|
|
@@ -2271,7 +2500,9 @@ ${contextBlock}
|
|
|
2271
2500
|
* ceph_osd_in / ceph_mon_quorum_status the criteria is `< 1`, so
|
|
2272
2501
|
* the zero rows are the whole point of the incident.
|
|
2273
2502
|
*/
|
|
2274
|
-
const breach = MonitorCriteriaEvaluator.
|
|
2503
|
+
const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
|
|
2504
|
+
criteriaInstance: input.criteriaInstance,
|
|
2505
|
+
});
|
|
2275
2506
|
const sortedResources = [
|
|
2276
2507
|
...breakdown.affectedResources,
|
|
2277
2508
|
]
|
|
@@ -2284,7 +2515,7 @@ ${contextBlock}
|
|
|
2284
2515
|
: b.metricValue - a.metricValue;
|
|
2285
2516
|
});
|
|
2286
2517
|
/*
|
|
2287
|
-
* Skip the
|
|
2518
|
+
* Skip the list when no row carries any identity label
|
|
2288
2519
|
* (cluster-wide series like ceph_health_status) — it would add
|
|
2289
2520
|
* nothing.
|
|
2290
2521
|
*/
|
|
@@ -2292,42 +2523,29 @@ ${contextBlock}
|
|
|
2292
2523
|
return r.daemon || r.poolId || r.poolName || r.hostname;
|
|
2293
2524
|
});
|
|
2294
2525
|
if (sortedResources.length > 0 && hasIdentity) {
|
|
2295
|
-
const resourcesToShow = sortedResources.slice(0,
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
? `\`${resource.hostname}\``
|
|
2315
|
-
: "-";
|
|
2316
|
-
resourceLines.push(`| ${daemonCell} | ${poolCell} | ${hostCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
2317
|
-
platform: "ceph",
|
|
2318
|
-
metricName: breakdown.metricName,
|
|
2319
|
-
value: resource.metricValue,
|
|
2320
|
-
})} |`);
|
|
2321
|
-
}
|
|
2322
|
-
if (sortedResources.length > 10) {
|
|
2323
|
-
resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
|
|
2324
|
-
}
|
|
2325
|
-
sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
|
|
2326
|
-
renderedBreakdownTable = true;
|
|
2526
|
+
const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
|
|
2527
|
+
sections.push(AffectedResourceList.render({
|
|
2528
|
+
heading: "Affected Resources",
|
|
2529
|
+
overflowNoun: "affected resources",
|
|
2530
|
+
totalCount: sortedResources.length,
|
|
2531
|
+
entries: resourcesToShow.map((resource) => {
|
|
2532
|
+
return MonitorCriteriaEvaluator.getCephAffectedResourceEntry({
|
|
2533
|
+
resource: resource,
|
|
2534
|
+
clusterName: breakdown.clusterName,
|
|
2535
|
+
metricName: breakdown.metricName,
|
|
2536
|
+
value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
|
|
2537
|
+
platform: "ceph",
|
|
2538
|
+
metricName: breakdown.metricName,
|
|
2539
|
+
value: resource.metricValue,
|
|
2540
|
+
}),
|
|
2541
|
+
});
|
|
2542
|
+
}),
|
|
2543
|
+
}));
|
|
2544
|
+
renderedBreakdownList = true;
|
|
2327
2545
|
}
|
|
2328
2546
|
}
|
|
2329
|
-
// Metric results summary (fallback context when no
|
|
2330
|
-
if (!
|
|
2547
|
+
// Metric results summary (fallback context when no list rendered)
|
|
2548
|
+
if (!renderedBreakdownList &&
|
|
2331
2549
|
metricResponse.metricResult &&
|
|
2332
2550
|
metricResponse.metricResult.length > 0) {
|
|
2333
2551
|
const resultDetails = [];
|