@oneuptime/common 12.0.8 → 12.0.9
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/MetricItemAggMV1mByK8sCluster.ts +1 -1
- package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
- package/Models/DatabaseModels/AIConversation.ts +32 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
- package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
- package/Models/DatabaseModels/Index.ts +12 -4
- package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +139 -9
- package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
- package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +8 -8
- package/Models/DatabaseModels/NetworkDevice.ts +141 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
- package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
- package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
- package/Models/DatabaseModels/Project.ts +78 -0
- package/Models/DatabaseModels/UserCall.ts +42 -0
- package/Models/DatabaseModels/UserEmail.ts +44 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
- package/Models/DatabaseModels/UserPush.ts +49 -0
- package/Models/DatabaseModels/UserSMS.ts +43 -0
- package/Models/DatabaseModels/UserTelegram.ts +59 -0
- package/Models/DatabaseModels/UserWebhook.ts +52 -0
- package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
- package/Server/API/AIChatAPI.ts +315 -1
- package/Server/API/DashboardAPI.ts +217 -1
- package/Server/API/OnCallReadinessAPI.ts +841 -0
- package/Server/API/TeamComplianceAPI.ts +69 -17
- package/Server/API/TelemetryAPI.ts +220 -12
- package/Server/EnvironmentConfig.ts +52 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
- package/Server/Infrastructure/Queue.ts +78 -13
- package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
- package/Server/Services/AIService.ts +7 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/AlertSeverityService.ts +63 -0
- package/Server/Services/DashboardService.ts +9 -10
- package/Server/Services/DatabaseService.ts +32 -2
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/IncidentSeverityService.ts +76 -0
- package/Server/Services/Index.ts +12 -4
- package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
- package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +4 -4
- package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +89 -20
- package/Server/Services/LogAggregationService.ts +45 -8
- package/Server/Services/MetricAggregationService.ts +121 -0
- package/Server/Services/MetricService.ts +7 -7
- package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
- package/Server/Services/NetworkDeviceLinkService.ts +84 -0
- package/Server/Services/NetworkDeviceService.ts +140 -0
- package/Server/Services/NetworkSiteService.ts +77 -25
- package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
- package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
- package/Server/Services/OnCallReadinessService.ts +2803 -0
- package/Server/Services/OnCallSetupReminderService.ts +955 -0
- package/Server/Services/ProfileAggregationService.ts +123 -0
- package/Server/Services/StatusPageService.ts +9 -10
- package/Server/Services/TeamComplianceService.ts +429 -252
- package/Server/Services/UserCallService.ts +26 -1
- package/Server/Services/UserEmailService.ts +26 -1
- package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
- package/Server/Services/UserNotificationRuleService.ts +3812 -333
- package/Server/Services/UserOnCallLogService.ts +561 -48
- package/Server/Services/UserPushService.ts +29 -0
- package/Server/Services/UserService.ts +11 -0
- package/Server/Services/UserSmsService.ts +26 -1
- package/Server/Services/UserTelegramService.ts +24 -1
- package/Server/Services/UserWebhookService.ts +28 -1
- package/Server/Services/UserWhatsAppService.ts +24 -1
- package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
- package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
- package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
- package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
- package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
- package/Server/Types/Workflow/Components/API/Get.ts +1 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
- package/Server/Types/Workflow/Components/API/Post.ts +1 -1
- package/Server/Types/Workflow/Components/API/Put.ts +1 -1
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
- package/Server/Types/Workflow/Components/Email.ts +25 -7
- package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
- package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
- package/Server/Utils/AI/Toolbox/Index.ts +48 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
- package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
- package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
- package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
- package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
- package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
- package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
- package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
- package/Server/Utils/ClientIp.ts +221 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
- package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
- package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
- package/Server/Utils/Express.ts +12 -17
- package/Server/Utils/LLM/LLMService.ts +85 -8
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
- package/Server/Utils/SSRFProtection.ts +98 -23
- package/Server/Utils/StartServer.ts +12 -3
- package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +25 -25
- package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
- package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
- package/Tests/Models/InventoryItemModel.test.ts +174 -0
- package/Tests/Models/InventoryItemNaming.test.ts +302 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
- package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
- package/Tests/Server/API/Helpers.ts +24 -15
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
- package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
- package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
- package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
- package/Tests/Server/Services/{TelemetryEntityManualCreate.test.ts → InventoryItemManualCreate.test.ts} +27 -27
- package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
- package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
- package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
- package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
- package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
- package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
- package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
- package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
- package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
- package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
- package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
- package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
- package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
- package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
- package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
- package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
- package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
- package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
- package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
- package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
- package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
- package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
- package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
- package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
- package/Tests/Server/Utils/ClientIp.test.ts +438 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
- package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
- package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +15 -15
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
- package/Tests/Types/IP/IP.test.ts +263 -0
- package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
- package/Tests/Types/IP/IPv6.test.ts +12 -1
- package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +6 -6
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
- package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
- package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +112 -0
- package/Tests/Types/Workflow/Templates.test.ts +916 -101
- package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
- package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
- package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
- package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
- package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
- package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
- package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +72 -0
- package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +242 -7
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +15 -7
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +242 -28
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +59 -1
- package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
- package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
- package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
- package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
- package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
- package/Tests/UI/EsbuildConfig.test.ts +607 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
- package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
- package/Tests/UI/Utils/Platform.test.ts +147 -0
- package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +487 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
- package/Tests/__mocks__/bullmq.js +55 -0
- package/Types/AI/AIChatMessageStatus.ts +8 -1
- package/Types/AI/AIChatTypes.ts +12 -0
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
- package/Types/Exception/ExceptionCode.ts +2 -0
- package/Types/Exception/ServiceUnavailableException.ts +8 -0
- package/Types/Exception/TooManyRequestsException.ts +8 -0
- package/Types/IP/IP.ts +93 -47
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +49 -3
- package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
- package/Types/OnCallDutyPolicy/Layer.ts +203 -149
- package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
- package/Types/Permission.ts +193 -0
- package/Types/Telemetry/EntityRelationshipType.ts +1 -1
- package/Types/Telemetry/EntitySource.ts +1 -1
- package/Types/Telemetry/EntityType.ts +1 -1
- package/Types/Telemetry/EntityTypeGroups.ts +1 -1
- package/Types/Workflow/Components/BaseModel.ts +75 -29
- package/Types/Workflow/Components/Discord.ts +1 -0
- package/Types/Workflow/Components/Email.ts +12 -3
- package/Types/Workflow/Components/JavaScript.ts +7 -0
- package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
- package/Types/Workflow/Components/Slack.ts +1 -0
- package/Types/Workflow/Components/Telegram.ts +1 -0
- package/Types/Workflow/TemplateSyntax.ts +44 -0
- package/Types/Workflow/Templates.ts +2097 -45
- package/UI/Components/Calendar/Calendar.css +43 -0
- package/UI/Components/Calendar/Calendar.tsx +8 -0
- package/UI/Components/Card/Card.tsx +2 -2
- package/UI/Components/Checkbox/Checkbox.tsx +16 -0
- package/UI/Components/Dictionary/Dictionary.tsx +48 -9
- package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
- package/UI/Components/Input/Input.tsx +1 -0
- package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
- package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
- package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
- package/UI/Components/LogsViewer/types.ts +23 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
- package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
- package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
- package/UI/Components/Table/Table.tsx +49 -16
- package/UI/Components/Table/TableBody.tsx +53 -28
- package/UI/Components/Table/TableHeader.tsx +13 -0
- package/UI/Components/Table/TableRow.tsx +58 -26
- package/UI/Components/Workflow/ArgumentsForm.tsx +155 -9
- package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +10 -1
- package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
- package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
- package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
- package/UI/Components/Workflow/GraphLint.ts +42 -5
- package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +154 -22
- package/UI/Components/Workflow/ModelSchema.ts +115 -33
- package/UI/Components/Workflow/RunStatusWatcher.ts +1 -1
- package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
- package/UI/Components/Workflow/UseRunWatch.ts +212 -0
- package/UI/Components/Workflow/VariableModal.tsx +6 -2
- package/UI/Components/Workflow/Workflow.tsx +49 -3
- package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
- package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
- package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
- package/UI/Utils/Platform.ts +149 -0
- package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
- package/UI/esbuild-config.js +22 -1
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +888 -164
- package/Utils/Telemetry/CrossSignalScope.ts +502 -0
- package/Utils/Telemetry/EntityKey.ts +5 -5
- package/Utils/Telemetry/EntityRelationship.ts +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +12 -4
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +167 -31
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +27 -27
- package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +80 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
- package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +237 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +165 -13
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
- package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +129 -18
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +45 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +72 -13
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +1 -0
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +54 -0
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/DashboardService.js +10 -9
- package/build/dist/Server/Services/DashboardService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +24 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +12 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +6 -6
- package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +68 -23
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
- package/build/dist/Server/Services/LogAggregationService.js +27 -8
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +80 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +6 -6
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +53 -13
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
- package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
- package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
- package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +11 -10
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceService.js +312 -160
- package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +24 -1
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +24 -1
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +26 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +24 -1
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +22 -1
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +25 -1
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
- package/build/dist/Server/Utils/ClientIp.js +137 -0
- package/build/dist/Server/Utils/ClientIp.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -12
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +82 -21
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +11 -11
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatTypes.js +12 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +2 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
- package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
- package/build/dist/Types/IP/IP.js +87 -43
- package/build/dist/Types/IP/IP.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +174 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +66 -28
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Discord.js +1 -0
- package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Email.js +12 -3
- package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
- package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Slack.js +1 -0
- package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
- package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
- package/build/dist/Types/Workflow/TemplateSyntax.js +12 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -1
- package/build/dist/Types/Workflow/Templates.js +1931 -38
- package/build/dist/Types/Workflow/Templates.js.map +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -11
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -0
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +27 -15
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +24 -18
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +9 -1
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +29 -21
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +130 -12
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +4 -4
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +33 -4
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +116 -21
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelSchema.js +72 -34
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
- package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
- package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +41 -1
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Platform.js +118 -0
- package/build/dist/UI/Utils/Platform.js.map +1 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +642 -136
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +5 -5
- package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
- package/jest.config.json +1 -0
- package/package.json +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
|
@@ -29,6 +29,33 @@ export interface TopologyDeviceInput {
|
|
|
29
29
|
*/
|
|
30
30
|
sysDescr?: string | undefined;
|
|
31
31
|
sysObjectId?: string | undefined;
|
|
32
|
+
/*
|
|
33
|
+
* ENTITY-MIB chassis serial. Not rendered — it is here because LLDP
|
|
34
|
+
* chassis-id subtype 7 ("locally assigned") is very often exactly this
|
|
35
|
+
* string, so without it a peer we already manage is drawn as a stranger.
|
|
36
|
+
*/
|
|
37
|
+
serialNumber?: string | undefined;
|
|
38
|
+
/*
|
|
39
|
+
* Health as reported by a Monitor watching this device, already reduced to
|
|
40
|
+
* the map's three states by the caller. Load-bearing for monitor-backed
|
|
41
|
+
* devices (monitoringMethod "Monitor"): nothing ever polls them, so their
|
|
42
|
+
* lastSeenAt is permanently absent and freshness alone would draw every
|
|
43
|
+
* one of them as "unknown" forever.
|
|
44
|
+
*/
|
|
45
|
+
monitorStatus?: NetworkTopologyNodeStatus | undefined;
|
|
46
|
+
/*
|
|
47
|
+
* Whether the device's interface walk is on. LLDP and CDP ride along with
|
|
48
|
+
* it, so a device with it off can never report a neighbour — which is one
|
|
49
|
+
* of the ways a device ends up drawn floating, and one the map has no
|
|
50
|
+
* other way to explain.
|
|
51
|
+
*/
|
|
52
|
+
isNeighborDiscoveryEnabled?: boolean | undefined;
|
|
53
|
+
/*
|
|
54
|
+
* Every interface MAC this device reports. Not rendered either: LLDP
|
|
55
|
+
* chassis-id subtype 4 IS a MAC, and it is frequently the only thing a
|
|
56
|
+
* peer advertises about itself.
|
|
57
|
+
*/
|
|
58
|
+
macAddresses?: Array<string> | undefined;
|
|
32
59
|
lldpNeighbors?: Array<LldpNeighbor> | undefined;
|
|
33
60
|
cdpNeighbors?: Array<CdpNeighbor> | undefined;
|
|
34
61
|
}
|
|
@@ -68,6 +95,22 @@ export interface TopologyEndpointInput {
|
|
|
68
95
|
lastSeenAt?: Date | undefined;
|
|
69
96
|
}
|
|
70
97
|
|
|
98
|
+
/*
|
|
99
|
+
* One operator-declared link (NetworkDeviceLink), reduced to what the graph
|
|
100
|
+
* needs. These exist for cables neither discovery protocol can see: a device
|
|
101
|
+
* with LLDP/CDP disabled, one that speaks neither, or a monitor-backed device
|
|
102
|
+
* that is never walked at all.
|
|
103
|
+
*/
|
|
104
|
+
export interface TopologyManualLinkInput {
|
|
105
|
+
fromDeviceId: string;
|
|
106
|
+
toDeviceId: string;
|
|
107
|
+
name?: string | undefined;
|
|
108
|
+
fromPortName?: string | undefined;
|
|
109
|
+
toPortName?: string | undefined;
|
|
110
|
+
// Health from a bound Monitor, already reduced by the caller.
|
|
111
|
+
monitorStatus?: "up" | "down" | undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
71
114
|
/*
|
|
72
115
|
* buildTopology's result: the wire-format NetworkTopology plus endpoint
|
|
73
116
|
* bookkeeping. The extra fields are optional on the wire type, so older
|
|
@@ -78,6 +121,13 @@ export interface TopologyBuildResult extends NetworkTopology {
|
|
|
78
121
|
droppedEndpointCount?: number | undefined;
|
|
79
122
|
// True when more attachable endpoints existed than the render cap allows.
|
|
80
123
|
endpointsTruncated?: boolean | undefined;
|
|
124
|
+
/*
|
|
125
|
+
* Nodes removed because the project hid them. Always reported, even when
|
|
126
|
+
* zero: a map that quietly drops things is the same failure as a map that
|
|
127
|
+
* quietly invents them, and this is what the "N hidden — show" affordance
|
|
128
|
+
* counts.
|
|
129
|
+
*/
|
|
130
|
+
suppressedNodeCount?: number | undefined;
|
|
81
131
|
}
|
|
82
132
|
|
|
83
133
|
/*
|
|
@@ -86,14 +136,86 @@ export interface TopologyBuildResult extends NetworkTopology {
|
|
|
86
136
|
*/
|
|
87
137
|
interface NeighborClaim {
|
|
88
138
|
protocol: NetworkTopologyLinkProtocol;
|
|
89
|
-
|
|
139
|
+
/*
|
|
140
|
+
* Every identifier this report offers for the peer, strongest source
|
|
141
|
+
* first — LLDP's remoteSysName ahead of its remoteChassisId, CDP's
|
|
142
|
+
* remoteDeviceId on its own. Plural because a chassis id is frequently
|
|
143
|
+
* the ONLY thing a peer advertises, and it names a device we already
|
|
144
|
+
* manage exactly as well as a sysName does.
|
|
145
|
+
*/
|
|
146
|
+
identifiers: Array<string>;
|
|
90
147
|
displayName: string | undefined;
|
|
148
|
+
// True when displayName came from an advertised name, not a chassis id.
|
|
149
|
+
hasAdvertisedName: boolean;
|
|
91
150
|
localInterfaceIndex: number | undefined;
|
|
92
151
|
remotePortId: string | undefined;
|
|
93
152
|
// CDP-only: the platform string the neighbor advertises about itself.
|
|
94
153
|
remotePlatform: string | undefined;
|
|
95
154
|
}
|
|
96
155
|
|
|
156
|
+
/*
|
|
157
|
+
* How a device can be recognized, strongest evidence first.
|
|
158
|
+
*
|
|
159
|
+
* "name" — something the device answers to (sysName, hostname, or the
|
|
160
|
+
* row's own name); what both discovery protocols advertise.
|
|
161
|
+
* "serial" — the ENTITY-MIB chassis serial, which is what LLDP
|
|
162
|
+
* chassis-id subtype 7 ("locally assigned") usually carries.
|
|
163
|
+
* "mac" — an interface MAC; LLDP chassis-id subtype 4 IS a MAC.
|
|
164
|
+
* "shortHost" — the first label of an FQDN, so `sw01.corp.local` and
|
|
165
|
+
* `sw01` are read as one device. Deliberately weakest: it
|
|
166
|
+
* throws information away, so it may only decide a match no
|
|
167
|
+
* stronger key could.
|
|
168
|
+
*/
|
|
169
|
+
type MatchKeyKind = "name" | "serial" | "mac" | "shortHost";
|
|
170
|
+
|
|
171
|
+
const MATCH_KEY_KINDS_STRONGEST_FIRST: ReadonlyArray<MatchKeyKind> = [
|
|
172
|
+
"name",
|
|
173
|
+
"serial",
|
|
174
|
+
"mac",
|
|
175
|
+
"shortHost",
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
/*
|
|
179
|
+
* The kinds strong enough to say "these two reports describe one peer".
|
|
180
|
+
*
|
|
181
|
+
* shortHost is excluded on purpose: `sw01.site-a` and `sw01.site-b` share
|
|
182
|
+
* one, and merging those would invent a device nobody owns. Against a
|
|
183
|
+
* MANAGED device the same key is safe, because a key two devices both
|
|
184
|
+
* claim is dropped from the index entirely rather than guessed at.
|
|
185
|
+
*/
|
|
186
|
+
const PEER_IDENTITY_KINDS: ReadonlySet<MatchKeyKind> = new Set<MatchKeyKind>([
|
|
187
|
+
"name",
|
|
188
|
+
"serial",
|
|
189
|
+
"mac",
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
interface MatchKey {
|
|
193
|
+
kind: MatchKeyKind;
|
|
194
|
+
value: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/*
|
|
198
|
+
* One neighbor report, with its peer already resolved. Claims are resolved
|
|
199
|
+
* in a pass of their own because the identity of an UNMANAGED peer is not
|
|
200
|
+
* knowable from a single report: two switches can describe the same
|
|
201
|
+
* stranger by different identifiers, and only the whole set says they meant
|
|
202
|
+
* one box.
|
|
203
|
+
*/
|
|
204
|
+
interface ResolvedClaim {
|
|
205
|
+
device: TopologyDeviceInput;
|
|
206
|
+
claim: NeighborClaim;
|
|
207
|
+
// Set when the claim named a device in this graph.
|
|
208
|
+
matchedDeviceId: string | undefined;
|
|
209
|
+
// Populated otherwise: the keys this unmanaged peer is known by.
|
|
210
|
+
peerAliases: Array<MatchKey>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* Hoisted so the literal is not the object of a member expression, which
|
|
215
|
+
* `wrap-regex` and Prettier cannot agree on.
|
|
216
|
+
*/
|
|
217
|
+
const IPV4_PATTERN: RegExp = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
218
|
+
|
|
97
219
|
// A device not seen within this window is treated as not currently up.
|
|
98
220
|
const FRESH_WINDOW_MS: number = 15 * 60 * 1000;
|
|
99
221
|
|
|
@@ -103,9 +225,11 @@ const ENDPOINT_RENDER_CAP: number = 2000;
|
|
|
103
225
|
export default class NetworkTopologyUtil {
|
|
104
226
|
/*
|
|
105
227
|
* Builds the topology graph from every device in a project. Nodes are the
|
|
106
|
-
* devices; LLDP and CDP neighbors that match another device (by
|
|
107
|
-
*
|
|
108
|
-
*
|
|
228
|
+
* devices; LLDP and CDP neighbors that match another device (by name,
|
|
229
|
+
* serial, interface MAC or FQDN short form — see MatchKeyKind) become
|
|
230
|
+
* edges, and neighbors that match nothing become lightweight "unmanaged"
|
|
231
|
+
* nodes so links don't dead-end. Reports that share an identity-grade
|
|
232
|
+
* alias are one unmanaged node, however many switches saw it. Edges are
|
|
109
233
|
* undirected and deduplicated — a link reported from both ends, or by
|
|
110
234
|
* both protocols, appears once with the union of what each report knew
|
|
111
235
|
* (ports, protocols, per-end interface state). When `interfaces` rows are
|
|
@@ -120,16 +244,35 @@ export default class NetworkTopologyUtil {
|
|
|
120
244
|
now: Date,
|
|
121
245
|
interfaces: Array<TopologyInterfaceInput> = [],
|
|
122
246
|
endpoints: Array<TopologyEndpointInput> = [],
|
|
247
|
+
manualLinks: Array<TopologyManualLinkInput> = [],
|
|
248
|
+
suppressedNodeKeys: ReadonlySet<string> = new Set<string>(),
|
|
123
249
|
): TopologyBuildResult {
|
|
124
250
|
const nodes: Array<NetworkTopologyNode> = [];
|
|
125
251
|
|
|
126
|
-
|
|
252
|
+
/*
|
|
253
|
+
* Index managed devices by every identifier a neighbor entry could
|
|
254
|
+
* reference. A key TWO devices both claim is deleted rather than
|
|
255
|
+
* resolved: last-writer-wins would quietly draw the link to whichever
|
|
256
|
+
* device happened to be indexed second, and a wrong cable on a network
|
|
257
|
+
* map is worse than a missing one.
|
|
258
|
+
*/
|
|
127
259
|
const deviceByMatchKey: Map<string, TopologyDeviceInput> = new Map();
|
|
260
|
+
const ambiguousMatchKeyIds: Set<string> = new Set<string>();
|
|
128
261
|
for (const device of devices) {
|
|
129
262
|
for (const key of NetworkTopologyUtil.matchKeysForDevice(device)) {
|
|
130
|
-
|
|
263
|
+
const keyId: string = NetworkTopologyUtil.matchKeyId(key);
|
|
264
|
+
const existing: TopologyDeviceInput | undefined =
|
|
265
|
+
deviceByMatchKey.get(keyId);
|
|
266
|
+
if (existing && existing.id !== device.id) {
|
|
267
|
+
ambiguousMatchKeyIds.add(keyId);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
deviceByMatchKey.set(keyId, device);
|
|
131
271
|
}
|
|
132
272
|
}
|
|
273
|
+
for (const keyId of ambiguousMatchKeyIds) {
|
|
274
|
+
deviceByMatchKey.delete(keyId);
|
|
275
|
+
}
|
|
133
276
|
|
|
134
277
|
// Index interface rows by (device, index) and by (device, name).
|
|
135
278
|
const interfaceByDeviceAndIndex: Map<string, TopologyInterfaceInput> =
|
|
@@ -149,7 +292,15 @@ export default class NetworkTopologyUtil {
|
|
|
149
292
|
}
|
|
150
293
|
}
|
|
151
294
|
|
|
295
|
+
// Kept aside so the diagnostics pass below can read it back per node.
|
|
296
|
+
const isNeighborDiscoveryEnabledById: Map<string, boolean | undefined> =
|
|
297
|
+
new Map<string, boolean | undefined>();
|
|
298
|
+
|
|
152
299
|
for (const device of devices) {
|
|
300
|
+
isNeighborDiscoveryEnabledById.set(
|
|
301
|
+
device.id,
|
|
302
|
+
device.isNeighborDiscoveryEnabled,
|
|
303
|
+
);
|
|
153
304
|
nodes.push({
|
|
154
305
|
id: device.id,
|
|
155
306
|
name: device.name,
|
|
@@ -172,171 +323,174 @@ export default class NetworkTopologyUtil {
|
|
|
172
323
|
});
|
|
173
324
|
}
|
|
174
325
|
|
|
175
|
-
const unmanagedNodeById: Map<string, NetworkTopologyNode> = new Map();
|
|
176
326
|
const edgeByKey: Map<string, NetworkTopologyEdge> = new Map();
|
|
177
327
|
|
|
328
|
+
/*
|
|
329
|
+
* Resolve every neighbor report before drawing anything.
|
|
330
|
+
*
|
|
331
|
+
* A managed match is decidable from one report, but the IDENTITY of an
|
|
332
|
+
* unmanaged peer is not: the switch at one end may know its neighbor's
|
|
333
|
+
* sysName while the switch at the other end knows only its chassis MAC,
|
|
334
|
+
* and only the whole set of reports says those two describe one box.
|
|
335
|
+
*/
|
|
336
|
+
const resolvedClaims: Array<ResolvedClaim> = [];
|
|
178
337
|
for (const device of devices) {
|
|
179
338
|
for (const claim of NetworkTopologyUtil.neighborClaimsForDevice(device)) {
|
|
180
|
-
|
|
339
|
+
const candidates: Array<MatchKey> =
|
|
340
|
+
NetworkTopologyUtil.candidateKeysForClaim(claim);
|
|
341
|
+
if (candidates.length === 0) {
|
|
181
342
|
continue;
|
|
182
343
|
}
|
|
183
344
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
345
|
+
// Candidates arrive strongest-first, so the first hit is the best one.
|
|
346
|
+
let matched: TopologyDeviceInput | undefined = undefined;
|
|
347
|
+
for (const candidate of candidates) {
|
|
348
|
+
matched = deviceByMatchKey.get(
|
|
349
|
+
NetworkTopologyUtil.matchKeyId(candidate),
|
|
350
|
+
);
|
|
351
|
+
if (matched) {
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
189
355
|
|
|
190
356
|
if (matched) {
|
|
191
357
|
if (matched.id === device.id) {
|
|
192
358
|
// Self-referential neighbor entry; skip.
|
|
193
359
|
continue;
|
|
194
360
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (!existingUnmanaged) {
|
|
201
|
-
const unmanagedName: string = claim.displayName || "Unknown device";
|
|
202
|
-
const unmanagedNode: NetworkTopologyNode = {
|
|
203
|
-
id: toNodeId,
|
|
204
|
-
name: unmanagedName,
|
|
205
|
-
isManaged: false,
|
|
206
|
-
kind: "unmanaged",
|
|
207
|
-
role: classifyDeviceRole({
|
|
208
|
-
platform: claim.remotePlatform,
|
|
209
|
-
name: unmanagedName,
|
|
210
|
-
}),
|
|
211
|
-
status: "unknown",
|
|
212
|
-
deviceModel: claim.remotePlatform,
|
|
213
|
-
};
|
|
214
|
-
unmanagedNodeById.set(toNodeId, unmanagedNode);
|
|
215
|
-
nodes.push(unmanagedNode);
|
|
216
|
-
} else if (!existingUnmanaged.deviceModel && claim.remotePlatform) {
|
|
217
|
-
/*
|
|
218
|
-
* A later CDP claim may know the platform the first one didn't
|
|
219
|
-
* — which is usually the only evidence a peer's role rests on,
|
|
220
|
-
* so the role is re-derived rather than left at whatever the
|
|
221
|
-
* name alone produced.
|
|
222
|
-
*/
|
|
223
|
-
existingUnmanaged.deviceModel = claim.remotePlatform;
|
|
224
|
-
existingUnmanaged.role = classifyDeviceRole({
|
|
225
|
-
platform: claim.remotePlatform,
|
|
226
|
-
name: existingUnmanaged.name,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// The reporting device's own end, enriched from its interface row.
|
|
232
|
-
let localEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
|
|
233
|
-
let localPortLabel: string | undefined = undefined;
|
|
234
|
-
if (claim.localInterfaceIndex !== undefined) {
|
|
235
|
-
const localInterface: TopologyInterfaceInput | undefined =
|
|
236
|
-
interfaceByDeviceAndIndex.get(
|
|
237
|
-
`${device.id}::${claim.localInterfaceIndex}`,
|
|
238
|
-
);
|
|
239
|
-
localEndpoint = {
|
|
240
|
-
interfaceIndex: claim.localInterfaceIndex,
|
|
241
|
-
interfaceName: localInterface?.name,
|
|
242
|
-
isOperationallyUp: localInterface?.isOperationallyUp,
|
|
243
|
-
isAdministrativelyUp: localInterface?.isAdministrativelyUp,
|
|
244
|
-
utilizationPercent: localInterface?.utilizationPercent,
|
|
245
|
-
inRateMbps: localInterface?.inRateMbps,
|
|
246
|
-
outRateMbps: localInterface?.outRateMbps,
|
|
247
|
-
errorsPerSecond: localInterface?.errorsPerSecond,
|
|
248
|
-
};
|
|
249
|
-
localPortLabel =
|
|
250
|
-
localInterface?.name || `if${claim.localInterfaceIndex}`;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/*
|
|
254
|
-
* The far end: best-effort — the advertised remote port id often
|
|
255
|
-
* matches the remote device's interface name (e.g. "GigabitEthernet
|
|
256
|
-
* 0/1"). Only attach data when it does; a bare port id string stays
|
|
257
|
-
* in toPort.
|
|
258
|
-
*/
|
|
259
|
-
let remoteEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
|
|
260
|
-
if (matched && claim.remotePortId) {
|
|
261
|
-
const remotePortKey: string | undefined =
|
|
262
|
-
NetworkTopologyUtil.normalizeKey(claim.remotePortId);
|
|
263
|
-
const remoteInterface: TopologyInterfaceInput | undefined =
|
|
264
|
-
remotePortKey
|
|
265
|
-
? interfaceByDeviceAndName.get(`${matched.id}::${remotePortKey}`)
|
|
266
|
-
: undefined;
|
|
267
|
-
if (remoteInterface) {
|
|
268
|
-
remoteEndpoint = {
|
|
269
|
-
interfaceIndex: remoteInterface.interfaceIndex,
|
|
270
|
-
interfaceName: remoteInterface.name,
|
|
271
|
-
isOperationallyUp: remoteInterface.isOperationallyUp,
|
|
272
|
-
isAdministrativelyUp: remoteInterface.isAdministrativelyUp,
|
|
273
|
-
utilizationPercent: remoteInterface.utilizationPercent,
|
|
274
|
-
inRateMbps: remoteInterface.inRateMbps,
|
|
275
|
-
outRateMbps: remoteInterface.outRateMbps,
|
|
276
|
-
errorsPerSecond: remoteInterface.errorsPerSecond,
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Undirected dedupe: canonicalize the endpoint pair.
|
|
282
|
-
const edgeKey: string = [device.id, toNodeId].sort().join("::");
|
|
283
|
-
const existing: NetworkTopologyEdge | undefined =
|
|
284
|
-
edgeByKey.get(edgeKey);
|
|
285
|
-
|
|
286
|
-
if (!existing) {
|
|
287
|
-
edgeByKey.set(edgeKey, {
|
|
288
|
-
fromNodeId: device.id,
|
|
289
|
-
toNodeId: toNodeId,
|
|
290
|
-
fromPort: localPortLabel,
|
|
291
|
-
toPort: claim.remotePortId,
|
|
292
|
-
protocols: [claim.protocol],
|
|
293
|
-
fromInterface: localEndpoint,
|
|
294
|
-
toInterface: remoteEndpoint,
|
|
361
|
+
resolvedClaims.push({
|
|
362
|
+
device: device,
|
|
363
|
+
claim: claim,
|
|
364
|
+
matchedDeviceId: matched.id,
|
|
365
|
+
peerAliases: [],
|
|
295
366
|
});
|
|
296
367
|
continue;
|
|
297
368
|
}
|
|
298
369
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
370
|
+
resolvedClaims.push({
|
|
371
|
+
device: device,
|
|
372
|
+
claim: claim,
|
|
373
|
+
matchedDeviceId: undefined,
|
|
374
|
+
peerAliases: candidates.filter((candidate: MatchKey) => {
|
|
375
|
+
return PEER_IDENTITY_KINDS.has(candidate.kind);
|
|
376
|
+
}),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
310
380
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
remoteEndpoint,
|
|
381
|
+
const peerNodeIdByClaim: Map<ResolvedClaim, string> =
|
|
382
|
+
NetworkTopologyUtil.buildUnmanagedPeerNodes(resolvedClaims, nodes);
|
|
383
|
+
|
|
384
|
+
for (const resolved of resolvedClaims) {
|
|
385
|
+
const device: TopologyDeviceInput = resolved.device;
|
|
386
|
+
const claim: NeighborClaim = resolved.claim;
|
|
387
|
+
const matchedId: string | undefined = resolved.matchedDeviceId;
|
|
388
|
+
const toNodeId: string | undefined =
|
|
389
|
+
matchedId || peerNodeIdByClaim.get(resolved);
|
|
390
|
+
if (!toNodeId) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// The reporting device's own end, enriched from its interface row.
|
|
395
|
+
let localEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
|
|
396
|
+
let localPortLabel: string | undefined = undefined;
|
|
397
|
+
if (claim.localInterfaceIndex !== undefined) {
|
|
398
|
+
const localInterface: TopologyInterfaceInput | undefined =
|
|
399
|
+
interfaceByDeviceAndIndex.get(
|
|
400
|
+
`${device.id}::${claim.localInterfaceIndex}`,
|
|
332
401
|
);
|
|
402
|
+
localEndpoint = {
|
|
403
|
+
interfaceIndex: claim.localInterfaceIndex,
|
|
404
|
+
interfaceName: localInterface?.name,
|
|
405
|
+
isOperationallyUp: localInterface?.isOperationallyUp,
|
|
406
|
+
isAdministrativelyUp: localInterface?.isAdministrativelyUp,
|
|
407
|
+
utilizationPercent: localInterface?.utilizationPercent,
|
|
408
|
+
inRateMbps: localInterface?.inRateMbps,
|
|
409
|
+
outRateMbps: localInterface?.outRateMbps,
|
|
410
|
+
errorsPerSecond: localInterface?.errorsPerSecond,
|
|
411
|
+
};
|
|
412
|
+
localPortLabel =
|
|
413
|
+
localInterface?.name || `if${claim.localInterfaceIndex}`;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/*
|
|
417
|
+
* The far end: best-effort — the advertised remote port id often
|
|
418
|
+
* matches the remote device's interface name (e.g. "GigabitEthernet
|
|
419
|
+
* 0/1"). Only attach data when it does; a bare port id string stays
|
|
420
|
+
* in toPort.
|
|
421
|
+
*/
|
|
422
|
+
let remoteEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
|
|
423
|
+
if (matchedId && claim.remotePortId) {
|
|
424
|
+
const remotePortKey: string | undefined =
|
|
425
|
+
NetworkTopologyUtil.normalizeKey(claim.remotePortId);
|
|
426
|
+
const remoteInterface: TopologyInterfaceInput | undefined =
|
|
427
|
+
remotePortKey
|
|
428
|
+
? interfaceByDeviceAndName.get(`${matchedId}::${remotePortKey}`)
|
|
429
|
+
: undefined;
|
|
430
|
+
if (remoteInterface) {
|
|
431
|
+
remoteEndpoint = {
|
|
432
|
+
interfaceIndex: remoteInterface.interfaceIndex,
|
|
433
|
+
interfaceName: remoteInterface.name,
|
|
434
|
+
isOperationallyUp: remoteInterface.isOperationallyUp,
|
|
435
|
+
isAdministrativelyUp: remoteInterface.isAdministrativelyUp,
|
|
436
|
+
utilizationPercent: remoteInterface.utilizationPercent,
|
|
437
|
+
inRateMbps: remoteInterface.inRateMbps,
|
|
438
|
+
outRateMbps: remoteInterface.outRateMbps,
|
|
439
|
+
errorsPerSecond: remoteInterface.errorsPerSecond,
|
|
440
|
+
};
|
|
333
441
|
}
|
|
334
442
|
}
|
|
335
|
-
}
|
|
336
443
|
|
|
337
|
-
|
|
444
|
+
// Undirected dedupe: canonicalize the endpoint pair.
|
|
445
|
+
const edgeKey: string = [device.id, toNodeId].sort().join("::");
|
|
446
|
+
const existing: NetworkTopologyEdge | undefined = edgeByKey.get(edgeKey);
|
|
338
447
|
|
|
339
|
-
|
|
448
|
+
if (!existing) {
|
|
449
|
+
edgeByKey.set(edgeKey, {
|
|
450
|
+
fromNodeId: device.id,
|
|
451
|
+
toNodeId: toNodeId,
|
|
452
|
+
fromPort: localPortLabel,
|
|
453
|
+
toPort: claim.remotePortId,
|
|
454
|
+
protocols: [claim.protocol],
|
|
455
|
+
fromInterface: localEndpoint,
|
|
456
|
+
toInterface: remoteEndpoint,
|
|
457
|
+
});
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/*
|
|
462
|
+
* Same link reported again — from the other end, or by the other
|
|
463
|
+
* protocol. Merge instead of dropping so both ends keep the best
|
|
464
|
+
* data anyone reported about them.
|
|
465
|
+
*/
|
|
466
|
+
if (existing.protocols && !existing.protocols.includes(claim.protocol)) {
|
|
467
|
+
existing.protocols.push(claim.protocol);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (existing.fromNodeId === device.id) {
|
|
471
|
+
existing.fromPort = existing.fromPort || localPortLabel;
|
|
472
|
+
existing.toPort = existing.toPort || claim.remotePortId;
|
|
473
|
+
existing.fromInterface = NetworkTopologyUtil.mergeEndpoints(
|
|
474
|
+
existing.fromInterface,
|
|
475
|
+
localEndpoint,
|
|
476
|
+
);
|
|
477
|
+
existing.toInterface = NetworkTopologyUtil.mergeEndpoints(
|
|
478
|
+
existing.toInterface,
|
|
479
|
+
remoteEndpoint,
|
|
480
|
+
);
|
|
481
|
+
} else {
|
|
482
|
+
existing.toPort = existing.toPort || localPortLabel;
|
|
483
|
+
existing.fromPort = existing.fromPort || claim.remotePortId;
|
|
484
|
+
existing.toInterface = NetworkTopologyUtil.mergeEndpoints(
|
|
485
|
+
existing.toInterface,
|
|
486
|
+
localEndpoint,
|
|
487
|
+
);
|
|
488
|
+
existing.fromInterface = NetworkTopologyUtil.mergeEndpoints(
|
|
489
|
+
existing.fromInterface,
|
|
490
|
+
remoteEndpoint,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
340
494
|
|
|
341
495
|
const deviceNodeIds: Set<string> = new Set(
|
|
342
496
|
devices.map((device: TopologyDeviceInput) => {
|
|
@@ -344,6 +498,129 @@ export default class NetworkTopologyUtil {
|
|
|
344
498
|
}),
|
|
345
499
|
);
|
|
346
500
|
|
|
501
|
+
/*
|
|
502
|
+
* --- Operator-declared links ---
|
|
503
|
+
*
|
|
504
|
+
* Merged into the SAME edge map the discovery protocols write to, so a
|
|
505
|
+
* cable somebody drew by hand and the same cable LLDP later reports are
|
|
506
|
+
* one line carrying both protocols — not two lines between one pair of
|
|
507
|
+
* boxes, which is exactly the duplicate an operator would read as a
|
|
508
|
+
* second physical link.
|
|
509
|
+
*
|
|
510
|
+
* Ends outside this graph are skipped rather than drawn: a link to a
|
|
511
|
+
* device in another site (or one since archived) has nothing to attach
|
|
512
|
+
* to here.
|
|
513
|
+
*/
|
|
514
|
+
for (const link of manualLinks) {
|
|
515
|
+
if (
|
|
516
|
+
!deviceNodeIds.has(link.fromDeviceId) ||
|
|
517
|
+
!deviceNodeIds.has(link.toDeviceId) ||
|
|
518
|
+
link.fromDeviceId === link.toDeviceId
|
|
519
|
+
) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const edgeKey: string = [link.fromDeviceId, link.toDeviceId]
|
|
524
|
+
.sort()
|
|
525
|
+
.join("::");
|
|
526
|
+
const existing: NetworkTopologyEdge | undefined = edgeByKey.get(edgeKey);
|
|
527
|
+
|
|
528
|
+
if (!existing) {
|
|
529
|
+
edgeByKey.set(edgeKey, {
|
|
530
|
+
fromNodeId: link.fromDeviceId,
|
|
531
|
+
toNodeId: link.toDeviceId,
|
|
532
|
+
fromPort: link.fromPortName,
|
|
533
|
+
toPort: link.toPortName,
|
|
534
|
+
protocols: ["manual"],
|
|
535
|
+
monitorState: link.monitorStatus,
|
|
536
|
+
name: link.name,
|
|
537
|
+
});
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (existing.protocols && !existing.protocols.includes("manual")) {
|
|
542
|
+
existing.protocols.push("manual");
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/*
|
|
546
|
+
* Discovered data wins on the shared fields — it is measured, and the
|
|
547
|
+
* hand-typed port is a best recollection. The operator's name and the
|
|
548
|
+
* monitor's verdict have no discovered counterpart, so they always
|
|
549
|
+
* apply.
|
|
550
|
+
*/
|
|
551
|
+
if (existing.fromNodeId === link.fromDeviceId) {
|
|
552
|
+
existing.fromPort = existing.fromPort || link.fromPortName;
|
|
553
|
+
existing.toPort = existing.toPort || link.toPortName;
|
|
554
|
+
} else {
|
|
555
|
+
existing.toPort = existing.toPort || link.fromPortName;
|
|
556
|
+
existing.fromPort = existing.fromPort || link.toPortName;
|
|
557
|
+
}
|
|
558
|
+
existing.name = existing.name || link.name;
|
|
559
|
+
existing.monitorState = existing.monitorState ?? link.monitorStatus;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const edges: Array<NetworkTopologyEdge> = Array.from(edgeByKey.values());
|
|
563
|
+
|
|
564
|
+
/*
|
|
565
|
+
* --- Why a device has no links ---
|
|
566
|
+
*
|
|
567
|
+
* Attached to every managed node, so the detail panel can answer "the
|
|
568
|
+
* router is not linked to anything" with a cause instead of a shrug.
|
|
569
|
+
* Built from the claims that were just resolved: the unmatched ones are
|
|
570
|
+
* the actionable evidence — a renamed peer, a device nobody has added
|
|
571
|
+
* yet — and they are otherwise thrown away here.
|
|
572
|
+
*/
|
|
573
|
+
const reportedNeighborCountByDeviceId: Map<string, number> = new Map<
|
|
574
|
+
string,
|
|
575
|
+
number
|
|
576
|
+
>();
|
|
577
|
+
const unmatchedIdentifiersByDeviceId: Map<string, Array<string>> = new Map<
|
|
578
|
+
string,
|
|
579
|
+
Array<string>
|
|
580
|
+
>();
|
|
581
|
+
|
|
582
|
+
for (const resolved of resolvedClaims) {
|
|
583
|
+
const deviceId: string = resolved.device.id;
|
|
584
|
+
reportedNeighborCountByDeviceId.set(
|
|
585
|
+
deviceId,
|
|
586
|
+
(reportedNeighborCountByDeviceId.get(deviceId) || 0) + 1,
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
if (resolved.matchedDeviceId) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const identifier: string | undefined =
|
|
594
|
+
resolved.claim.displayName || resolved.claim.identifiers[0];
|
|
595
|
+
if (!identifier) {
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const bucket: Array<string> | undefined =
|
|
599
|
+
unmatchedIdentifiersByDeviceId.get(deviceId);
|
|
600
|
+
if (bucket) {
|
|
601
|
+
if (!bucket.includes(identifier)) {
|
|
602
|
+
bucket.push(identifier);
|
|
603
|
+
}
|
|
604
|
+
} else {
|
|
605
|
+
unmatchedIdentifiersByDeviceId.set(deviceId, [identifier]);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
for (const node of nodes) {
|
|
610
|
+
if (node.kind !== "device") {
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
node.diagnostics = {
|
|
614
|
+
isNeighborDiscoveryEnabled: isNeighborDiscoveryEnabledById.get(node.id),
|
|
615
|
+
reportedNeighborCount:
|
|
616
|
+
reportedNeighborCountByDeviceId.get(node.id) || 0,
|
|
617
|
+
unmatchedNeighborIdentifiers:
|
|
618
|
+
unmatchedIdentifiersByDeviceId.get(node.id) || [],
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// --- Endpoint nodes + fdb edges ---
|
|
623
|
+
|
|
347
624
|
/*
|
|
348
625
|
* Deterministic render order: by normalized MAC, then id, so the capped
|
|
349
626
|
* slice is stable across rebuilds regardless of query order.
|
|
@@ -439,11 +716,47 @@ export default class NetworkTopologyUtil {
|
|
|
439
716
|
});
|
|
440
717
|
}
|
|
441
718
|
|
|
719
|
+
/*
|
|
720
|
+
* --- Hidden nodes ---
|
|
721
|
+
*
|
|
722
|
+
* Applied last, on the finished graph, so a suppressed node cannot
|
|
723
|
+
* change how anything else resolved. Hiding a switch must not silently
|
|
724
|
+
* re-route its neighbours' links or turn its peers into strangers — it
|
|
725
|
+
* takes the box off the map and nothing more. Its edges go with it,
|
|
726
|
+
* because a line to a node that is not drawn is a line into empty space.
|
|
727
|
+
*/
|
|
728
|
+
if (suppressedNodeKeys.size === 0) {
|
|
729
|
+
return {
|
|
730
|
+
nodes,
|
|
731
|
+
edges,
|
|
732
|
+
droppedEndpointCount,
|
|
733
|
+
endpointsTruncated,
|
|
734
|
+
suppressedNodeCount: 0,
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const visibleNodes: Array<NetworkTopologyNode> = nodes.filter(
|
|
739
|
+
(node: NetworkTopologyNode) => {
|
|
740
|
+
return !suppressedNodeKeys.has(node.id);
|
|
741
|
+
},
|
|
742
|
+
);
|
|
743
|
+
const visibleNodeIds: Set<string> = new Set<string>(
|
|
744
|
+
visibleNodes.map((node: NetworkTopologyNode) => {
|
|
745
|
+
return node.id;
|
|
746
|
+
}),
|
|
747
|
+
);
|
|
748
|
+
|
|
442
749
|
return {
|
|
443
|
-
nodes,
|
|
444
|
-
edges
|
|
750
|
+
nodes: visibleNodes,
|
|
751
|
+
edges: edges.filter((edge: NetworkTopologyEdge) => {
|
|
752
|
+
return (
|
|
753
|
+
visibleNodeIds.has(edge.fromNodeId) &&
|
|
754
|
+
visibleNodeIds.has(edge.toNodeId)
|
|
755
|
+
);
|
|
756
|
+
}),
|
|
445
757
|
droppedEndpointCount,
|
|
446
758
|
endpointsTruncated,
|
|
759
|
+
suppressedNodeCount: nodes.length - visibleNodes.length,
|
|
447
760
|
};
|
|
448
761
|
}
|
|
449
762
|
|
|
@@ -458,12 +771,26 @@ export default class NetworkTopologyUtil {
|
|
|
458
771
|
const claims: Array<NeighborClaim> = [];
|
|
459
772
|
|
|
460
773
|
for (const neighbor of device.lldpNeighbors || []) {
|
|
774
|
+
/*
|
|
775
|
+
* Both identifiers are carried, not just the first non-empty one.
|
|
776
|
+
* The chassis id used to be a fallback that only applied when the
|
|
777
|
+
* peer advertised no sysName; it is in fact an identifier in its own
|
|
778
|
+
* right — a MAC or a serial — and the device it names is very often
|
|
779
|
+
* one we already manage.
|
|
780
|
+
*/
|
|
781
|
+
const identifiers: Array<string> = [];
|
|
782
|
+
if (neighbor.remoteSysName) {
|
|
783
|
+
identifiers.push(neighbor.remoteSysName);
|
|
784
|
+
}
|
|
785
|
+
if (neighbor.remoteChassisId) {
|
|
786
|
+
identifiers.push(neighbor.remoteChassisId);
|
|
787
|
+
}
|
|
788
|
+
|
|
461
789
|
claims.push({
|
|
462
790
|
protocol: "lldp",
|
|
463
|
-
|
|
464
|
-
NetworkTopologyUtil.normalizeKey(neighbor.remoteSysName) ||
|
|
465
|
-
NetworkTopologyUtil.normalizeKey(neighbor.remoteChassisId),
|
|
791
|
+
identifiers: identifiers,
|
|
466
792
|
displayName: neighbor.remoteSysName || neighbor.remoteChassisId,
|
|
793
|
+
hasAdvertisedName: Boolean(neighbor.remoteSysName),
|
|
467
794
|
localInterfaceIndex: neighbor.localInterfaceIndex,
|
|
468
795
|
remotePortId: neighbor.remotePortId,
|
|
469
796
|
remotePlatform: undefined,
|
|
@@ -473,8 +800,9 @@ export default class NetworkTopologyUtil {
|
|
|
473
800
|
for (const neighbor of device.cdpNeighbors || []) {
|
|
474
801
|
claims.push({
|
|
475
802
|
protocol: "cdp",
|
|
476
|
-
|
|
803
|
+
identifiers: neighbor.remoteDeviceId ? [neighbor.remoteDeviceId] : [],
|
|
477
804
|
displayName: neighbor.remoteDeviceId,
|
|
805
|
+
hasAdvertisedName: Boolean(neighbor.remoteDeviceId),
|
|
478
806
|
localInterfaceIndex: neighbor.localInterfaceIndex,
|
|
479
807
|
remotePortId: neighbor.remotePortId,
|
|
480
808
|
remotePlatform: neighbor.remotePlatform,
|
|
@@ -484,6 +812,102 @@ export default class NetworkTopologyUtil {
|
|
|
484
812
|
return claims;
|
|
485
813
|
}
|
|
486
814
|
|
|
815
|
+
/*
|
|
816
|
+
* Every key a claim could be resolved by, strongest kind first and, within
|
|
817
|
+
* a kind, strongest SOURCE first (an advertised sysName ahead of a chassis
|
|
818
|
+
* id). Sorting by kind before source is deliberate: a MAC read off a
|
|
819
|
+
* chassis id identifies a box far more precisely than the first label of
|
|
820
|
+
* somebody's FQDN, whichever field it arrived in.
|
|
821
|
+
*/
|
|
822
|
+
private static candidateKeysForClaim(claim: NeighborClaim): Array<MatchKey> {
|
|
823
|
+
const byKind: Map<MatchKeyKind, Array<MatchKey>> = new Map<
|
|
824
|
+
MatchKeyKind,
|
|
825
|
+
Array<MatchKey>
|
|
826
|
+
>();
|
|
827
|
+
const seen: Set<string> = new Set<string>();
|
|
828
|
+
|
|
829
|
+
for (const identifier of claim.identifiers) {
|
|
830
|
+
for (const key of NetworkTopologyUtil.candidateKeysForIdentifier(
|
|
831
|
+
identifier,
|
|
832
|
+
)) {
|
|
833
|
+
const keyId: string = NetworkTopologyUtil.matchKeyId(key);
|
|
834
|
+
if (seen.has(keyId)) {
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
seen.add(keyId);
|
|
838
|
+
const bucket: Array<MatchKey> | undefined = byKind.get(key.kind);
|
|
839
|
+
if (bucket) {
|
|
840
|
+
bucket.push(key);
|
|
841
|
+
} else {
|
|
842
|
+
byKind.set(key.kind, [key]);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const ordered: Array<MatchKey> = [];
|
|
848
|
+
for (const kind of MATCH_KEY_KINDS_STRONGEST_FIRST) {
|
|
849
|
+
ordered.push(...(byKind.get(kind) || []));
|
|
850
|
+
}
|
|
851
|
+
return ordered;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/*
|
|
855
|
+
* One identifier string, offered as every kind it could be.
|
|
856
|
+
*
|
|
857
|
+
* A chassis id is just bytes on the wire — nothing in the protocol says
|
|
858
|
+
* whether it is a name, a serial or a MAC — so it is offered as all of
|
|
859
|
+
* them, and only a key some device actually published can match.
|
|
860
|
+
*/
|
|
861
|
+
private static candidateKeysForIdentifier(
|
|
862
|
+
raw: string | undefined,
|
|
863
|
+
): Array<MatchKey> {
|
|
864
|
+
const normalized: string | undefined =
|
|
865
|
+
NetworkTopologyUtil.normalizeKey(raw);
|
|
866
|
+
if (!normalized) {
|
|
867
|
+
return [];
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const keys: Array<MatchKey> = [
|
|
871
|
+
{ kind: "name", value: normalized },
|
|
872
|
+
{ kind: "serial", value: normalized },
|
|
873
|
+
];
|
|
874
|
+
|
|
875
|
+
const mac: string | undefined = normalizeMac(normalized);
|
|
876
|
+
if (mac) {
|
|
877
|
+
keys.push({ kind: "mac", value: mac });
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const shortHost: string | undefined =
|
|
881
|
+
NetworkTopologyUtil.shortHostKey(normalized);
|
|
882
|
+
if (shortHost) {
|
|
883
|
+
keys.push({ kind: "shortHost", value: shortHost });
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
return keys;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/*
|
|
890
|
+
* The first label of a hostname, or undefined when taking one would be
|
|
891
|
+
* nonsense. An IPv4 literal would shorten to "10" and a MAC to its first
|
|
892
|
+
* octet pair — both of which would collide half the estate into one node,
|
|
893
|
+
* so both are refused outright.
|
|
894
|
+
*/
|
|
895
|
+
private static shortHostKey(normalized: string): string | undefined {
|
|
896
|
+
if (IPV4_PATTERN.test(normalized) || normalizeMac(normalized)) {
|
|
897
|
+
return undefined;
|
|
898
|
+
}
|
|
899
|
+
const firstLabel: string = normalized.split(".")[0] || "";
|
|
900
|
+
return firstLabel.length > 0 ? firstLabel : undefined;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
private static matchKeyId(key: MatchKey): string {
|
|
904
|
+
/*
|
|
905
|
+
* A NUL separator, because every kind shares one namespace and a
|
|
906
|
+
* hostname may legitimately contain a colon-ish character.
|
|
907
|
+
*/
|
|
908
|
+
return `${key.kind}\u0000${key.value}`;
|
|
909
|
+
}
|
|
910
|
+
|
|
487
911
|
// Existing fields win; the incoming report only fills what's missing.
|
|
488
912
|
private static mergeEndpoints(
|
|
489
913
|
existing: NetworkTopologyEdgeEndpoint | undefined,
|
|
@@ -510,25 +934,316 @@ export default class NetworkTopologyUtil {
|
|
|
510
934
|
};
|
|
511
935
|
}
|
|
512
936
|
|
|
937
|
+
/*
|
|
938
|
+
* Every key this device answers to.
|
|
939
|
+
*
|
|
940
|
+
* Unlike a claim's candidates, these are NOT speculative: we know which
|
|
941
|
+
* column each value came from, so a serial is only ever registered as a
|
|
942
|
+
* serial. That asymmetry is the whole point — a claim may offer its
|
|
943
|
+
* chassis id as a name, a serial and a MAC, but only the kind a device
|
|
944
|
+
* actually published can be the one that matches.
|
|
945
|
+
*/
|
|
513
946
|
private static matchKeysForDevice(
|
|
514
947
|
device: TopologyDeviceInput,
|
|
515
|
-
): Array<
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
948
|
+
): Array<MatchKey> {
|
|
949
|
+
const keys: Array<MatchKey> = [];
|
|
950
|
+
const seen: Set<string> = new Set<string>();
|
|
951
|
+
|
|
952
|
+
const add: (key: MatchKey) => void = (key: MatchKey): void => {
|
|
953
|
+
const keyId: string = NetworkTopologyUtil.matchKeyId(key);
|
|
954
|
+
if (seen.has(keyId)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
seen.add(keyId);
|
|
958
|
+
keys.push(key);
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
// sysName / hostname / name — what both protocols advertise.
|
|
522
962
|
for (const candidate of [device.sysName, device.hostname, device.name]) {
|
|
523
|
-
const
|
|
963
|
+
const normalized: string | undefined =
|
|
524
964
|
NetworkTopologyUtil.normalizeKey(candidate);
|
|
525
|
-
if (
|
|
526
|
-
|
|
965
|
+
if (!normalized) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
add({ kind: "name", value: normalized });
|
|
969
|
+
/*
|
|
970
|
+
* The short form is registered for EVERY name, dotted or not, so the
|
|
971
|
+
* two halves of the comparison meet: an FQDN on the wire has to be
|
|
972
|
+
* able to find a bare sysName here, and a bare name on the wire has
|
|
973
|
+
* to be able to find an FQDN.
|
|
974
|
+
*/
|
|
975
|
+
const shortHost: string | undefined =
|
|
976
|
+
NetworkTopologyUtil.shortHostKey(normalized);
|
|
977
|
+
if (shortHost) {
|
|
978
|
+
add({ kind: "shortHost", value: shortHost });
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const serial: string | undefined = NetworkTopologyUtil.normalizeKey(
|
|
983
|
+
device.serialNumber,
|
|
984
|
+
);
|
|
985
|
+
if (serial) {
|
|
986
|
+
add({ kind: "serial", value: serial });
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
for (const rawMac of device.macAddresses || []) {
|
|
990
|
+
const mac: string | undefined = normalizeMac(rawMac);
|
|
991
|
+
if (mac) {
|
|
992
|
+
add({ kind: "mac", value: mac });
|
|
527
993
|
}
|
|
528
994
|
}
|
|
995
|
+
|
|
529
996
|
return keys;
|
|
530
997
|
}
|
|
531
998
|
|
|
999
|
+
/*
|
|
1000
|
+
* Turns the unmatched claims into unmanaged peer NODES, one per box.
|
|
1001
|
+
*
|
|
1002
|
+
* Reports are merged when they share any identity-grade alias, so the
|
|
1003
|
+
* neighbor one switch knows as "ap-lobby" and another knows only by its
|
|
1004
|
+
* chassis MAC lands on a single node instead of the two strangers the map
|
|
1005
|
+
* used to draw. Appends the nodes it mints to `nodes` and returns the node
|
|
1006
|
+
* id each claim should draw its edge to.
|
|
1007
|
+
*/
|
|
1008
|
+
private static buildUnmanagedPeerNodes(
|
|
1009
|
+
resolvedClaims: Array<ResolvedClaim>,
|
|
1010
|
+
nodes: Array<NetworkTopologyNode>,
|
|
1011
|
+
): Map<ResolvedClaim, string> {
|
|
1012
|
+
const unmanagedClaims: Array<ResolvedClaim> = resolvedClaims.filter(
|
|
1013
|
+
(resolved: ResolvedClaim) => {
|
|
1014
|
+
return !resolved.matchedDeviceId && resolved.peerAliases.length > 0;
|
|
1015
|
+
},
|
|
1016
|
+
);
|
|
1017
|
+
|
|
1018
|
+
const rootByAliasId: Map<string, string> =
|
|
1019
|
+
NetworkTopologyUtil.groupPeerAliases(unmanagedClaims);
|
|
1020
|
+
|
|
1021
|
+
// Claims per peer, in first-encounter order so the output is stable.
|
|
1022
|
+
const claimsByRoot: Map<string, Array<ResolvedClaim>> = new Map<
|
|
1023
|
+
string,
|
|
1024
|
+
Array<ResolvedClaim>
|
|
1025
|
+
>();
|
|
1026
|
+
for (const resolved of unmanagedClaims) {
|
|
1027
|
+
const root: string | undefined = rootByAliasId.get(
|
|
1028
|
+
NetworkTopologyUtil.matchKeyId(resolved.peerAliases[0]!),
|
|
1029
|
+
);
|
|
1030
|
+
if (!root) {
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
const bucket: Array<ResolvedClaim> | undefined = claimsByRoot.get(root);
|
|
1034
|
+
if (bucket) {
|
|
1035
|
+
bucket.push(resolved);
|
|
1036
|
+
} else {
|
|
1037
|
+
claimsByRoot.set(root, [resolved]);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const nodeIdByClaim: Map<ResolvedClaim, string> = new Map<
|
|
1042
|
+
ResolvedClaim,
|
|
1043
|
+
string
|
|
1044
|
+
>();
|
|
1045
|
+
const usedNodeIds: Set<string> = new Set<string>();
|
|
1046
|
+
|
|
1047
|
+
for (const groupClaims of claimsByRoot.values()) {
|
|
1048
|
+
/*
|
|
1049
|
+
* The nicest label anyone reported: a name the peer advertises about
|
|
1050
|
+
* itself beats a bare chassis id, whichever report arrived first.
|
|
1051
|
+
*/
|
|
1052
|
+
let displayName: string | undefined = undefined;
|
|
1053
|
+
let platform: string | undefined = undefined;
|
|
1054
|
+
for (const resolved of groupClaims) {
|
|
1055
|
+
if (!displayName && resolved.claim.hasAdvertisedName) {
|
|
1056
|
+
displayName = resolved.claim.displayName;
|
|
1057
|
+
}
|
|
1058
|
+
if (!platform && resolved.claim.remotePlatform) {
|
|
1059
|
+
platform = resolved.claim.remotePlatform;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (!displayName) {
|
|
1063
|
+
for (const resolved of groupClaims) {
|
|
1064
|
+
if (resolved.claim.displayName) {
|
|
1065
|
+
displayName = resolved.claim.displayName;
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/*
|
|
1072
|
+
* The id keeps its historic `unmanaged:<value>` shape — it is the key
|
|
1073
|
+
* saved arrangements are stored under, so changing it would scatter
|
|
1074
|
+
* every pinned peer on the map. It is named after the label wherever
|
|
1075
|
+
* that label is one of the peer's own aliases, so merging a report
|
|
1076
|
+
* that knew a sysName with one that knew only a chassis MAC still
|
|
1077
|
+
* produces `unmanaged:ap-lobby` rather than an id made of hex.
|
|
1078
|
+
*/
|
|
1079
|
+
const nodeId: string = NetworkTopologyUtil.uniqueNodeId(
|
|
1080
|
+
`unmanaged:${NetworkTopologyUtil.canonicalPeerValue(
|
|
1081
|
+
groupClaims,
|
|
1082
|
+
displayName,
|
|
1083
|
+
)}`,
|
|
1084
|
+
usedNodeIds,
|
|
1085
|
+
);
|
|
1086
|
+
usedNodeIds.add(nodeId);
|
|
1087
|
+
|
|
1088
|
+
const unmanagedName: string = displayName || "Unknown device";
|
|
1089
|
+
nodes.push({
|
|
1090
|
+
id: nodeId,
|
|
1091
|
+
name: unmanagedName,
|
|
1092
|
+
isManaged: false,
|
|
1093
|
+
kind: "unmanaged",
|
|
1094
|
+
/*
|
|
1095
|
+
* Classified from the whole group rather than from whichever
|
|
1096
|
+
* report happened to be first: the platform string is usually the
|
|
1097
|
+
* only real evidence about a peer, and it may well arrive on the
|
|
1098
|
+
* second report of it.
|
|
1099
|
+
*/
|
|
1100
|
+
role: classifyDeviceRole({
|
|
1101
|
+
platform: platform,
|
|
1102
|
+
name: unmanagedName,
|
|
1103
|
+
}),
|
|
1104
|
+
status: "unknown",
|
|
1105
|
+
deviceModel: platform,
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
for (const resolved of groupClaims) {
|
|
1109
|
+
nodeIdByClaim.set(resolved, nodeId);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return nodeIdByClaim;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/*
|
|
1117
|
+
* The alias a merged peer is named after.
|
|
1118
|
+
*
|
|
1119
|
+
* The chosen label wins whenever it is genuinely one of the peer's own
|
|
1120
|
+
* aliases; otherwise the strongest kind present decides, and within it
|
|
1121
|
+
* the lexicographically smallest value, so the id never depends on which
|
|
1122
|
+
* switch happened to be polled first.
|
|
1123
|
+
*/
|
|
1124
|
+
private static canonicalPeerValue(
|
|
1125
|
+
groupClaims: Array<ResolvedClaim>,
|
|
1126
|
+
displayName: string | undefined,
|
|
1127
|
+
): string {
|
|
1128
|
+
const valuesByKind: Map<MatchKeyKind, Array<string>> = new Map<
|
|
1129
|
+
MatchKeyKind,
|
|
1130
|
+
Array<string>
|
|
1131
|
+
>();
|
|
1132
|
+
for (const resolved of groupClaims) {
|
|
1133
|
+
for (const alias of resolved.peerAliases) {
|
|
1134
|
+
const bucket: Array<string> | undefined = valuesByKind.get(alias.kind);
|
|
1135
|
+
if (bucket) {
|
|
1136
|
+
if (!bucket.includes(alias.value)) {
|
|
1137
|
+
bucket.push(alias.value);
|
|
1138
|
+
}
|
|
1139
|
+
} else {
|
|
1140
|
+
valuesByKind.set(alias.kind, [alias.value]);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
const preferred: string | undefined =
|
|
1146
|
+
NetworkTopologyUtil.normalizeKey(displayName);
|
|
1147
|
+
if (preferred && (valuesByKind.get("name") || []).includes(preferred)) {
|
|
1148
|
+
return preferred;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
for (const kind of MATCH_KEY_KINDS_STRONGEST_FIRST) {
|
|
1152
|
+
const values: Array<string> | undefined = valuesByKind.get(kind);
|
|
1153
|
+
if (values && values.length > 0) {
|
|
1154
|
+
return [...values].sort()[0]!;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return "unknown-peer";
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/*
|
|
1162
|
+
* Two peers could in principle canonicalize to the same string (one by
|
|
1163
|
+
* name, one by serial). Suffixing rather than merging keeps them the
|
|
1164
|
+
* distinct boxes they are.
|
|
1165
|
+
*/
|
|
1166
|
+
private static uniqueNodeId(
|
|
1167
|
+
preferred: string,
|
|
1168
|
+
used: ReadonlySet<string>,
|
|
1169
|
+
): string {
|
|
1170
|
+
if (!used.has(preferred)) {
|
|
1171
|
+
return preferred;
|
|
1172
|
+
}
|
|
1173
|
+
let suffix: number = 2;
|
|
1174
|
+
while (used.has(`${preferred}#${suffix}`)) {
|
|
1175
|
+
suffix++;
|
|
1176
|
+
}
|
|
1177
|
+
return `${preferred}#${suffix}`;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/*
|
|
1181
|
+
* Union-find over peer aliases: two reports that share ANY identity-grade
|
|
1182
|
+
* alias describe one box. Returns the representative alias id for every
|
|
1183
|
+
* alias seen.
|
|
1184
|
+
*/
|
|
1185
|
+
private static groupPeerAliases(
|
|
1186
|
+
unmanagedClaims: Array<ResolvedClaim>,
|
|
1187
|
+
): Map<string, string> {
|
|
1188
|
+
const parent: Map<string, string> = new Map<string, string>();
|
|
1189
|
+
|
|
1190
|
+
const find: (key: string) => string = (key: string): string => {
|
|
1191
|
+
let root: string = key;
|
|
1192
|
+
let next: string | undefined = parent.get(root);
|
|
1193
|
+
while (next !== undefined && next !== root) {
|
|
1194
|
+
root = next;
|
|
1195
|
+
next = parent.get(root);
|
|
1196
|
+
}
|
|
1197
|
+
// Path compression, so repeated lookups stay flat.
|
|
1198
|
+
let cursor: string = key;
|
|
1199
|
+
while (cursor !== root) {
|
|
1200
|
+
const following: string = parent.get(cursor) || root;
|
|
1201
|
+
parent.set(cursor, root);
|
|
1202
|
+
cursor = following;
|
|
1203
|
+
}
|
|
1204
|
+
return root;
|
|
1205
|
+
};
|
|
1206
|
+
|
|
1207
|
+
const union: (a: string, b: string) => void = (
|
|
1208
|
+
a: string,
|
|
1209
|
+
b: string,
|
|
1210
|
+
): void => {
|
|
1211
|
+
const rootA: string = find(a);
|
|
1212
|
+
const rootB: string = find(b);
|
|
1213
|
+
if (rootA === rootB) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
// Lexicographic tie-break, so the result ignores input order.
|
|
1217
|
+
if (rootA < rootB) {
|
|
1218
|
+
parent.set(rootB, rootA);
|
|
1219
|
+
} else {
|
|
1220
|
+
parent.set(rootA, rootB);
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
for (const resolved of unmanagedClaims) {
|
|
1225
|
+
const aliasIds: Array<string> = resolved.peerAliases.map(
|
|
1226
|
+
(alias: MatchKey) => {
|
|
1227
|
+
return NetworkTopologyUtil.matchKeyId(alias);
|
|
1228
|
+
},
|
|
1229
|
+
);
|
|
1230
|
+
for (const aliasId of aliasIds) {
|
|
1231
|
+
if (!parent.has(aliasId)) {
|
|
1232
|
+
parent.set(aliasId, aliasId);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
for (let index: number = 1; index < aliasIds.length; index++) {
|
|
1236
|
+
union(aliasIds[0]!, aliasIds[index]!);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
const rootByAliasId: Map<string, string> = new Map<string, string>();
|
|
1241
|
+
for (const aliasId of parent.keys()) {
|
|
1242
|
+
rootByAliasId.set(aliasId, find(aliasId));
|
|
1243
|
+
}
|
|
1244
|
+
return rootByAliasId;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
532
1247
|
private static normalizeKey(value: string | undefined): string | undefined {
|
|
533
1248
|
if (!value) {
|
|
534
1249
|
return undefined;
|
|
@@ -537,10 +1252,19 @@ export default class NetworkTopologyUtil {
|
|
|
537
1252
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
538
1253
|
}
|
|
539
1254
|
|
|
1255
|
+
/*
|
|
1256
|
+
* A stamped monitor status beats SNMP freshness, and freshness is the
|
|
1257
|
+
* fallback — the same precedence SiteStatusRollupUtil already applies when
|
|
1258
|
+
* it rolls devices up into a site, so a device cannot read "up" on the map
|
|
1259
|
+
* and "down" on the site card above it.
|
|
1260
|
+
*/
|
|
540
1261
|
private static deviceStatus(
|
|
541
1262
|
device: TopologyDeviceInput,
|
|
542
1263
|
now: Date,
|
|
543
1264
|
): NetworkTopologyNodeStatus {
|
|
1265
|
+
if (device.monitorStatus) {
|
|
1266
|
+
return device.monitorStatus;
|
|
1267
|
+
}
|
|
544
1268
|
return NetworkTopologyUtil.statusFromLastSeen(device.lastSeenAt, now);
|
|
545
1269
|
}
|
|
546
1270
|
|