@oneuptime/common 12.0.34 → 13.0.0
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/DatabaseModels/GlobalConfig.ts +12 -12
- package/Models/DatabaseModels/InventoryItem.ts +44 -1
- package/Models/DatabaseModels/NetworkDevice.ts +126 -0
- package/Server/EnvironmentConfig.ts +47 -16
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Redis.ts +8 -8
- package/Server/Infrastructure/Status.ts +10 -4
- package/Server/Services/DatabaseService.ts +65 -13
- package/Server/Services/InventoryItemService.ts +31 -1
- package/Server/Services/MonitorTemplateService.ts +140 -19
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +349 -41
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
- package/Server/Services/NetworkDeviceService.ts +69 -0
- package/Server/Services/UserNotificationRuleService.ts +76 -25
- package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
- package/Server/Types/Database/QueryUtil.ts +56 -0
- package/Server/Types/Markdown.ts +93 -1
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +10 -1
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +82 -35
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +202 -37
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
- package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
- package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
- package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
- package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
- package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
- package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
- package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
- package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
- package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
- package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
- package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
- package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
- package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
- package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
- package/Tests/Models/InventoryItemStatus.test.ts +147 -0
- package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
- package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
- package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
- package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +62 -23
- package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
- package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
- package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
- package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
- package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
- package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1133 -81
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
- package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
- package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
- package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
- package/Tests/Server/Types/Markdown.test.ts +194 -1
- package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
- package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1 -1
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
- package/Tests/Server/Utils/EventLoop.test.ts +359 -0
- package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +13 -7
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +224 -10
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
- package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
- package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
- package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
- package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
- package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
- package/Tests/Types/JSONFunctions.test.ts +40 -0
- package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
- package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
- package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
- package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
- package/Tests/UI/Rum/ReplayHeader.test.tsx +10 -2
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
- package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
- package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
- package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
- package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
- package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
- package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
- package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
- package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
- package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
- package/Types/Dashboard/DashboardVariable.ts +8 -0
- package/Types/JSON.ts +4 -0
- package/Types/JSONFunctions.ts +16 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
- package/Types/SerializableObjectDictionary.ts +4 -0
- package/Types/Telemetry/InventoryLiveness.ts +56 -0
- package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +46 -21
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
- package/UI/esbuild-config.js +6 -0
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
- package/Utils/Dashboard/LabelVariable.ts +152 -0
- package/Utils/Dashboard/VariableUrlState.ts +27 -5
- package/Utils/MetricUnitUtil.ts +34 -0
- package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
- package/Utils/Monitor/MetricValueFormatter.ts +342 -0
- package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
- package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
- package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +44 -12
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Redis.js +8 -8
- package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
- package/build/dist/Server/Infrastructure/Status.js +10 -4
- package/build/dist/Server/Infrastructure/Status.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +58 -13
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +28 -1
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +270 -37
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +75 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +10 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +58 -35
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +166 -35
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
- package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
- package/build/dist/Types/JSON.js +1 -0
- package/build/dist/Types/JSON.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +13 -0
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +4 -0
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +9 -2
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
- package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
- package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
- package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +26 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/test-setup.sh +9 -9
- package/tsconfig.json +51 -11
- package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +0 -399
|
@@ -113,12 +113,52 @@ export interface AutoImportRuleRunResult {
|
|
|
113
113
|
monitorsSkippedUnsupportedHost: number;
|
|
114
114
|
// Monitor creates that failed after the device was available.
|
|
115
115
|
monitorsFailed: number;
|
|
116
|
+
/*
|
|
117
|
+
* Why devices could not be created — the actual server-side messages,
|
|
118
|
+
* deduplicated and capped. "0 imported, check the server logs" is not an
|
|
119
|
+
* answer an operator of a self-hosted install can act on, and it is not one
|
|
120
|
+
* support can act on either: every distinct failure mode used to render the
|
|
121
|
+
* identical sentence. See OneUptime/oneuptime#3643.
|
|
122
|
+
*/
|
|
123
|
+
deviceFailureReasons: Array<string>;
|
|
124
|
+
// Why active Network Device monitors could not be created. Same contract.
|
|
125
|
+
monitorFailureReasons: Array<string>;
|
|
126
|
+
/*
|
|
127
|
+
* True when the run stopped ATTEMPTING monitor creates because the failure
|
|
128
|
+
* it hit is systemic rather than per host — the rule's Monitor Template
|
|
129
|
+
* cannot be resolved at all, or the identical error came back for every
|
|
130
|
+
* device it tried. The remaining devices are still counted into
|
|
131
|
+
* monitorsFailed (that many monitors really are missing); they are simply
|
|
132
|
+
* not retried, because they would fail identically.
|
|
133
|
+
*/
|
|
134
|
+
monitorProvisioningHalted: boolean;
|
|
116
135
|
/*
|
|
117
136
|
* True when the run stopped at the device-create or monitor-create cap
|
|
118
137
|
* with work left over. Running again continues from idempotent inventory
|
|
119
138
|
* and provisioning keys.
|
|
120
139
|
*/
|
|
121
140
|
isTruncated: boolean;
|
|
141
|
+
/*
|
|
142
|
+
* Matched hosts a cap left un-imported: they have no device yet and this
|
|
143
|
+
* run did not create one. A capped run keeps EVALUATING after it stops
|
|
144
|
+
* writing precisely so it can answer "how much is left" — reporting
|
|
145
|
+
* "imported 500" with no remainder is what makes a truncated run read as
|
|
146
|
+
* silently losing the rest of the estate (OneUptime issue #3642).
|
|
147
|
+
*/
|
|
148
|
+
hostsPendingImport: number;
|
|
149
|
+
/*
|
|
150
|
+
* (device, template) pairs a cap left unprovisioned: the device is there,
|
|
151
|
+
* nothing monitors it yet, and this run's monitor budget was spent. The
|
|
152
|
+
* counterpart of hostsPendingImport for the monitor half of the work,
|
|
153
|
+
* which has its OWN cap and so can be the only thing left over.
|
|
154
|
+
*/
|
|
155
|
+
monitorsPendingCreation: number;
|
|
156
|
+
/*
|
|
157
|
+
* True when a capped manual run stopped OPENING further scans. The two
|
|
158
|
+
* pending counters above then cover only the scans it actually read, so
|
|
159
|
+
* the remainder they report is a floor rather than a total.
|
|
160
|
+
*/
|
|
161
|
+
hasUnevaluatedScans: boolean;
|
|
122
162
|
/*
|
|
123
163
|
* True when the project has more completed scans than one manual run
|
|
124
164
|
* reads (the newest MAX_SCANS_PER_AUTO_IMPORT_RULE_RUN). Distinct from
|
|
@@ -139,6 +179,38 @@ export interface AutoImportRuleRunResult {
|
|
|
139
179
|
// How many addresses matchedIpAddressSample carries at most.
|
|
140
180
|
export const MAX_MATCHED_IP_SAMPLE: number = 50;
|
|
141
181
|
|
|
182
|
+
/*
|
|
183
|
+
* How many DISTINCT failure reasons a run reports. A run that fails the same
|
|
184
|
+
* way 500 times has one reason; a run that fails three different ways has
|
|
185
|
+
* three worth reading. Beyond that the dialog stops being a summary.
|
|
186
|
+
*/
|
|
187
|
+
export const MAX_RUN_FAILURE_REASONS: number = 3;
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
* How much of one reason survives. Server exception messages are written for
|
|
191
|
+
* humans and are short, but a driver error can carry an entire statement, and
|
|
192
|
+
* that belongs in the logs rather than in a modal.
|
|
193
|
+
*/
|
|
194
|
+
export const MAX_RUN_FAILURE_REASON_LENGTH: number = 300;
|
|
195
|
+
|
|
196
|
+
/*
|
|
197
|
+
* How many capped server passes ONE press of "Run Rule" chains together.
|
|
198
|
+
*
|
|
199
|
+
* A real run is bounded server-side (see the engine's per-run device and
|
|
200
|
+
* monitor caps) so a rule that meets a /16 cannot become one unbounded API
|
|
201
|
+
* request. That bound used to be the operator's problem: a 909-device estate
|
|
202
|
+
* imported 500 and stopped, and finishing it meant noticing the cap line and
|
|
203
|
+
* pressing the button again — which is exactly what OneUptime issue #3642
|
|
204
|
+
* reported as "the rest are simply missing". The button now drives the passes
|
|
205
|
+
* itself, so one press finishes an estate of any ordinary size, while each
|
|
206
|
+
* individual request stays as small as it always was.
|
|
207
|
+
*
|
|
208
|
+
* Still bounded, because "one press" must not mean "an hour of chained
|
|
209
|
+
* requests": past this many passes the run reports what is left and the
|
|
210
|
+
* operator presses again, deliberately.
|
|
211
|
+
*/
|
|
212
|
+
export const MAX_AUTO_IMPORT_RUN_PASSES: number = 20;
|
|
213
|
+
|
|
142
214
|
/*
|
|
143
215
|
* A count off the wire. An absent or non-numeric field reads as zero rather
|
|
144
216
|
* than as NaN: a summary line that says "NaN devices" is worse than one that
|
|
@@ -158,12 +230,129 @@ function hosts(count: number): string {
|
|
|
158
230
|
return `${count} ${count === 1 ? "host" : "hosts"}`;
|
|
159
231
|
}
|
|
160
232
|
|
|
233
|
+
/*
|
|
234
|
+
* The denominator of "matched N out of M". It carries its own noun because
|
|
235
|
+
* hosts() already supplies one: composing them produced "matched 501 hosts out
|
|
236
|
+
* of the 501 hosts discovered hosts it looked at", which is the sentence in the
|
|
237
|
+
* screenshot on issue #3643.
|
|
238
|
+
*/
|
|
239
|
+
function discoveredHosts(count: number): string {
|
|
240
|
+
return `${count} discovered ${count === 1 ? "host" : "hosts"}`;
|
|
241
|
+
}
|
|
242
|
+
|
|
161
243
|
function activeMonitors(count: number): string {
|
|
162
244
|
return `${count} active Network Device ${
|
|
163
245
|
count === 1 ? "monitor" : "monitors"
|
|
164
246
|
}`;
|
|
165
247
|
}
|
|
166
248
|
|
|
249
|
+
/*
|
|
250
|
+
* Reasons off the wire. Anything that is not a non-empty string is dropped
|
|
251
|
+
* rather than rendered as "undefined", and the list is capped the same way the
|
|
252
|
+
* server caps it so a hand-rolled API client cannot make the dialog unbounded.
|
|
253
|
+
*/
|
|
254
|
+
function readReasons(json: JSONObject, key: string): Array<string> {
|
|
255
|
+
const value: unknown = json[key];
|
|
256
|
+
|
|
257
|
+
if (!Array.isArray(value)) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return (value as Array<unknown>)
|
|
262
|
+
.filter((entry: unknown): boolean => {
|
|
263
|
+
return typeof entry === "string" && entry.trim().length > 0;
|
|
264
|
+
})
|
|
265
|
+
.map((entry: unknown): string => {
|
|
266
|
+
return (entry as string)
|
|
267
|
+
.trim()
|
|
268
|
+
.substring(0, MAX_RUN_FAILURE_REASON_LENGTH);
|
|
269
|
+
})
|
|
270
|
+
.slice(0, MAX_RUN_FAILURE_REASONS);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// What already ends a sentence, so becauseOf does not add a second full stop.
|
|
274
|
+
const TERMINAL_PUNCTUATION: Array<string> = [".", "!", "?"];
|
|
275
|
+
|
|
276
|
+
/*
|
|
277
|
+
* The half-sentence that turns "N could not be created" into something the
|
|
278
|
+
* operator can act on. With no reason recorded the old pointer at the server
|
|
279
|
+
* logs is still the honest answer; with reasons, they ARE the answer.
|
|
280
|
+
*/
|
|
281
|
+
function becauseOf(reasons: Array<string>): string {
|
|
282
|
+
if (reasons.length === 0) {
|
|
283
|
+
return " Check the server logs for the reason.";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const rendered: Array<string> = reasons.map((reason: string): string => {
|
|
287
|
+
return TERMINAL_PUNCTUATION.includes(reason.slice(-1))
|
|
288
|
+
? reason
|
|
289
|
+
: `${reason}.`;
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (rendered.length === 1) {
|
|
293
|
+
return ` Reason: ${rendered[0]}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return ` Reasons: ${rendered.join(" ")}`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/*
|
|
300
|
+
* Every pass's reasons as one deduplicated, capped list — see
|
|
301
|
+
* mergeAutoImportRunPasses.
|
|
302
|
+
*/
|
|
303
|
+
function mergeReasons(lists: Array<Array<string>>): Array<string> {
|
|
304
|
+
const seen: Set<string> = new Set<string>();
|
|
305
|
+
|
|
306
|
+
for (const list of lists) {
|
|
307
|
+
for (const reason of list) {
|
|
308
|
+
if (seen.size >= MAX_RUN_FAILURE_REASONS) {
|
|
309
|
+
return Array.from(seen);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
seen.add(reason);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return Array.from(seen);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/*
|
|
320
|
+
* What a capped run left behind, as a phrase — "409 hosts still to import",
|
|
321
|
+
* "409 active Network Device monitors still to create", or both.
|
|
322
|
+
*
|
|
323
|
+
* The two halves of the work have separate caps, so either can be the only
|
|
324
|
+
* thing left over: an estate that is fully imported but unmonitored hits the
|
|
325
|
+
* monitor cap with nothing at all pending on the device side.
|
|
326
|
+
*
|
|
327
|
+
* Empty when the run stopped without anything measurable left — the caller
|
|
328
|
+
* falls back to the unquantified sentence rather than printing "with —".
|
|
329
|
+
*/
|
|
330
|
+
function describeRemainingWork(result: AutoImportRuleRunResult): string {
|
|
331
|
+
const parts: Array<string> = [];
|
|
332
|
+
|
|
333
|
+
if (result.hostsPendingImport > 0) {
|
|
334
|
+
parts.push(`${hosts(result.hostsPendingImport)} still to import`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (result.monitorsPendingCreation > 0) {
|
|
338
|
+
parts.push(
|
|
339
|
+
`${activeMonitors(result.monitorsPendingCreation)} still to create`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (parts.length === 0) {
|
|
344
|
+
return "";
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/*
|
|
348
|
+
* A capped run stops OPENING scans once it stops writing, so when scans
|
|
349
|
+
* were left unread the remainder it counted is a floor, not a total.
|
|
350
|
+
*/
|
|
351
|
+
return `${result.hasUnevaluatedScans ? "at least " : ""}${parts.join(
|
|
352
|
+
" and ",
|
|
353
|
+
)}`;
|
|
354
|
+
}
|
|
355
|
+
|
|
167
356
|
export class RuleRunResultUtil {
|
|
168
357
|
/*
|
|
169
358
|
* The sentences the auto-import report shows. A run that imported nothing is
|
|
@@ -199,22 +388,32 @@ export class RuleRunResultUtil {
|
|
|
199
388
|
lines.push(
|
|
200
389
|
`This rule would import nothing. It matched ${hosts(
|
|
201
390
|
result.hostsMatched,
|
|
202
|
-
)} out of the ${
|
|
391
|
+
)} out of the ${discoveredHosts(
|
|
203
392
|
result.hostsEvaluated,
|
|
204
|
-
)}
|
|
393
|
+
)} it looked at.`,
|
|
205
394
|
);
|
|
206
395
|
}
|
|
207
396
|
} else if (result.devicesCreated > 0) {
|
|
208
397
|
lines.push(
|
|
209
398
|
`Imported ${hosts(result.devicesCreated)} as network devices.`,
|
|
210
399
|
);
|
|
400
|
+
} else if (result.monitorsCreated > 0) {
|
|
401
|
+
/*
|
|
402
|
+
* A backfill run over an estate that is ALREADY imported creates no
|
|
403
|
+
* devices at all, and the flat "No devices were imported" read as a
|
|
404
|
+
* failure right above "Created 500 active Network Device monitors".
|
|
405
|
+
* Name what did not happen precisely instead.
|
|
406
|
+
*/
|
|
407
|
+
lines.push(
|
|
408
|
+
`No new network devices were imported. This rule matched ${hosts(
|
|
409
|
+
result.hostsMatched,
|
|
410
|
+
)} out of the ${discoveredHosts(result.hostsEvaluated)} it looked at.`,
|
|
411
|
+
);
|
|
211
412
|
} else {
|
|
212
413
|
lines.push(
|
|
213
414
|
`No devices were imported. This rule matched ${hosts(
|
|
214
415
|
result.hostsMatched,
|
|
215
|
-
)} out of the ${
|
|
216
|
-
result.hostsEvaluated,
|
|
217
|
-
)} discovered hosts it looked at.`,
|
|
416
|
+
)} out of the ${discoveredHosts(result.hostsEvaluated)} it looked at.`,
|
|
218
417
|
);
|
|
219
418
|
}
|
|
220
419
|
|
|
@@ -262,7 +461,20 @@ export class RuleRunResultUtil {
|
|
|
262
461
|
lines.push(
|
|
263
462
|
`${activeMonitors(
|
|
264
463
|
result.monitorsFailed,
|
|
265
|
-
)} could not be created. Their network devices remain imported
|
|
464
|
+
)} could not be created. Their network devices remain imported.${becauseOf(
|
|
465
|
+
result.monitorFailureReasons,
|
|
466
|
+
)}`,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/*
|
|
471
|
+
* Said once, after the reason, because it changes what the operator should
|
|
472
|
+
* do next: nothing here is waiting for another run. The remaining devices
|
|
473
|
+
* were counted, not attempted — see monitorProvisioningHalted.
|
|
474
|
+
*/
|
|
475
|
+
if (result.monitorProvisioningHalted) {
|
|
476
|
+
lines.push(
|
|
477
|
+
"Monitor provisioning stopped for the rest of this run: that failure applies to every remaining device, not just one, so the rest were not attempted. Fix it and run this rule again.",
|
|
266
478
|
);
|
|
267
479
|
}
|
|
268
480
|
|
|
@@ -280,9 +492,9 @@ export class RuleRunResultUtil {
|
|
|
280
492
|
|
|
281
493
|
if (result.devicesFailed > 0) {
|
|
282
494
|
lines.push(
|
|
283
|
-
`${hosts(
|
|
284
|
-
result.
|
|
285
|
-
)}
|
|
495
|
+
`${hosts(result.devicesFailed)} could not be imported.${becauseOf(
|
|
496
|
+
result.deviceFailureReasons,
|
|
497
|
+
)}`,
|
|
286
498
|
);
|
|
287
499
|
}
|
|
288
500
|
|
|
@@ -291,14 +503,43 @@ export class RuleRunResultUtil {
|
|
|
291
503
|
* re-running skips what is already imported and continues. The scan cap
|
|
292
504
|
* does NOT: a re-run re-reads the same newest scans, so promising "run
|
|
293
505
|
* again to continue" there would send the operator in a circle.
|
|
506
|
+
*
|
|
507
|
+
* And a run that reached the cap having created NOTHING resumes nowhere
|
|
508
|
+
* either: every attempt failed, so the identical run would fail again.
|
|
509
|
+
* Telling that operator to "run again to continue" is the advice issue
|
|
510
|
+
* #3643 was given, and following it changes nothing. The automatic sweep
|
|
511
|
+
* has always understood this — it refuses to re-queue a zero-progress
|
|
512
|
+
* truncated pass — so say the same thing here.
|
|
294
513
|
*/
|
|
295
514
|
if (result.isTruncated && !result.isDryRun) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
);
|
|
515
|
+
const madeProgress: boolean =
|
|
516
|
+
result.devicesCreated > 0 || result.monitorsCreated > 0;
|
|
517
|
+
const remaining: string = describeRemainingWork(result);
|
|
518
|
+
|
|
519
|
+
if (!madeProgress) {
|
|
520
|
+
/*
|
|
521
|
+
* The remainder is still worth naming here — it is the size of the
|
|
522
|
+
* problem to fix — but the advice is the opposite of "run again".
|
|
523
|
+
*/
|
|
524
|
+
lines.push(
|
|
525
|
+
remaining
|
|
526
|
+
? `Stopped at the run cap without creating anything, with ${remaining}. Running again as-is would repeat the same failures — fix the failures reported above first.`
|
|
527
|
+
: "Stopped at the run cap without creating anything, so running again as-is would repeat the same failures — fix the failures reported above first.",
|
|
528
|
+
);
|
|
529
|
+
} else {
|
|
530
|
+
lines.push(
|
|
531
|
+
remaining
|
|
532
|
+
? `Stopped at the run cap with ${remaining}. Run again to continue; already-imported hosts are skipped.`
|
|
533
|
+
: "Stopped at the run cap — run again to continue; already-imported hosts are skipped.",
|
|
534
|
+
);
|
|
535
|
+
}
|
|
299
536
|
} else if (result.isTruncated) {
|
|
537
|
+
const remaining: string = describeRemainingWork(result);
|
|
538
|
+
|
|
300
539
|
lines.push(
|
|
301
|
-
|
|
540
|
+
remaining
|
|
541
|
+
? `Stopped counting at the run cap with ${remaining} — a real run is bounded by the same device-import and active-monitor creation limits.`
|
|
542
|
+
: "Stopped counting at the run cap — a real run is bounded by the same device-import and active-monitor creation limits.",
|
|
302
543
|
);
|
|
303
544
|
}
|
|
304
545
|
|
|
@@ -311,6 +552,148 @@ export class RuleRunResultUtil {
|
|
|
311
552
|
return lines.join(" ");
|
|
312
553
|
}
|
|
313
554
|
|
|
555
|
+
/*
|
|
556
|
+
* The line shown WHILE a "Run Rule" press is still chaining passes.
|
|
557
|
+
*
|
|
558
|
+
* A capped run over a large estate is now several requests long, and an
|
|
559
|
+
* unlabelled spinner sitting there for two minutes reads as a hang — which
|
|
560
|
+
* is the same "is it doing anything?" the silent cap created in the first
|
|
561
|
+
* place. This says what has landed so far; the full report replaces it when
|
|
562
|
+
* the chain ends.
|
|
563
|
+
*/
|
|
564
|
+
public static describeAutoImportProgress(
|
|
565
|
+
result: AutoImportRuleRunResult,
|
|
566
|
+
): string {
|
|
567
|
+
const done: Array<string> = [];
|
|
568
|
+
|
|
569
|
+
if (result.devicesCreated > 0) {
|
|
570
|
+
done.push(`${hosts(result.devicesCreated)} imported`);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (result.monitorsCreated > 0) {
|
|
574
|
+
done.push(`${activeMonitors(result.monitorsCreated)} created`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (done.length === 0) {
|
|
578
|
+
return "Still importing…";
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return `${done.join(
|
|
582
|
+
", ",
|
|
583
|
+
)} so far. This rule matched more than one run's cap, so the import is continuing — leave this open.`;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/*
|
|
587
|
+
* The one report for a "Run Rule" press that took several server passes.
|
|
588
|
+
*
|
|
589
|
+
* Each pass is capped server-side and re-reads the SAME scans, so the
|
|
590
|
+
* counters split into three kinds and only one of them may be summed:
|
|
591
|
+
*
|
|
592
|
+
* - State of the estate as the run found it (evaluated / matched /
|
|
593
|
+
* excluded / already-registered / already-monitored) comes from the
|
|
594
|
+
* FIRST pass. Summing would multiply the estate by the pass count, and
|
|
595
|
+
* taking the last pass would report the run's own creates back as
|
|
596
|
+
* "already registered" — "imported 909 hosts" followed by "909 hosts
|
|
597
|
+
* already had network devices" is a self-contradiction.
|
|
598
|
+
* - Work actually done (creates and failures) is SUMMED: every pass does
|
|
599
|
+
* a different 500.
|
|
600
|
+
* - What is still left (the pending counters and isTruncated) comes from
|
|
601
|
+
* the LAST pass, which is the only one that still speaks for now.
|
|
602
|
+
*
|
|
603
|
+
* hasMoreScans is a property of the project, identical in every pass; it is
|
|
604
|
+
* taken from the last for the same reason as the pending counters.
|
|
605
|
+
*/
|
|
606
|
+
public static mergeAutoImportRunPasses(
|
|
607
|
+
passes: Array<AutoImportRuleRunResult>,
|
|
608
|
+
): AutoImportRuleRunResult {
|
|
609
|
+
const first: AutoImportRuleRunResult | undefined = passes[0];
|
|
610
|
+
const last: AutoImportRuleRunResult | undefined = passes[passes.length - 1];
|
|
611
|
+
|
|
612
|
+
if (!first || !last) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
"mergeAutoImportRunPasses needs at least one run result to merge.",
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (passes.length === 1) {
|
|
619
|
+
return first;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const sum: (pick: (pass: AutoImportRuleRunResult) => number) => number = (
|
|
623
|
+
pick: (pass: AutoImportRuleRunResult) => number,
|
|
624
|
+
): number => {
|
|
625
|
+
return passes.reduce(
|
|
626
|
+
(total: number, pass: AutoImportRuleRunResult): number => {
|
|
627
|
+
return total + pick(pass);
|
|
628
|
+
},
|
|
629
|
+
0,
|
|
630
|
+
);
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
return {
|
|
634
|
+
hostsEvaluated: first.hostsEvaluated,
|
|
635
|
+
hostsMatched: first.hostsMatched,
|
|
636
|
+
hostsExcluded: first.hostsExcluded,
|
|
637
|
+
hostsSkippedAlreadyRegistered: first.hostsSkippedAlreadyRegistered,
|
|
638
|
+
monitorsSkippedAlreadyExisting: first.monitorsSkippedAlreadyExisting,
|
|
639
|
+
monitorsSkippedUnsupportedHost: first.monitorsSkippedUnsupportedHost,
|
|
640
|
+
devicesCreated: sum((pass: AutoImportRuleRunResult): number => {
|
|
641
|
+
return pass.devicesCreated;
|
|
642
|
+
}),
|
|
643
|
+
devicesFailed: sum((pass: AutoImportRuleRunResult): number => {
|
|
644
|
+
return pass.devicesFailed;
|
|
645
|
+
}),
|
|
646
|
+
monitorsWouldCreate: sum((pass: AutoImportRuleRunResult): number => {
|
|
647
|
+
return pass.monitorsWouldCreate;
|
|
648
|
+
}),
|
|
649
|
+
monitorsCreated: sum((pass: AutoImportRuleRunResult): number => {
|
|
650
|
+
return pass.monitorsCreated;
|
|
651
|
+
}),
|
|
652
|
+
monitorsFailed: sum((pass: AutoImportRuleRunResult): number => {
|
|
653
|
+
return pass.monitorsFailed;
|
|
654
|
+
}),
|
|
655
|
+
/*
|
|
656
|
+
* Failure reasons are a SET across the chain, not a per-pass list: the
|
|
657
|
+
* same fault usually reappears in every pass, and a distinct fault in
|
|
658
|
+
* pass three is exactly what the operator has to see. Deduplicated in
|
|
659
|
+
* order and capped the same way one pass is, so the dialog stays a
|
|
660
|
+
* summary however many passes ran.
|
|
661
|
+
*/
|
|
662
|
+
deviceFailureReasons: mergeReasons(
|
|
663
|
+
passes.map((pass: AutoImportRuleRunResult): Array<string> => {
|
|
664
|
+
return pass.deviceFailureReasons;
|
|
665
|
+
}),
|
|
666
|
+
),
|
|
667
|
+
monitorFailureReasons: mergeReasons(
|
|
668
|
+
passes.map((pass: AutoImportRuleRunResult): Array<string> => {
|
|
669
|
+
return pass.monitorFailureReasons;
|
|
670
|
+
}),
|
|
671
|
+
),
|
|
672
|
+
/*
|
|
673
|
+
* Halting is a property of the pass that halted, and the chain stops
|
|
674
|
+
* chaining after one — so any pass having halted is the answer, not
|
|
675
|
+
* just the last.
|
|
676
|
+
*/
|
|
677
|
+
monitorProvisioningHalted: passes.some(
|
|
678
|
+
(pass: AutoImportRuleRunResult): boolean => {
|
|
679
|
+
return pass.monitorProvisioningHalted;
|
|
680
|
+
},
|
|
681
|
+
),
|
|
682
|
+
isTruncated: last.isTruncated,
|
|
683
|
+
hostsPendingImport: last.hostsPendingImport,
|
|
684
|
+
monitorsPendingCreation: last.monitorsPendingCreation,
|
|
685
|
+
hasUnevaluatedScans: last.hasUnevaluatedScans,
|
|
686
|
+
hasMoreScans: last.hasMoreScans,
|
|
687
|
+
isDryRun: first.isDryRun,
|
|
688
|
+
/*
|
|
689
|
+
* The first pass's sample is the one worth keeping: it was drawn
|
|
690
|
+
* before this run changed anything, so it answers "which hosts is this
|
|
691
|
+
* rule claiming" rather than "which hosts were left over by pass 19".
|
|
692
|
+
*/
|
|
693
|
+
matchedIpAddressSample: first.matchedIpAddressSample,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
314
697
|
public static parseSiteAssignmentRuleRunResult(
|
|
315
698
|
json: JSONObject | undefined | null,
|
|
316
699
|
): SiteAssignmentRuleRunResult {
|
|
@@ -385,7 +768,13 @@ export class RuleRunResultUtil {
|
|
|
385
768
|
"monitorsSkippedUnsupportedHost",
|
|
386
769
|
),
|
|
387
770
|
monitorsFailed: readCount(source, "monitorsFailed"),
|
|
771
|
+
deviceFailureReasons: readReasons(source, "deviceFailureReasons"),
|
|
772
|
+
monitorFailureReasons: readReasons(source, "monitorFailureReasons"),
|
|
773
|
+
monitorProvisioningHalted: source["monitorProvisioningHalted"] === true,
|
|
388
774
|
isTruncated: source["isTruncated"] === true,
|
|
775
|
+
hostsPendingImport: readCount(source, "hostsPendingImport"),
|
|
776
|
+
monitorsPendingCreation: readCount(source, "monitorsPendingCreation"),
|
|
777
|
+
hasUnevaluatedScans: source["hasUnevaluatedScans"] === true,
|
|
389
778
|
hasMoreScans: source["hasMoreScans"] === true,
|
|
390
779
|
isDryRun: source["isDryRun"] === true,
|
|
391
780
|
matchedIpAddressSample: sample as Array<string>,
|
|
@@ -8,6 +8,7 @@ import GreaterThanOrEqual from "./BaseDatabase/GreaterThanOrEqual";
|
|
|
8
8
|
import InBetween from "./BaseDatabase/InBetween";
|
|
9
9
|
import Includes from "./BaseDatabase/Includes";
|
|
10
10
|
import IncludesAll from "./BaseDatabase/IncludesAll";
|
|
11
|
+
import IncludesAnyOfGroups from "./BaseDatabase/IncludesAnyOfGroups";
|
|
11
12
|
import IncludesNone from "./BaseDatabase/IncludesNone";
|
|
12
13
|
import StartsWith from "./BaseDatabase/StartsWith";
|
|
13
14
|
import EndsWith from "./BaseDatabase/EndsWith";
|
|
@@ -155,6 +156,9 @@ const SerializableObjectDictionary: Dictionary<any> = {
|
|
|
155
156
|
get [ObjectType.IncludesAll](): any {
|
|
156
157
|
return IncludesAll;
|
|
157
158
|
},
|
|
159
|
+
get [ObjectType.IncludesAnyOfGroups](): any {
|
|
160
|
+
return IncludesAnyOfGroups;
|
|
161
|
+
},
|
|
158
162
|
get [ObjectType.IncludesNone](): any {
|
|
159
163
|
return IncludesNone;
|
|
160
164
|
},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import EntitySource from "./EntitySource";
|
|
2
|
+
|
|
3
|
+
export enum InventoryLiveness {
|
|
4
|
+
Live = "live",
|
|
5
|
+
Recent = "recent",
|
|
6
|
+
Stale = "stale",
|
|
7
|
+
Never = "never",
|
|
8
|
+
NotTracked = "not-tracked",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const INVENTORY_LIVE_WINDOW_MINUTES: number = 30;
|
|
12
|
+
export const INVENTORY_STALE_AFTER_MINUTES: number = 24 * 60;
|
|
13
|
+
|
|
14
|
+
export interface InventoryLivenessInput {
|
|
15
|
+
source?: string | undefined;
|
|
16
|
+
lastSeenAt?: Date | string | undefined | null;
|
|
17
|
+
now: Date;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A last-seen timestamp is a heartbeat only for discovered items. Mirrored
|
|
22
|
+
* and manual items retain their creation timestamp, so aging those would
|
|
23
|
+
* incorrectly classify healthy infrastructure as stale.
|
|
24
|
+
*/
|
|
25
|
+
export const getInventoryLivenessState: (
|
|
26
|
+
input: InventoryLivenessInput,
|
|
27
|
+
) => InventoryLiveness = (input: InventoryLivenessInput): InventoryLiveness => {
|
|
28
|
+
if (input.source !== EntitySource.Discovered) {
|
|
29
|
+
return InventoryLiveness.NotTracked;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!input.lastSeenAt) {
|
|
33
|
+
return InventoryLiveness.Never;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const lastSeenAt: Date = new Date(input.lastSeenAt);
|
|
37
|
+
|
|
38
|
+
if (Number.isNaN(lastSeenAt.getTime())) {
|
|
39
|
+
return InventoryLiveness.Never;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const minutesSinceLastSeen: number = Math.max(
|
|
43
|
+
0,
|
|
44
|
+
Math.floor((input.now.getTime() - lastSeenAt.getTime()) / (60 * 1000)),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (minutesSinceLastSeen <= INVENTORY_LIVE_WINDOW_MINUTES) {
|
|
48
|
+
return InventoryLiveness.Live;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (minutesSinceLastSeen <= INVENTORY_STALE_AFTER_MINUTES) {
|
|
52
|
+
return InventoryLiveness.Recent;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return InventoryLiveness.Stale;
|
|
56
|
+
};
|
|
@@ -93,7 +93,14 @@ const CSVFileUpload: FunctionComponent<ComponentProps> = (
|
|
|
93
93
|
const [error, setError] = useState<string>("");
|
|
94
94
|
const [parsedRows, setParsedRows] = useState<Array<CSVRow>>([]);
|
|
95
95
|
const [fileName, setFileName] = useState<string>("");
|
|
96
|
-
|
|
96
|
+
/*
|
|
97
|
+
* The element type goes in T on its own: RefObject<T> already declares
|
|
98
|
+
* `current` as `T | null`, and RefObject<HTMLInputElement | null> is a
|
|
99
|
+
* structurally identical but non-assignable type, rejected on the `ref`
|
|
100
|
+
* prop below because RefObject is covariant in T. That mismatch is what
|
|
101
|
+
* used to need a cast there.
|
|
102
|
+
*/
|
|
103
|
+
const fileInputRef: React.RefObject<HTMLInputElement> =
|
|
97
104
|
useRef<HTMLInputElement>(null);
|
|
98
105
|
|
|
99
106
|
const downloadTemplate: DownloadTemplateFunction = (): void => {
|
|
@@ -270,7 +277,7 @@ const CSVFileUpload: FunctionComponent<ComponentProps> = (
|
|
|
270
277
|
</div>
|
|
271
278
|
<p className="text-xs text-gray-500">CSV files only</p>
|
|
272
279
|
<input
|
|
273
|
-
ref={fileInputRef
|
|
280
|
+
ref={fileInputRef}
|
|
274
281
|
type="file"
|
|
275
282
|
accept=".csv"
|
|
276
283
|
className="sr-only"
|