@oneuptime/common 12.0.7 → 12.0.8
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/TelemetryEntity.ts +45 -0
- package/Models/DatabaseModels/TelemetryEntityRelationship.ts +26 -0
- package/Models/DatabaseModels/WorkflowLog.ts +38 -0
- package/Server/API/UserAPI.ts +16 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
- package/Server/Services/TelemetryEntityRelationshipService.ts +3 -0
- package/Server/Services/TelemetryEntityService.ts +114 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
- package/Server/Types/Workflow/TriggerCode.ts +12 -0
- package/Server/Types/Workflow/Workflow.ts +5 -0
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
- package/Server/Utils/VM/VMAPI.ts +56 -8
- package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
- package/Tests/Server/Services/TelemetryEntityManualCreate.test.ts +248 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
- package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
- package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
- package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
- package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +379 -0
- package/Tests/Types/Workflow/Templates.test.ts +403 -0
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
- package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
- package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
- package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
- package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +821 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +287 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +185 -0
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +174 -0
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +152 -0
- package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
- package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +240 -0
- package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
- package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
- package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +31 -0
- package/Types/SerializableObjectDictionary.ts +133 -39
- package/Types/Telemetry/EntitySource.ts +40 -0
- package/Types/Telemetry/EntityType.ts +27 -0
- package/Types/Telemetry/EntityTypeGroups.ts +65 -0
- package/Types/Workflow/Component.ts +29 -0
- package/Types/Workflow/Components/API.ts +35 -0
- package/Types/Workflow/Components/BaseModel.ts +7 -3
- package/Types/Workflow/StepTrace.ts +181 -0
- package/Types/Workflow/TemplateSyntax.ts +499 -0
- package/Types/Workflow/Templates.ts +316 -0
- package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +23 -6
- package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
- package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
- package/UI/Components/Forms/Types/Field.ts +7 -0
- package/UI/Components/Forms/Validation.ts +63 -1
- package/UI/Components/Input/Input.tsx +31 -3
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
- package/UI/Components/Modal/Modal.tsx +37 -4
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
- package/UI/Components/Workflow/ArgumentsForm.tsx +194 -7
- package/UI/Components/Workflow/Component.tsx +28 -26
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +69 -8
- package/UI/Components/Workflow/GraphLint.ts +591 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +396 -0
- package/UI/Components/Workflow/ModelSchema.ts +196 -0
- package/UI/Components/Workflow/RunForm.tsx +41 -7
- package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
- package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
- package/UI/Components/Workflow/Utils.ts +93 -1
- package/UI/Components/Workflow/Workflow.tsx +77 -4
- package/UI/Types/LayeredDismissal.ts +31 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +45 -7
- package/Utils/Telemetry/EntityKey.ts +66 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js +47 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js +27 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
- package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +16 -1
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +3 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +91 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +133 -39
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +39 -0
- package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +27 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
- package/build/dist/Types/Workflow/Component.js +21 -0
- package/build/dist/Types/Workflow/Component.js.map +1 -1
- package/build/dist/Types/Workflow/Components/API.js +35 -0
- package/build/dist/Types/Workflow/Components/API.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +7 -3
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/StepTrace.js +104 -0
- package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js +326 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +218 -0
- package/build/dist/Types/Workflow/Templates.js.map +1 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +22 -6
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +44 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +12 -4
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +31 -5
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +140 -11
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Component.js +20 -19
- package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +31 -3
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +396 -0
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +225 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js +118 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
- package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
- package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Utils.js +73 -1
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +51 -6
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Types/LayeredDismissal.js +21 -0
- package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +35 -6
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityKey.js +52 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { NetworkTopologyDeviceRole } from "../../../Types/Monitor/SnmpMonitor/NetworkTopology";
|
|
3
|
+
import {
|
|
4
|
+
DEVICE_ROLES_IN_LEGEND_ORDER,
|
|
5
|
+
DEVICE_ROLE_LABELS,
|
|
6
|
+
classifyDeviceRole,
|
|
7
|
+
classifyEndpointRole,
|
|
8
|
+
labelForDeviceRole,
|
|
9
|
+
} from "../../../Utils/Monitor/NetworkDeviceRoleUtil";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* The role classifier decides what shape every node on the topology map is
|
|
13
|
+
* drawn as, so these tests are written as a catalogue of real hardware
|
|
14
|
+
* strings — the ones SNMP actually returns — rather than as coverage of
|
|
15
|
+
* the regexes. The interesting cases are the collisions: vendors reuse
|
|
16
|
+
* each other's model prefixes (Juniper MX960 vs Meraki MX64), reuse their
|
|
17
|
+
* OWN prefixes across roles (Catalyst 2960 / 8300 / 9800), and put a
|
|
18
|
+
* Linux kernel in the sysDescr of things that are emphatically not
|
|
19
|
+
* servers.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
describe("classifyDeviceRole — switches", () => {
|
|
23
|
+
test("Cisco Catalyst and Nexus families", () => {
|
|
24
|
+
expect(classifyDeviceRole({ deviceModel: "Catalyst 2960X-48TS-L" })).toBe(
|
|
25
|
+
"switch",
|
|
26
|
+
);
|
|
27
|
+
expect(classifyDeviceRole({ deviceModel: "WS-C3850-48P" })).toBe("switch");
|
|
28
|
+
expect(classifyDeviceRole({ deviceModel: "Nexus 9336C-FX2" })).toBe(
|
|
29
|
+
"switch",
|
|
30
|
+
);
|
|
31
|
+
expect(classifyDeviceRole({ deviceModel: "C9300-24T" })).toBe("switch");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("Juniper EX and QFX", () => {
|
|
35
|
+
expect(
|
|
36
|
+
classifyDeviceRole({
|
|
37
|
+
vendor: "Juniper Networks",
|
|
38
|
+
deviceModel: "EX4300-48T",
|
|
39
|
+
}),
|
|
40
|
+
).toBe("switch");
|
|
41
|
+
expect(
|
|
42
|
+
classifyDeviceRole({
|
|
43
|
+
vendor: "Juniper Networks",
|
|
44
|
+
deviceModel: "QFX5120",
|
|
45
|
+
}),
|
|
46
|
+
).toBe("switch");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("other switching families", () => {
|
|
50
|
+
expect(classifyDeviceRole({ deviceModel: "DCS-7050SX3-48YC8" })).toBe(
|
|
51
|
+
"switch",
|
|
52
|
+
);
|
|
53
|
+
expect(classifyDeviceRole({ deviceModel: "FortiSwitch-124E" })).toBe(
|
|
54
|
+
"switch",
|
|
55
|
+
);
|
|
56
|
+
expect(classifyDeviceRole({ deviceModel: "ProCurve J9085A" })).toBe(
|
|
57
|
+
"switch",
|
|
58
|
+
);
|
|
59
|
+
expect(classifyDeviceRole({ deviceModel: "PowerConnect 5548" })).toBe(
|
|
60
|
+
"switch",
|
|
61
|
+
);
|
|
62
|
+
expect(classifyDeviceRole({ deviceModel: "USW-24-PoE" })).toBe("switch");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("MikroTik CRS is a switch even though MikroTik reads as a router", () => {
|
|
66
|
+
expect(
|
|
67
|
+
classifyDeviceRole({ sysDescr: "MikroTik RouterOS CRS326-24G-2S+" }),
|
|
68
|
+
).toBe("switch");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("classifyDeviceRole — routers", () => {
|
|
73
|
+
test("Cisco ISR and ASR", () => {
|
|
74
|
+
expect(classifyDeviceRole({ deviceModel: "ISR4331/K9" })).toBe("router");
|
|
75
|
+
expect(classifyDeviceRole({ deviceModel: "ASR1001-X" })).toBe("router");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("Juniper MX only when the vendor evidence says Juniper", () => {
|
|
79
|
+
expect(
|
|
80
|
+
classifyDeviceRole({ vendor: "Juniper Networks", deviceModel: "MX960" }),
|
|
81
|
+
).toBe("router");
|
|
82
|
+
expect(
|
|
83
|
+
classifyDeviceRole({ sysDescr: "Juniper Networks, Inc. mx240 JUNOS" }),
|
|
84
|
+
).toBe("router");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("MikroTik and other router families", () => {
|
|
88
|
+
expect(classifyDeviceRole({ sysDescr: "RouterOS CCR1036-8G-2S+" })).toBe(
|
|
89
|
+
"router",
|
|
90
|
+
);
|
|
91
|
+
expect(classifyDeviceRole({ deviceModel: "EdgeRouter 4" })).toBe("router");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("classifyDeviceRole — the Catalyst collision", () => {
|
|
96
|
+
test("Catalyst names three different roles and each wins its own", () => {
|
|
97
|
+
expect(classifyDeviceRole({ deviceModel: "Catalyst 2960X" })).toBe(
|
|
98
|
+
"switch",
|
|
99
|
+
);
|
|
100
|
+
expect(classifyDeviceRole({ deviceModel: "Catalyst 8300-1N1S-4T2X" })).toBe(
|
|
101
|
+
"router",
|
|
102
|
+
);
|
|
103
|
+
expect(
|
|
104
|
+
classifyDeviceRole({ deviceModel: "Catalyst 9800-40 Wireless" }),
|
|
105
|
+
).toBe("wirelessAccessPoint");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("the bare model codes collide the same way and resolve the same way", () => {
|
|
109
|
+
expect(classifyDeviceRole({ deviceModel: "C9300L-24P" })).toBe("switch");
|
|
110
|
+
expect(classifyDeviceRole({ deviceModel: "C8500-12X" })).toBe("router");
|
|
111
|
+
expect(classifyDeviceRole({ deviceModel: "C9800-CL" })).toBe(
|
|
112
|
+
"wirelessAccessPoint",
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("classifyDeviceRole — the MX collision", () => {
|
|
118
|
+
test("Meraki MX is a firewall, Juniper MX is a router", () => {
|
|
119
|
+
expect(
|
|
120
|
+
classifyDeviceRole({ vendor: "Cisco Meraki", deviceModel: "MX68" }),
|
|
121
|
+
).toBe("firewall");
|
|
122
|
+
expect(
|
|
123
|
+
classifyDeviceRole({ vendor: "Juniper Networks", deviceModel: "MX204" }),
|
|
124
|
+
).toBe("router");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("the Meraki rule reads the platform string, not just the vendor column", () => {
|
|
128
|
+
expect(classifyDeviceRole({ platform: "Meraki MX250 Cloud Managed" })).toBe(
|
|
129
|
+
"firewall",
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("an MX with no vendor evidence at all stays unknown rather than guessing", () => {
|
|
134
|
+
expect(classifyDeviceRole({ deviceModel: "MX100" })).toBe("unknown");
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("classifyDeviceRole — firewalls", () => {
|
|
139
|
+
test("the major firewall families", () => {
|
|
140
|
+
expect(classifyDeviceRole({ deviceModel: "FortiGate-60F" })).toBe(
|
|
141
|
+
"firewall",
|
|
142
|
+
);
|
|
143
|
+
expect(classifyDeviceRole({ deviceModel: "PA-3220" })).toBe("firewall");
|
|
144
|
+
expect(classifyDeviceRole({ deviceModel: "SRX345" })).toBe("firewall");
|
|
145
|
+
expect(
|
|
146
|
+
classifyDeviceRole({ sysDescr: "Cisco Adaptive Security Appliance" }),
|
|
147
|
+
).toBe("firewall");
|
|
148
|
+
expect(classifyDeviceRole({ deviceModel: "ASA5516-X" })).toBe("firewall");
|
|
149
|
+
expect(classifyDeviceRole({ sysDescr: "Firepower Threat Defense" })).toBe(
|
|
150
|
+
"firewall",
|
|
151
|
+
);
|
|
152
|
+
expect(classifyDeviceRole({ sysDescr: "SonicWALL NSa 2700" })).toBe(
|
|
153
|
+
"firewall",
|
|
154
|
+
);
|
|
155
|
+
expect(classifyDeviceRole({ sysDescr: "pfSense 2.7.0-RELEASE" })).toBe(
|
|
156
|
+
"firewall",
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("Sophos SG is a firewall and does not fall into the SG switch rule", () => {
|
|
161
|
+
expect(classifyDeviceRole({ deviceModel: "Sophos SG 210" })).toBe(
|
|
162
|
+
"firewall",
|
|
163
|
+
);
|
|
164
|
+
expect(classifyDeviceRole({ deviceModel: "SG350-28P" })).toBe("switch");
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe("classifyDeviceRole — wireless, load balancers, storage, servers", () => {
|
|
169
|
+
test("access points and controllers", () => {
|
|
170
|
+
expect(classifyDeviceRole({ deviceModel: "AIR-CAP3702I-A-K9" })).toBe(
|
|
171
|
+
"wirelessAccessPoint",
|
|
172
|
+
);
|
|
173
|
+
expect(classifyDeviceRole({ sysDescr: "Cisco Aironet 1852i" })).toBe(
|
|
174
|
+
"wirelessAccessPoint",
|
|
175
|
+
);
|
|
176
|
+
expect(classifyDeviceRole({ deviceModel: "FortiAP-231F" })).toBe(
|
|
177
|
+
"wirelessAccessPoint",
|
|
178
|
+
);
|
|
179
|
+
expect(
|
|
180
|
+
classifyDeviceRole({ vendor: "Cisco Meraki", deviceModel: "MR46" }),
|
|
181
|
+
).toBe("wirelessAccessPoint");
|
|
182
|
+
expect(classifyDeviceRole({ deviceModel: "AP-515" })).toBe(
|
|
183
|
+
"wirelessAccessPoint",
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("load balancers", () => {
|
|
188
|
+
expect(classifyDeviceRole({ sysDescr: "BIG-IP 4000s" })).toBe(
|
|
189
|
+
"loadBalancer",
|
|
190
|
+
);
|
|
191
|
+
expect(classifyDeviceRole({ sysDescr: "NetScaler MPX 8905" })).toBe(
|
|
192
|
+
"loadBalancer",
|
|
193
|
+
);
|
|
194
|
+
expect(classifyDeviceRole({ deviceModel: "Thunder 3030S" })).toBe(
|
|
195
|
+
"unknown",
|
|
196
|
+
);
|
|
197
|
+
expect(classifyDeviceRole({ deviceModel: "A10 Thunder 3030S" })).toBe(
|
|
198
|
+
"loadBalancer",
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("storage", () => {
|
|
203
|
+
expect(
|
|
204
|
+
classifyDeviceRole({ vendor: "NetApp", sysDescr: "NetApp Release 9.9" }),
|
|
205
|
+
).toBe("storage");
|
|
206
|
+
expect(classifyDeviceRole({ deviceModel: "Synology DS1819+" })).toBe(
|
|
207
|
+
"storage",
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("servers and hypervisors", () => {
|
|
212
|
+
expect(classifyDeviceRole({ sysDescr: "VMware ESXi 7.0.3" })).toBe(
|
|
213
|
+
"server",
|
|
214
|
+
);
|
|
215
|
+
expect(classifyDeviceRole({ deviceModel: "PowerEdge R740" })).toBe(
|
|
216
|
+
"server",
|
|
217
|
+
);
|
|
218
|
+
expect(classifyDeviceRole({ sysDescr: "HPE iLO 5" })).toBe("server");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("printers, cameras and phones that speak SNMP", () => {
|
|
222
|
+
expect(classifyDeviceRole({ deviceModel: "HP LaserJet M607" })).toBe(
|
|
223
|
+
"printer",
|
|
224
|
+
);
|
|
225
|
+
expect(
|
|
226
|
+
classifyDeviceRole({ sysDescr: "AXIS P3245-LVE Network Camera" }),
|
|
227
|
+
).toBe("camera");
|
|
228
|
+
expect(classifyDeviceRole({ sysDescr: "Cisco IP Phone 7961" })).toBe(
|
|
229
|
+
"phone",
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("classifyDeviceRole — evidence precedence", () => {
|
|
235
|
+
test("the model beats a sysDescr that says something else", () => {
|
|
236
|
+
/*
|
|
237
|
+
* The box is a switch; its sysDescr is the vendor's boilerplate about
|
|
238
|
+
* the software image, which mentions routing.
|
|
239
|
+
*/
|
|
240
|
+
expect(
|
|
241
|
+
classifyDeviceRole({
|
|
242
|
+
deviceModel: "Catalyst 2960X",
|
|
243
|
+
sysDescr: "Cisco IOS Software, IP Routing Base",
|
|
244
|
+
}),
|
|
245
|
+
).toBe("switch");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("sysDescr beats sysObjectId", () => {
|
|
249
|
+
expect(
|
|
250
|
+
classifyDeviceRole({
|
|
251
|
+
sysDescr: "FortiSwitch-148F",
|
|
252
|
+
// Fortinet's arc on its own would say firewall.
|
|
253
|
+
sysObjectId: "1.3.6.1.4.1.12356.106.1.1",
|
|
254
|
+
}),
|
|
255
|
+
).toBe("switch");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("sysObjectId beats the generic words in sysDescr", () => {
|
|
259
|
+
expect(
|
|
260
|
+
classifyDeviceRole({
|
|
261
|
+
sysDescr: "Linux-based security gateway appliance",
|
|
262
|
+
sysObjectId: "1.3.6.1.4.1.3375.2.1.3.4.10",
|
|
263
|
+
}),
|
|
264
|
+
).toBe("loadBalancer");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("a generic word in sysDescr beats the hostname convention", () => {
|
|
268
|
+
expect(
|
|
269
|
+
classifyDeviceRole({
|
|
270
|
+
sysDescr: "24-port Gigabit Switch",
|
|
271
|
+
name: "edge-fw01",
|
|
272
|
+
}),
|
|
273
|
+
).toBe("switch");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("sysName is preferred to name when the two disagree", () => {
|
|
277
|
+
expect(
|
|
278
|
+
classifyDeviceRole({ sysName: "core-rtr-1", name: "core-sw-1" }),
|
|
279
|
+
).toBe("router");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("the hostname beats a Linux kernel in sysDescr", () => {
|
|
283
|
+
/*
|
|
284
|
+
* The regression this ordering exists for: plenty of network gear
|
|
285
|
+
* announces "Linux ...", and calling every one of them a server would
|
|
286
|
+
* shape the whole map wrong.
|
|
287
|
+
*/
|
|
288
|
+
expect(
|
|
289
|
+
classifyDeviceRole({
|
|
290
|
+
sysDescr: "Linux gw-1 5.4.0-91-generic x86_64",
|
|
291
|
+
name: "gw-1",
|
|
292
|
+
}),
|
|
293
|
+
).toBe("router");
|
|
294
|
+
expect(
|
|
295
|
+
classifyDeviceRole({
|
|
296
|
+
sysDescr: "Linux fw-edge-1 5.15.0 x86_64",
|
|
297
|
+
name: "fw-edge-1",
|
|
298
|
+
}),
|
|
299
|
+
).toBe("firewall");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("the hostname also beats a generic SNMP agent OID", () => {
|
|
303
|
+
expect(
|
|
304
|
+
classifyDeviceRole({
|
|
305
|
+
sysObjectId: "1.3.6.1.4.1.8072.3.2.10",
|
|
306
|
+
name: "core-sw-2",
|
|
307
|
+
}),
|
|
308
|
+
).toBe("switch");
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("a Linux box with an uninformative name is still a server", () => {
|
|
312
|
+
expect(
|
|
313
|
+
classifyDeviceRole({
|
|
314
|
+
sysDescr: "Linux app-3 5.4.0-91-generic x86_64",
|
|
315
|
+
name: "app-3",
|
|
316
|
+
}),
|
|
317
|
+
).toBe("server");
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe("classifyDeviceRole — sysObjectId arcs", () => {
|
|
322
|
+
test("single-role vendor arcs", () => {
|
|
323
|
+
expect(
|
|
324
|
+
classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.25461.2.3.18" }),
|
|
325
|
+
).toBe("firewall");
|
|
326
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.789.2.5" })).toBe(
|
|
327
|
+
"storage",
|
|
328
|
+
);
|
|
329
|
+
expect(
|
|
330
|
+
classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.30065.1.3011" }),
|
|
331
|
+
).toBe("switch");
|
|
332
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.14988.1" })).toBe(
|
|
333
|
+
"router",
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("a leading dot, which many agents emit, is tolerated", () => {
|
|
338
|
+
expect(classifyDeviceRole({ sysObjectId: ".1.3.6.1.4.1.3375.2.1.3" })).toBe(
|
|
339
|
+
"loadBalancer",
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("an exact arc with no children matches", () => {
|
|
344
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.789" })).toBe(
|
|
345
|
+
"storage",
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("matching is on dotted boundaries, so a longer arc is not a prefix", () => {
|
|
350
|
+
/*
|
|
351
|
+
* ".11" (HP) must not swallow ".1124", and the printer arc under HP
|
|
352
|
+
* must not claim every HP switch.
|
|
353
|
+
*/
|
|
354
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.7890.1" })).toBe(
|
|
355
|
+
"unknown",
|
|
356
|
+
);
|
|
357
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.11.2.3.7.11" })).toBe(
|
|
358
|
+
"unknown",
|
|
359
|
+
);
|
|
360
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.11.2.3.9.1" })).toBe(
|
|
361
|
+
"printer",
|
|
362
|
+
);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("a mixed-catalogue vendor's arc says nothing on its own", () => {
|
|
366
|
+
// Cisco and Juniper make every kind of box; their arcs are no evidence.
|
|
367
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.9.1.1745" })).toBe(
|
|
368
|
+
"unknown",
|
|
369
|
+
);
|
|
370
|
+
expect(classifyDeviceRole({ sysObjectId: "1.3.6.1.4.1.2636.1.1.1" })).toBe(
|
|
371
|
+
"unknown",
|
|
372
|
+
);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
describe("classifyDeviceRole — hostname conventions", () => {
|
|
377
|
+
test("the common abbreviations, as whole tokens", () => {
|
|
378
|
+
const expectations: Array<[string, NetworkTopologyDeviceRole]> = [
|
|
379
|
+
["core-rtr-1", "router"],
|
|
380
|
+
["dc1-gw01", "router"],
|
|
381
|
+
["idf2-sw-3", "switch"],
|
|
382
|
+
["spine-01", "switch"],
|
|
383
|
+
["edge-fw01", "firewall"],
|
|
384
|
+
["bldg-a-ap-12", "wirelessAccessPoint"],
|
|
385
|
+
["prod-lb-2", "loadBalancer"],
|
|
386
|
+
["srv-db-1", "server"],
|
|
387
|
+
["nas-backup", "storage"],
|
|
388
|
+
["lobby-cam-4", "camera"],
|
|
389
|
+
];
|
|
390
|
+
for (const [name, role] of expectations) {
|
|
391
|
+
expect(classifyDeviceRole({ name: name })).toBe(role);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("separators other than the hyphen work too", () => {
|
|
396
|
+
expect(classifyDeviceRole({ name: "core.sw.1" })).toBe("switch");
|
|
397
|
+
expect(classifyDeviceRole({ name: "core_fw_1" })).toBe("firewall");
|
|
398
|
+
expect(classifyDeviceRole({ name: "CORE SW 1" })).toBe("switch");
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test("substrings never match — this is the whole point of tokenizing", () => {
|
|
402
|
+
expect(classifyDeviceRole({ name: "swansea-office" })).toBe("unknown");
|
|
403
|
+
expect(classifyDeviceRole({ name: "apex-1" })).toBe("unknown");
|
|
404
|
+
expect(classifyDeviceRole({ name: "lbrary-node" })).toBe("unknown");
|
|
405
|
+
expect(classifyDeviceRole({ name: "greatwall" })).toBe("unknown");
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test("a token with a trailing index still matches", () => {
|
|
409
|
+
expect(classifyDeviceRole({ name: "sw01" })).toBe("switch");
|
|
410
|
+
expect(classifyDeviceRole({ name: "fw2.example.com" })).toBe("firewall");
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
describe("classifyDeviceRole — nothing to go on", () => {
|
|
415
|
+
test("empty, blank and absent signals all come back unknown", () => {
|
|
416
|
+
expect(classifyDeviceRole({})).toBe("unknown");
|
|
417
|
+
expect(
|
|
418
|
+
classifyDeviceRole({
|
|
419
|
+
name: "",
|
|
420
|
+
sysName: " ",
|
|
421
|
+
sysDescr: "",
|
|
422
|
+
deviceModel: undefined,
|
|
423
|
+
sysObjectId: "",
|
|
424
|
+
}),
|
|
425
|
+
).toBe("unknown");
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test("an unremarkable name is unknown, not a bad guess", () => {
|
|
429
|
+
expect(classifyDeviceRole({ name: "device-42" })).toBe("unknown");
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test("classification is case- and whitespace-insensitive", () => {
|
|
433
|
+
expect(classifyDeviceRole({ deviceModel: " CATALYST 2960X " })).toBe(
|
|
434
|
+
"switch",
|
|
435
|
+
);
|
|
436
|
+
expect(classifyDeviceRole({ deviceModel: "fortigate-60f" })).toBe(
|
|
437
|
+
"firewall",
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("classifyEndpointRole", () => {
|
|
443
|
+
test("an endpoint with nothing said about it is a host", () => {
|
|
444
|
+
expect(classifyEndpointRole({})).toBe("host");
|
|
445
|
+
expect(classifyEndpointRole({ name: "aa:bb:cc:dd:ee:ff" })).toBe("host");
|
|
446
|
+
expect(classifyEndpointRole({ classification: "POS terminal" })).toBe(
|
|
447
|
+
"host",
|
|
448
|
+
);
|
|
449
|
+
expect(classifyEndpointRole({ classification: "Kiosk" })).toBe("host");
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
test("the human-typed classification is read first", () => {
|
|
453
|
+
expect(classifyEndpointRole({ classification: "Camera" })).toBe("camera");
|
|
454
|
+
expect(classifyEndpointRole({ classification: "printer" })).toBe("printer");
|
|
455
|
+
expect(classifyEndpointRole({ classification: "IP phone" })).toBe("phone");
|
|
456
|
+
expect(classifyEndpointRole({ classification: "Access point" })).toBe(
|
|
457
|
+
"wirelessAccessPoint",
|
|
458
|
+
);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("the OUI vendor is the fallback when nobody classified it", () => {
|
|
462
|
+
expect(
|
|
463
|
+
classifyEndpointRole({ vendor: "Hikvision Digital Technology" }),
|
|
464
|
+
).toBe("camera");
|
|
465
|
+
expect(classifyEndpointRole({ vendor: "Zebra Technologies" })).toBe(
|
|
466
|
+
"printer",
|
|
467
|
+
);
|
|
468
|
+
expect(classifyEndpointRole({ vendor: "Polycom Inc" })).toBe("phone");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("a classification wins over a vendor that disagrees", () => {
|
|
472
|
+
expect(
|
|
473
|
+
classifyEndpointRole({
|
|
474
|
+
classification: "Camera",
|
|
475
|
+
vendor: "Zebra Technologies",
|
|
476
|
+
}),
|
|
477
|
+
).toBe("camera");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test("an unrecognised vendor leaves it a host", () => {
|
|
481
|
+
expect(classifyEndpointRole({ vendor: "Intel Corporate" })).toBe("host");
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
test("the name is the last resort", () => {
|
|
485
|
+
expect(classifyEndpointRole({ name: "warehouse-camera-3" })).toBe("camera");
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
describe("labels", () => {
|
|
490
|
+
test("every role has a label", () => {
|
|
491
|
+
const roles: Array<NetworkTopologyDeviceRole> = Object.keys(
|
|
492
|
+
DEVICE_ROLE_LABELS,
|
|
493
|
+
) as Array<NetworkTopologyDeviceRole>;
|
|
494
|
+
for (const role of roles) {
|
|
495
|
+
expect(typeof DEVICE_ROLE_LABELS[role]).toBe("string");
|
|
496
|
+
expect(DEVICE_ROLE_LABELS[role].length).toBeGreaterThan(0);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test("labelForDeviceRole falls back for an absent role", () => {
|
|
501
|
+
expect(labelForDeviceRole("switch")).toBe("Switch");
|
|
502
|
+
expect(labelForDeviceRole(undefined)).toBe(DEVICE_ROLE_LABELS.unknown);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test("the legend order lists every role except unknown, once each", () => {
|
|
506
|
+
expect(DEVICE_ROLES_IN_LEGEND_ORDER).not.toContain("unknown");
|
|
507
|
+
expect(new Set(DEVICE_ROLES_IN_LEGEND_ORDER).size).toBe(
|
|
508
|
+
DEVICE_ROLES_IN_LEGEND_ORDER.length,
|
|
509
|
+
);
|
|
510
|
+
expect(DEVICE_ROLES_IN_LEGEND_ORDER.length).toBe(
|
|
511
|
+
Object.keys(DEVICE_ROLE_LABELS).length - 1,
|
|
512
|
+
);
|
|
513
|
+
});
|
|
514
|
+
});
|