@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
|
@@ -57,6 +57,17 @@ function isIpv4Address(value) {
|
|
|
57
57
|
}
|
|
58
58
|
// Endpoint nodes rendered per graph; the slice is deterministic (by MAC).
|
|
59
59
|
const ENDPOINT_RENDER_CAP = 2000;
|
|
60
|
+
/*
|
|
61
|
+
* The built-in roles that sit at the top of a network, for the parent rule
|
|
62
|
+
* in drawAdoptedEndpoint when the project has not stamped its own answer.
|
|
63
|
+
* Mirrors the isCoreLayer flags in DEFAULT_NETWORK_DEVICE_ROLES, which is
|
|
64
|
+
* the seed every project's role table starts from.
|
|
65
|
+
*/
|
|
66
|
+
const BUILT_IN_CORE_LAYER_ROLES = new Set([
|
|
67
|
+
"router",
|
|
68
|
+
"firewall",
|
|
69
|
+
"loadBalancer",
|
|
70
|
+
]);
|
|
60
71
|
/*
|
|
61
72
|
* How long an ARP/FDB-learned endpoint keeps its "up" colour after the last
|
|
62
73
|
* walk that saw it. Taken from the shared device rule at its default
|
|
@@ -128,6 +139,12 @@ export default class NetworkTopologyUtil {
|
|
|
128
139
|
}
|
|
129
140
|
// Kept aside so the diagnostics pass below can read it back per node.
|
|
130
141
|
const isNeighborDiscoveryEnabledById = new Map();
|
|
142
|
+
/*
|
|
143
|
+
* Managed nodes by id, for the endpoint adoption pass: a device found
|
|
144
|
+
* in a switch's forwarding table is stamped with what the table knew
|
|
145
|
+
* about it (MAC, address, VLAN), and the parent rule reads its role.
|
|
146
|
+
*/
|
|
147
|
+
const deviceNodeById = new Map();
|
|
131
148
|
for (const device of devices) {
|
|
132
149
|
isNeighborDiscoveryEnabledById.set(device.id, device.isNeighborDiscoveryEnabled);
|
|
133
150
|
const deviceRole = resolveDeviceRole(device.deviceRole, {
|
|
@@ -150,6 +167,13 @@ export default class NetworkTopologyUtil {
|
|
|
150
167
|
sysName: device.sysName,
|
|
151
168
|
vendor: device.vendor,
|
|
152
169
|
deviceModel: device.deviceModel,
|
|
170
|
+
/*
|
|
171
|
+
* The declared or learned MAC, whether or not a forwarding table
|
|
172
|
+
* has found it yet: the drawer shows it, and an operator checking
|
|
173
|
+
* why a device floats wants to see what the map was looking for.
|
|
174
|
+
* Normalised so it reads the same as the endpoint rows beside it.
|
|
175
|
+
*/
|
|
176
|
+
macAddress: normalizeMac(device.macAddress),
|
|
153
177
|
};
|
|
154
178
|
/*
|
|
155
179
|
* The configured role, when the project has one for this key. The
|
|
@@ -159,6 +183,7 @@ export default class NetworkTopologyUtil {
|
|
|
159
183
|
*/
|
|
160
184
|
applyRoleStamp(deviceNode, stampForRoleKey(roleKeyForNode(device.deviceRole, deviceRole), roleIndex));
|
|
161
185
|
nodes.push(deviceNode);
|
|
186
|
+
deviceNodeById.set(device.id, deviceNode);
|
|
162
187
|
}
|
|
163
188
|
const edgeByKey = new Map();
|
|
164
189
|
/*
|
|
@@ -442,6 +467,19 @@ export default class NetworkTopologyUtil {
|
|
|
442
467
|
let droppedEndpointCount = 0;
|
|
443
468
|
let renderedEndpointCount = 0;
|
|
444
469
|
let endpointsTruncated = false;
|
|
470
|
+
let adoptedEndpointCount = 0;
|
|
471
|
+
/*
|
|
472
|
+
* --- Endpoints that ARE managed devices ---
|
|
473
|
+
*
|
|
474
|
+
* A ping-only register, handset or kiosk is a NetworkDevice with a node
|
|
475
|
+
* of its own, and it is ALSO a MAC some switch learned on an access
|
|
476
|
+
* port - which until now was drawn as a second, anonymous node hanging
|
|
477
|
+
* off that switch while the device's own node floated with no link at
|
|
478
|
+
* all (issue #3489). The index below is what lets the two be recognised
|
|
479
|
+
* as one box, so the forwarding-table evidence draws the device's cable
|
|
480
|
+
* instead of a stranger's.
|
|
481
|
+
*/
|
|
482
|
+
const adoption = NetworkTopologyUtil.planEndpointAdoption(devices, sortedEndpoints, deviceNodeIds, now);
|
|
445
483
|
for (const endpoint of sortedEndpoints) {
|
|
446
484
|
if (!endpoint.attachedNetworkDeviceId ||
|
|
447
485
|
!deviceNodeIds.has(endpoint.attachedNetworkDeviceId)) {
|
|
@@ -449,6 +487,25 @@ export default class NetworkTopologyUtil {
|
|
|
449
487
|
droppedEndpointCount++;
|
|
450
488
|
continue;
|
|
451
489
|
}
|
|
490
|
+
const adoptedDevice = adoption.deviceByEndpointId.get(endpoint.id);
|
|
491
|
+
if (adoptedDevice) {
|
|
492
|
+
/*
|
|
493
|
+
* Counted before the render cap, and never against it: an adopted
|
|
494
|
+
* endpoint mints no node, so it cannot crowd out one that does.
|
|
495
|
+
*/
|
|
496
|
+
adoptedEndpointCount++;
|
|
497
|
+
if (adoption.drawnEndpointIds.has(endpoint.id)) {
|
|
498
|
+
NetworkTopologyUtil.drawAdoptedEndpoint({
|
|
499
|
+
endpoint: endpoint,
|
|
500
|
+
device: adoptedDevice,
|
|
501
|
+
deviceNode: deviceNodeById.get(adoptedDevice.id),
|
|
502
|
+
edgeByKey: edgeByKey,
|
|
503
|
+
edges: edges,
|
|
504
|
+
interfaceByDeviceAndIndex: interfaceByDeviceAndIndex,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
452
509
|
if (renderedEndpointCount >= ENDPOINT_RENDER_CAP) {
|
|
453
510
|
endpointsTruncated = true;
|
|
454
511
|
continue;
|
|
@@ -500,6 +557,7 @@ export default class NetworkTopologyUtil {
|
|
|
500
557
|
toNodeId: nodeId,
|
|
501
558
|
fromPort: fromPort,
|
|
502
559
|
protocols: ["fdb"],
|
|
560
|
+
learnedByNodeId: endpoint.attachedNetworkDeviceId,
|
|
503
561
|
fromInterface: switchInterface
|
|
504
562
|
? {
|
|
505
563
|
interfaceIndex: switchInterface.interfaceIndex,
|
|
@@ -529,6 +587,7 @@ export default class NetworkTopologyUtil {
|
|
|
529
587
|
edges,
|
|
530
588
|
droppedEndpointCount,
|
|
531
589
|
endpointsTruncated,
|
|
590
|
+
adoptedEndpointCount,
|
|
532
591
|
suppressedNodeCount: 0,
|
|
533
592
|
};
|
|
534
593
|
}
|
|
@@ -546,9 +605,425 @@ export default class NetworkTopologyUtil {
|
|
|
546
605
|
}),
|
|
547
606
|
droppedEndpointCount,
|
|
548
607
|
endpointsTruncated,
|
|
608
|
+
adoptedEndpointCount,
|
|
549
609
|
suppressedNodeCount: nodes.length - visibleNodes.length,
|
|
550
610
|
};
|
|
551
611
|
}
|
|
612
|
+
/*
|
|
613
|
+
* Which endpoints are managed devices, and which ONE of them draws the
|
|
614
|
+
* device's cable.
|
|
615
|
+
*
|
|
616
|
+
* A device can match more than one row. The common way is hardware that
|
|
617
|
+
* was swapped keeping its address: the old unit's MAC is still a row in
|
|
618
|
+
* the endpoint table (nothing prunes it), stored on the device from the
|
|
619
|
+
* ARP pass, and the new unit's MAC is a fresher row that matches the
|
|
620
|
+
* device by address. Drawing both would put the register on two ports
|
|
621
|
+
* at once, one of them a port nothing has been plugged into for a
|
|
622
|
+
* month. So every matching row is ADOPTED - none of them is minted as a
|
|
623
|
+
* stranger - and the most recently seen one draws the link. A MAC match
|
|
624
|
+
* outranks an address match at equal freshness, and the MAC order the
|
|
625
|
+
* caller already sorted by settles the rest, so the choice never depends
|
|
626
|
+
* on query order.
|
|
627
|
+
*
|
|
628
|
+
* The price is that a genuinely dual-homed device draws one cable. That
|
|
629
|
+
* is rare among the devices this exists for, and it is the honest
|
|
630
|
+
* trade: one current cable beats one current and one stale.
|
|
631
|
+
*/
|
|
632
|
+
static planEndpointAdoption(devices, sortedEndpoints, deviceNodeIds, now) {
|
|
633
|
+
const index = NetworkTopologyUtil.buildEndpointAdoptionIndex(devices);
|
|
634
|
+
const deviceByEndpointId = new Map();
|
|
635
|
+
const drawnEndpointIds = new Set();
|
|
636
|
+
if (index.size === 0) {
|
|
637
|
+
return { deviceByEndpointId, drawnEndpointIds };
|
|
638
|
+
}
|
|
639
|
+
const candidatesByDeviceId = new Map();
|
|
640
|
+
for (const endpoint of sortedEndpoints) {
|
|
641
|
+
if (!endpoint.attachedNetworkDeviceId ||
|
|
642
|
+
!deviceNodeIds.has(endpoint.attachedNetworkDeviceId)) {
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
const match = NetworkTopologyUtil.adoptionMatchForEndpoint(endpoint, index);
|
|
646
|
+
if (!match) {
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
/*
|
|
650
|
+
* An address is only evidence while it is current. A MAC names a
|
|
651
|
+
* box for good, but an address is re-leased: a row the ARP pass
|
|
652
|
+
* bound months ago may name a laptop that held the register's
|
|
653
|
+
* address before the register was plugged in. Such a row is left to
|
|
654
|
+
* be drawn as the stale (down-coloured) endpoint it is, rather than
|
|
655
|
+
* cabling the device to a stranger's port with a live node's
|
|
656
|
+
* confidence. The window is the one endpoint nodes age out on.
|
|
657
|
+
*/
|
|
658
|
+
if (!match.matchedByMac &&
|
|
659
|
+
!NetworkTopologyUtil.isFreshSighting(endpoint, now)) {
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
deviceByEndpointId.set(endpoint.id, match.device);
|
|
663
|
+
const bucket = candidatesByDeviceId.get(match.device.id);
|
|
664
|
+
const candidate = {
|
|
665
|
+
endpoint: endpoint,
|
|
666
|
+
matchedByMac: match.matchedByMac,
|
|
667
|
+
};
|
|
668
|
+
if (bucket) {
|
|
669
|
+
bucket.push(candidate);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
candidatesByDeviceId.set(match.device.id, [candidate]);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
for (const candidates of candidatesByDeviceId.values()) {
|
|
676
|
+
let chosen = candidates[0];
|
|
677
|
+
for (const candidate of candidates.slice(1)) {
|
|
678
|
+
if (NetworkTopologyUtil.isFresherSighting(candidate, chosen)) {
|
|
679
|
+
chosen = candidate;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
drawnEndpointIds.add(chosen.endpoint.id);
|
|
683
|
+
}
|
|
684
|
+
return { deviceByEndpointId, drawnEndpointIds };
|
|
685
|
+
}
|
|
686
|
+
static isFreshSighting(endpoint, now) {
|
|
687
|
+
if (!endpoint.lastSeenAt) {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
const ageMs = now.getTime() - new Date(endpoint.lastSeenAt).getTime();
|
|
691
|
+
return ageMs <= ENDPOINT_FRESH_WINDOW_MS;
|
|
692
|
+
}
|
|
693
|
+
/*
|
|
694
|
+
* Strictly fresher: later lastSeenAt, then a MAC match over an address
|
|
695
|
+
* match. Equal on both keeps the incumbent, which is the earlier entry
|
|
696
|
+
* in MAC order.
|
|
697
|
+
*/
|
|
698
|
+
static isFresherSighting(candidate, incumbent) {
|
|
699
|
+
const candidateSeen = candidate.endpoint.lastSeenAt
|
|
700
|
+
? new Date(candidate.endpoint.lastSeenAt).getTime()
|
|
701
|
+
: Number.NEGATIVE_INFINITY;
|
|
702
|
+
const incumbentSeen = incumbent.endpoint.lastSeenAt
|
|
703
|
+
? new Date(incumbent.endpoint.lastSeenAt).getTime()
|
|
704
|
+
: Number.NEGATIVE_INFINITY;
|
|
705
|
+
if (candidateSeen !== incumbentSeen) {
|
|
706
|
+
return candidateSeen > incumbentSeen;
|
|
707
|
+
}
|
|
708
|
+
return candidate.matchedByMac && !incumbent.matchedByMac;
|
|
709
|
+
}
|
|
710
|
+
/*
|
|
711
|
+
* Every key an endpoint could be recognised as a managed device by.
|
|
712
|
+
*
|
|
713
|
+
* Two kinds, strongest first:
|
|
714
|
+
*
|
|
715
|
+
* "mac" - the device's declared or ARP-learned MAC against the MAC the
|
|
716
|
+
* switch learned. A MAC is unique per project among endpoints
|
|
717
|
+
* (the endpoint table enforces it), so this needs no scoping.
|
|
718
|
+
* "ip" - the device's hostname, when it is an IPv4 literal, against
|
|
719
|
+
* the address a router's ARP table bound the MAC to. Scoped to
|
|
720
|
+
* the SITE: every branch has a 10.0.0.5, and an unscoped match
|
|
721
|
+
* would cable one register in every store to whichever switch
|
|
722
|
+
* happened to learn a MAC that answers at that address. No site
|
|
723
|
+
* on either side is a site of its own, so a project that never
|
|
724
|
+
* set sites up still matches - project-wide, as it has to.
|
|
725
|
+
*
|
|
726
|
+
* The same ambiguity guard as the neighbour matcher: a key two devices
|
|
727
|
+
* both claim is deleted rather than resolved, because a cable drawn to
|
|
728
|
+
* the wrong box is worse than one not drawn.
|
|
729
|
+
*
|
|
730
|
+
* Interface MACs (TopologyDeviceInput.macAddresses) are deliberately NOT
|
|
731
|
+
* keys here. A walked switch's interface MACs turn up in its uplink
|
|
732
|
+
* neighbours' forwarding tables as transit traffic, and adopting those
|
|
733
|
+
* would redraw every switch-to-switch cable LLDP already reports from a
|
|
734
|
+
* weaker source. The declared MAC is different in kind: an operator (or
|
|
735
|
+
* the ARP pass) put it there to say "this is the box".
|
|
736
|
+
*/
|
|
737
|
+
static buildEndpointAdoptionIndex(devices) {
|
|
738
|
+
const index = new Map();
|
|
739
|
+
const ambiguous = new Set();
|
|
740
|
+
for (const device of devices) {
|
|
741
|
+
for (const key of NetworkTopologyUtil.adoptionKeysForDevice(device)) {
|
|
742
|
+
const existing = index.get(key);
|
|
743
|
+
if (existing && existing.id !== device.id) {
|
|
744
|
+
ambiguous.add(key);
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
index.set(key, device);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/*
|
|
751
|
+
* A walked device's interface MACs are not adoption keys (see above),
|
|
752
|
+
* but they ARE evidence about who owns a MAC. A declared MAC that some
|
|
753
|
+
* walked device reports as one of its own interfaces is a contradiction
|
|
754
|
+
* - a register registered at a switch's SVI address and stamped with
|
|
755
|
+
* the switch's MAC, say - and the neighbour matcher already refuses
|
|
756
|
+
* that key. Refusing it here too keeps the two from disagreeing about
|
|
757
|
+
* which box a MAC is.
|
|
758
|
+
*/
|
|
759
|
+
for (const device of devices) {
|
|
760
|
+
for (const rawMac of device.macAddresses || []) {
|
|
761
|
+
const mac = normalizeMac(rawMac);
|
|
762
|
+
if (!mac) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
const key = NetworkTopologyUtil.adoptionMacKey(mac);
|
|
766
|
+
const claimant = index.get(key);
|
|
767
|
+
if (claimant && claimant.id !== device.id) {
|
|
768
|
+
ambiguous.add(key);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
for (const key of ambiguous) {
|
|
773
|
+
index.delete(key);
|
|
774
|
+
}
|
|
775
|
+
return index;
|
|
776
|
+
}
|
|
777
|
+
static adoptionKeysForDevice(device) {
|
|
778
|
+
const keys = [];
|
|
779
|
+
const mac = normalizeMac(device.macAddress);
|
|
780
|
+
if (mac) {
|
|
781
|
+
keys.push(NetworkTopologyUtil.adoptionMacKey(mac));
|
|
782
|
+
}
|
|
783
|
+
/*
|
|
784
|
+
* hostname only, never `name`: a name is an operator's label, and one
|
|
785
|
+
* that happens to be an IP literal is not a claim that the device
|
|
786
|
+
* answers there - the same line matchKeysForDevice draws.
|
|
787
|
+
*/
|
|
788
|
+
const hostname = NetworkTopologyUtil.normalizeKey(device.hostname);
|
|
789
|
+
if (isIpv4Address(hostname)) {
|
|
790
|
+
keys.push(NetworkTopologyUtil.adoptionIpKey(device.siteId, hostname));
|
|
791
|
+
}
|
|
792
|
+
return keys;
|
|
793
|
+
}
|
|
794
|
+
/*
|
|
795
|
+
* The device this endpoint is, if it is one, and by which evidence. MAC
|
|
796
|
+
* first: it is the stronger claim, and the only one that survives an
|
|
797
|
+
* address change.
|
|
798
|
+
*/
|
|
799
|
+
static adoptionMatchForEndpoint(endpoint, index) {
|
|
800
|
+
const mac = normalizeMac(endpoint.macAddress);
|
|
801
|
+
if (mac) {
|
|
802
|
+
const byMac = index.get(NetworkTopologyUtil.adoptionMacKey(mac));
|
|
803
|
+
if (byMac) {
|
|
804
|
+
return { device: byMac, matchedByMac: true };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
const ip = NetworkTopologyUtil.normalizeKey(endpoint.ipAddress);
|
|
808
|
+
if (isIpv4Address(ip)) {
|
|
809
|
+
const byIp = index.get(NetworkTopologyUtil.adoptionIpKey(endpoint.siteId, ip));
|
|
810
|
+
if (byIp) {
|
|
811
|
+
return { device: byIp, matchedByMac: false };
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return undefined;
|
|
815
|
+
}
|
|
816
|
+
static adoptionMacKey(normalizedMac) {
|
|
817
|
+
return `mac\u0000${normalizedMac}`;
|
|
818
|
+
}
|
|
819
|
+
static adoptionIpKey(siteId, normalizedIp) {
|
|
820
|
+
/*
|
|
821
|
+
* null, undefined, "" and whitespace all normalise to the same "no
|
|
822
|
+
* site" key - the convention NetworkDeviceLinkRuleUtil already uses.
|
|
823
|
+
*/
|
|
824
|
+
const siteKey = (siteId || "").trim();
|
|
825
|
+
return `ip\u0000${siteKey}\u0000${normalizedIp}`;
|
|
826
|
+
}
|
|
827
|
+
/*
|
|
828
|
+
* Draws one adopted endpoint: an "fdb" edge from the switch that learned
|
|
829
|
+
* the MAC to the device's own node, merged into whatever edge already
|
|
830
|
+
* joins that pair, plus the identity the tables knew stamped on the node.
|
|
831
|
+
*
|
|
832
|
+
* A device found in its OWN tables is a self-reference (its address in
|
|
833
|
+
* its own ARP cache, say) and draws nothing - an edge from a node to
|
|
834
|
+
* itself is not a cable. It still counts as adopted: the row is not an
|
|
835
|
+
* endpoint, and it must not be minted as one.
|
|
836
|
+
*/
|
|
837
|
+
static drawAdoptedEndpoint(data) {
|
|
838
|
+
var _a;
|
|
839
|
+
const switchId = data.endpoint.attachedNetworkDeviceId;
|
|
840
|
+
const deviceId = data.device.id;
|
|
841
|
+
if (data.deviceNode) {
|
|
842
|
+
NetworkTopologyUtil.stampAdoptedIdentity(data.deviceNode, data.endpoint);
|
|
843
|
+
}
|
|
844
|
+
if (switchId === deviceId) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
// Port label + interface state on the switch end, when identifiable.
|
|
848
|
+
let switchInterface = undefined;
|
|
849
|
+
if (data.endpoint.attachedInterfaceIndex !== undefined) {
|
|
850
|
+
switchInterface = data.interfaceByDeviceAndIndex.get(`${switchId}::${data.endpoint.attachedInterfaceIndex}`);
|
|
851
|
+
}
|
|
852
|
+
let switchPort = data.endpoint.attachedPortName || (switchInterface === null || switchInterface === void 0 ? void 0 : switchInterface.name);
|
|
853
|
+
if (!switchPort && data.endpoint.attachedInterfaceIndex !== undefined) {
|
|
854
|
+
switchPort = `if${data.endpoint.attachedInterfaceIndex}`;
|
|
855
|
+
}
|
|
856
|
+
const switchEndpoint = switchInterface
|
|
857
|
+
? {
|
|
858
|
+
interfaceIndex: switchInterface.interfaceIndex,
|
|
859
|
+
interfaceName: switchInterface.name,
|
|
860
|
+
isOperationallyUp: switchInterface.isOperationallyUp,
|
|
861
|
+
isAdministrativelyUp: switchInterface.isAdministrativelyUp,
|
|
862
|
+
utilizationPercent: switchInterface.utilizationPercent,
|
|
863
|
+
inRateMbps: switchInterface.inRateMbps,
|
|
864
|
+
outRateMbps: switchInterface.outRateMbps,
|
|
865
|
+
errorsPerSecond: switchInterface.errorsPerSecond,
|
|
866
|
+
}
|
|
867
|
+
: undefined;
|
|
868
|
+
/*
|
|
869
|
+
* Which end is up. A forwarding table is not symmetric the way a
|
|
870
|
+
* neighbour protocol is: the switch LEARNED this MAC on an access
|
|
871
|
+
* port, which is a statement that the device hangs off it - for a
|
|
872
|
+
* register, a phone, a camera, a server. It is no such statement
|
|
873
|
+
* about a router or a firewall, whose MAC a switch learns on the port
|
|
874
|
+
* that leads UPSTREAM, nor about another switch, where the table says
|
|
875
|
+
* nothing about which of the two is nearer the core. Those are left
|
|
876
|
+
* for the layout to infer, exactly as an LLDP edge is.
|
|
877
|
+
*/
|
|
878
|
+
const declaredParentId = NetworkTopologyUtil.isLearningSwitchTheParent(data.deviceNode)
|
|
879
|
+
? switchId
|
|
880
|
+
: undefined;
|
|
881
|
+
const edgeKey = [switchId, deviceId].sort().join("::");
|
|
882
|
+
const existing = data.edgeByKey.get(edgeKey);
|
|
883
|
+
if (!existing) {
|
|
884
|
+
const edge = {
|
|
885
|
+
fromNodeId: switchId,
|
|
886
|
+
toNodeId: deviceId,
|
|
887
|
+
fromPort: switchPort,
|
|
888
|
+
protocols: ["fdb"],
|
|
889
|
+
fromInterface: switchEndpoint,
|
|
890
|
+
parentNodeId: declaredParentId,
|
|
891
|
+
learnedByNodeId: switchId,
|
|
892
|
+
};
|
|
893
|
+
data.edgeByKey.set(edgeKey, edge);
|
|
894
|
+
data.edges.push(edge);
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
/*
|
|
898
|
+
* The pair is already joined - by a neighbour protocol, by a link the
|
|
899
|
+
* operator drew, or by both. Merge rather than double the line.
|
|
900
|
+
*/
|
|
901
|
+
if (existing.protocols && !existing.protocols.includes("fdb")) {
|
|
902
|
+
existing.protocols.push("fdb");
|
|
903
|
+
}
|
|
904
|
+
/*
|
|
905
|
+
* Which end learned. The existing edge may be stored device→switch,
|
|
906
|
+
* so this is the only reliable statement of which end the table
|
|
907
|
+
* belongs to; the freshest sighting draws, so a later one does not
|
|
908
|
+
* move it.
|
|
909
|
+
*/
|
|
910
|
+
existing.learnedByNodeId = existing.learnedByNodeId || switchId;
|
|
911
|
+
/*
|
|
912
|
+
* The switch-end port. Measured beats typed: against a hand-drawn
|
|
913
|
+
* link the forwarding table's port replaces whatever was typed,
|
|
914
|
+
* because the operator's port is a best recollection and this is
|
|
915
|
+
* what the switch actually reports. Against a neighbour protocol both
|
|
916
|
+
* are measured, and the protocol's is kept - it names the port from
|
|
917
|
+
* both ends, where the table only knows one.
|
|
918
|
+
*/
|
|
919
|
+
const isMeasuredAlready = Boolean((_a = existing.protocols) === null || _a === void 0 ? void 0 : _a.some((protocol) => {
|
|
920
|
+
return protocol === "lldp" || protocol === "cdp";
|
|
921
|
+
}));
|
|
922
|
+
const switchIsFromEnd = existing.fromNodeId === switchId;
|
|
923
|
+
const existingPort = switchIsFromEnd
|
|
924
|
+
? existing.fromPort
|
|
925
|
+
: existing.toPort;
|
|
926
|
+
const existingInterface = switchIsFromEnd ? existing.fromInterface : existing.toInterface;
|
|
927
|
+
let mergedPort;
|
|
928
|
+
let mergedInterface;
|
|
929
|
+
if (!isMeasuredAlready) {
|
|
930
|
+
mergedPort = switchPort || existingPort;
|
|
931
|
+
mergedInterface = NetworkTopologyUtil.mergeEndpoints(switchEndpoint, existingInterface);
|
|
932
|
+
}
|
|
933
|
+
else if (NetworkTopologyUtil.describesSamePort(existingInterface, existingPort, switchEndpoint, switchPort)) {
|
|
934
|
+
/*
|
|
935
|
+
* Two reports of ONE port: the protocol's fields are kept and the
|
|
936
|
+
* table fills whatever the protocol's report left blank.
|
|
937
|
+
*/
|
|
938
|
+
mergedPort = existingPort || switchPort;
|
|
939
|
+
mergedInterface = NetworkTopologyUtil.mergeEndpoints(existingInterface, switchEndpoint);
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
/*
|
|
943
|
+
* Two reports of DIFFERENT ports - the device was re-patched and the
|
|
944
|
+
* table has caught up before the neighbour protocol, or the other
|
|
945
|
+
* way round. Splicing the two field by field would label the
|
|
946
|
+
* protocol's port with the other port's name and paint it with the
|
|
947
|
+
* other port's state, so the protocol's end is left whole and the
|
|
948
|
+
* table's port is not shown until the two agree.
|
|
949
|
+
*/
|
|
950
|
+
mergedPort = existingPort;
|
|
951
|
+
mergedInterface = existingInterface;
|
|
952
|
+
}
|
|
953
|
+
if (switchIsFromEnd) {
|
|
954
|
+
existing.fromPort = mergedPort;
|
|
955
|
+
existing.fromInterface = mergedInterface;
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
existing.toPort = mergedPort;
|
|
959
|
+
existing.toInterface = mergedInterface;
|
|
960
|
+
}
|
|
961
|
+
/*
|
|
962
|
+
* A hierarchy somebody DECLARED - on the link, or through a rule -
|
|
963
|
+
* beats the one the table implies. The specific statement beats the
|
|
964
|
+
* general one, the same precedence the manual-link merge applies.
|
|
965
|
+
*/
|
|
966
|
+
existing.parentNodeId = existing.parentNodeId || declaredParentId;
|
|
967
|
+
}
|
|
968
|
+
/*
|
|
969
|
+
* Whether a neighbour protocol's report of the switch end and the
|
|
970
|
+
* forwarding table's attachment name the same port. By ifIndex when both
|
|
971
|
+
* know it; by port label when the protocol's end has no interface row
|
|
972
|
+
* behind it. Unknown on both sides is not "the same" - it is nothing to
|
|
973
|
+
* compare, and the protocol's end is kept as it was.
|
|
974
|
+
*/
|
|
975
|
+
static describesSamePort(existingInterface, existingPort, switchEndpoint, switchPort) {
|
|
976
|
+
if ((existingInterface === null || existingInterface === void 0 ? void 0 : existingInterface.interfaceIndex) !== undefined &&
|
|
977
|
+
(switchEndpoint === null || switchEndpoint === void 0 ? void 0 : switchEndpoint.interfaceIndex) !== undefined) {
|
|
978
|
+
return existingInterface.interfaceIndex === switchEndpoint.interfaceIndex;
|
|
979
|
+
}
|
|
980
|
+
const existingLabel = NetworkTopologyUtil.normalizeKey((existingInterface === null || existingInterface === void 0 ? void 0 : existingInterface.interfaceName) || existingPort);
|
|
981
|
+
const switchLabel = NetworkTopologyUtil.normalizeKey((switchEndpoint === null || switchEndpoint === void 0 ? void 0 : switchEndpoint.interfaceName) || switchPort);
|
|
982
|
+
return (existingLabel !== undefined &&
|
|
983
|
+
switchLabel !== undefined &&
|
|
984
|
+
existingLabel === switchLabel);
|
|
985
|
+
}
|
|
986
|
+
/*
|
|
987
|
+
* What the tables knew about the device, onto its node, so the detail
|
|
988
|
+
* drawer can show the MAC, the address and the VLAN it was learned on.
|
|
989
|
+
* Fills gaps only: a MAC the operator declared is already the one that
|
|
990
|
+
* matched, and an address the row carries is left as it is.
|
|
991
|
+
*/
|
|
992
|
+
static stampAdoptedIdentity(node, endpoint) {
|
|
993
|
+
const mac = normalizeMac(endpoint.macAddress);
|
|
994
|
+
if (mac && !node.macAddress) {
|
|
995
|
+
node.macAddress = mac;
|
|
996
|
+
}
|
|
997
|
+
if (endpoint.ipAddress && !node.ipAddress) {
|
|
998
|
+
node.ipAddress = endpoint.ipAddress;
|
|
999
|
+
}
|
|
1000
|
+
if (typeof endpoint.vlanId === "number" && node.vlanId === undefined) {
|
|
1001
|
+
node.vlanId = endpoint.vlanId;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
/*
|
|
1005
|
+
* Whether a switch that learned this device's MAC on an access port is
|
|
1006
|
+
* thereby its parent. True for anything that hangs off a switch; false
|
|
1007
|
+
* for a core-layer device (router, firewall, load balancer - or a
|
|
1008
|
+
* custom role the project marked core) and for another switch. The
|
|
1009
|
+
* configured flag wins where the project set one; the built-in core set
|
|
1010
|
+
* is the fallback, as everywhere else the stamp is read.
|
|
1011
|
+
*/
|
|
1012
|
+
static isLearningSwitchTheParent(deviceNode) {
|
|
1013
|
+
if (!deviceNode) {
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
const isCore = deviceNode.isCoreLayerRole !== undefined
|
|
1017
|
+
? deviceNode.isCoreLayerRole
|
|
1018
|
+
: BUILT_IN_CORE_LAYER_ROLES.has(deviceNode.role || "unknown");
|
|
1019
|
+
if (isCore) {
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
1022
|
+
const roleKey = (deviceNode.roleKey || deviceNode.role || "")
|
|
1023
|
+
.trim()
|
|
1024
|
+
.toLowerCase();
|
|
1025
|
+
return roleKey !== "switch";
|
|
1026
|
+
}
|
|
552
1027
|
/*
|
|
553
1028
|
* Both protocols' neighbor entries, normalized. LLDP first so its
|
|
554
1029
|
* (usually richer) identifiers win the first-report slots; CDP then
|
|
@@ -777,6 +1252,15 @@ export default class NetworkTopologyUtil {
|
|
|
777
1252
|
add({ kind: "mac", value: mac });
|
|
778
1253
|
}
|
|
779
1254
|
}
|
|
1255
|
+
/*
|
|
1256
|
+
* The declared MAC too. A ping-only device has no interface walk to
|
|
1257
|
+
* report its MACs from, so this is the only MAC it can answer to when
|
|
1258
|
+
* a neighbour advertises it as an LLDP chassis id.
|
|
1259
|
+
*/
|
|
1260
|
+
const declaredMac = normalizeMac(device.macAddress);
|
|
1261
|
+
if (declaredMac) {
|
|
1262
|
+
add({ kind: "mac", value: declaredMac });
|
|
1263
|
+
}
|
|
780
1264
|
return keys;
|
|
781
1265
|
}
|
|
782
1266
|
/*
|