473 lines
13 KiB
Elixir
473 lines
13 KiB
Elixir
defmodule Mix.Tasks.GenVendorModulesTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
import ExUnit.CaptureIO
|
|
|
|
alias Mix.Tasks.GenVendorModules
|
|
|
|
@moduletag :mix_task
|
|
|
|
setup do
|
|
# Save original directory
|
|
original_dir = File.cwd!()
|
|
|
|
# Create temporary directories for profiles and vendor modules
|
|
temp_dir = Path.join(System.tmp_dir!(), "gen_vendor_modules_#{System.unique_integer([:positive])}")
|
|
profiles_dir = Path.join([temp_dir, "priv", "profiles", "os_detection"])
|
|
vendors_dir = Path.join([temp_dir, "lib", "towerops", "snmp", "profiles", "vendors"])
|
|
File.mkdir_p!(profiles_dir)
|
|
File.mkdir_p!(vendors_dir)
|
|
|
|
on_exit(fn ->
|
|
File.cd!(original_dir)
|
|
File.rm_rf!(temp_dir)
|
|
end)
|
|
|
|
{:ok, temp_dir: temp_dir, profiles_dir: profiles_dir, vendors_dir: vendors_dir}
|
|
end
|
|
|
|
describe "run/1 with dry-run" do
|
|
test "reports vendors that would be generated in dry-run mode", %{
|
|
temp_dir: temp_dir,
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: _vendors_dir
|
|
} do
|
|
# Create profile YAML with group
|
|
File.write!(Path.join(profiles_dir, "mikrotik.yaml"), """
|
|
os: mikrotik
|
|
group: mikrotik
|
|
text: MikroTik
|
|
discovery:
|
|
- sysObjectID:
|
|
- .1.3.6.1.4.1.14988.
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run(["--dry-run"])
|
|
end)
|
|
|
|
assert output =~ "Found 1 YAML profiles"
|
|
assert output =~ "Grouped into 1 vendors"
|
|
assert output =~ "1 new vendors to generate"
|
|
assert output =~ "mikrotik.ex (Mikrotik)"
|
|
assert output =~ "Profiles: mikrotik"
|
|
assert output =~ "Enterprise OID: 1.3.6.1.4.1.14988"
|
|
end)
|
|
end
|
|
|
|
test "reports no work needed when all vendors exist", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
# Create profile YAML
|
|
File.write!(Path.join(profiles_dir, "mikrotik.yaml"), """
|
|
os: mikrotik
|
|
group: mikrotik
|
|
text: MikroTik
|
|
""")
|
|
|
|
# Create existing vendor module
|
|
File.write!(Path.join(vendors_dir, "mikrotik.ex"), """
|
|
defmodule Towerops.Snmp.Profiles.Vendors.Mikrotik do
|
|
def profile_names, do: ["mikrotik"]
|
|
end
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run(["--dry-run"])
|
|
end)
|
|
|
|
assert output =~ "0 new vendors to generate"
|
|
assert output =~ "No new vendors to generate!"
|
|
end)
|
|
end
|
|
end
|
|
|
|
describe "run/1 without dry-run" do
|
|
test "generates vendor module from profile with group", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "cisco-ios.yaml"), """
|
|
os: cisco-ios
|
|
group: cisco
|
|
text: Cisco IOS
|
|
discovery:
|
|
- sysObjectID:
|
|
- .1.3.6.1.4.1.9.
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
assert output =~ "Generated: lib/towerops/snmp/profiles/vendors/cisco.ex"
|
|
assert output =~ "Generated 1 vendor modules"
|
|
|
|
# Verify file was created
|
|
vendor_file = Path.join(vendors_dir, "cisco.ex")
|
|
assert File.exists?(vendor_file)
|
|
|
|
# Verify file content
|
|
content = File.read!(vendor_file)
|
|
assert content =~ "defmodule Towerops.Snmp.Profiles.Vendors.Cisco do"
|
|
assert content =~ "@moduledoc"
|
|
assert content =~ "SNMP profile for Cisco devices"
|
|
assert content =~ "@behaviour Towerops.Snmp.Profiles.Vendors.Vendor"
|
|
assert content =~ "def profile_names, do: [\"cisco-ios\"]"
|
|
assert content =~ "# Enterprise OID: 1.3.6.1.4.1.9"
|
|
assert content =~ "def detect_hardware(client_opts)"
|
|
assert content =~ "def wireless_oid_defs"
|
|
assert content =~ "def discover_wireless_sensors(client_opts)"
|
|
end)
|
|
end
|
|
|
|
test "generates vendor module from profile without group using filename inference", %{
|
|
temp_dir: temp_dir,
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "cisco_ios.yaml"), """
|
|
os: cisco-ios
|
|
text: Cisco IOS
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
# Should infer cisco from filename
|
|
assert output =~ "Generated: lib/towerops/snmp/profiles/vendors/cisco.ex"
|
|
|
|
vendor_file = Path.join(vendors_dir, "cisco.ex")
|
|
assert File.exists?(vendor_file)
|
|
end)
|
|
end
|
|
|
|
test "groups multiple profiles into single vendor module", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "cisco-ios.yaml"), """
|
|
os: cisco-ios
|
|
group: cisco
|
|
text: Cisco IOS
|
|
""")
|
|
|
|
File.write!(Path.join(profiles_dir, "cisco-nxos.yaml"), """
|
|
os: cisco-nxos
|
|
group: cisco
|
|
text: Cisco NX-OS
|
|
""")
|
|
|
|
File.write!(Path.join(profiles_dir, "cisco-asa.yaml"), """
|
|
os: cisco-asa
|
|
group: cisco
|
|
text: Cisco ASA
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
assert output =~ "Generated 1 vendor modules"
|
|
|
|
vendor_file = Path.join(vendors_dir, "cisco.ex")
|
|
content = File.read!(vendor_file)
|
|
|
|
# Should include all three profiles
|
|
assert content =~ ~s(["cisco-asa", "cisco-ios", "cisco-nxos"])
|
|
end)
|
|
end
|
|
|
|
test "generates modules for multiple vendors", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "mikrotik.yaml"), """
|
|
os: mikrotik
|
|
group: mikrotik
|
|
text: MikroTik
|
|
""")
|
|
|
|
File.write!(Path.join(profiles_dir, "ubnt.yaml"), """
|
|
os: ubnt
|
|
group: ubiquiti
|
|
text: Ubiquiti
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
assert output =~ "Generated 2 vendor modules"
|
|
assert File.exists?(Path.join(vendors_dir, "mikrotik.ex"))
|
|
assert File.exists?(Path.join(vendors_dir, "ubiquiti.ex"))
|
|
end)
|
|
end
|
|
|
|
test "generates module with specific group filter", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "mikrotik.yaml"), """
|
|
os: mikrotik
|
|
group: mikrotik
|
|
text: MikroTik
|
|
""")
|
|
|
|
File.write!(Path.join(profiles_dir, "cisco.yaml"), """
|
|
os: cisco
|
|
group: cisco
|
|
text: Cisco
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run(["--group", "mikrotik"])
|
|
end)
|
|
|
|
assert output =~ "Generated 1 vendor modules"
|
|
assert File.exists?(Path.join(vendors_dir, "mikrotik.ex"))
|
|
refute File.exists?(Path.join(vendors_dir, "cisco.ex"))
|
|
end)
|
|
end
|
|
|
|
test "handles vendor names starting with numbers", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "4rf.yaml"), """
|
|
os: 4rf
|
|
group: 4rf
|
|
text: 4RF
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
# Should prefix with "n" for filename and "N" for module
|
|
assert output =~ "Generated: lib/towerops/snmp/profiles/vendors/n4rf.ex"
|
|
|
|
vendor_file = Path.join(vendors_dir, "n4rf.ex")
|
|
assert File.exists?(vendor_file)
|
|
|
|
content = File.read!(vendor_file)
|
|
assert content =~ "defmodule Towerops.Snmp.Profiles.Vendors.N4rf do"
|
|
end)
|
|
end
|
|
|
|
test "handles vendor names with special characters", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "device.yaml"), """
|
|
os: test-device
|
|
group: hp-aruba
|
|
text: HP Aruba
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
# Should convert to snake_case for filename
|
|
assert output =~ "Generated: lib/towerops/snmp/profiles/vendors/hp_aruba.ex"
|
|
|
|
vendor_file = Path.join(vendors_dir, "hp_aruba.ex")
|
|
assert File.exists?(vendor_file)
|
|
|
|
content = File.read!(vendor_file)
|
|
assert content =~ "defmodule Towerops.Snmp.Profiles.Vendors.HpAruba do"
|
|
end)
|
|
end
|
|
|
|
test "extracts enterprise OID from sysObjectID", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "test.yaml"), """
|
|
os: test
|
|
group: testvendor
|
|
text: Test Vendor
|
|
discovery:
|
|
- sysObjectID:
|
|
- .1.3.6.1.4.1.12345.1.2.3
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
vendor_file = Path.join(vendors_dir, "testvendor.ex")
|
|
content = File.read!(vendor_file)
|
|
|
|
assert content =~ "# Enterprise OID: 1.3.6.1.4.1.12345"
|
|
end)
|
|
end
|
|
|
|
test "handles profile without enterprise OID", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "generic.yaml"), """
|
|
os: generic
|
|
group: generic
|
|
text: Generic Device
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
vendor_file = Path.join(vendors_dir, "generic.ex")
|
|
content = File.read!(vendor_file)
|
|
|
|
assert content =~ "# Enterprise OID: unknown - uses standard MIB OIDs"
|
|
end)
|
|
end
|
|
|
|
test "includes standard CPU sensor in wireless_oid_defs", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "test.yaml"), """
|
|
os: test
|
|
group: test
|
|
text: Test
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
vendor_file = Path.join(vendors_dir, "test.ex")
|
|
content = File.read!(vendor_file)
|
|
|
|
assert content =~ "wireless_oid_defs do"
|
|
assert content =~ "1.3.6.1.2.1.25.3.3.1.2.1"
|
|
assert content =~ "CPU Load"
|
|
assert content =~ "sensor_type: \"load\""
|
|
end)
|
|
end
|
|
|
|
test "skips profiles that cannot be grouped to a vendor", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: _vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "unknown_device.yaml"), """
|
|
os: unknown-device
|
|
text: Unknown Device
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
assert output =~ "0 new vendors to generate"
|
|
assert output =~ "No new vendors to generate!"
|
|
end)
|
|
end
|
|
|
|
test "includes instructions after generation", %{
|
|
profiles_dir: profiles_dir,
|
|
vendors_dir: _vendors_dir,
|
|
temp_dir: temp_dir
|
|
} do
|
|
File.write!(Path.join(profiles_dir, "test.yaml"), """
|
|
os: test
|
|
group: test
|
|
text: Test
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
assert output =~ "Run `mix compile` to check for errors"
|
|
assert output =~ "Run `mix test` to verify"
|
|
assert output =~ "Add aliases to registry.ex"
|
|
assert output =~ "Add vendors to @vendors list in registry.ex"
|
|
assert output =~ "Write tests for each vendor"
|
|
assert output =~ "Add vendor-specific OIDs if available"
|
|
end)
|
|
end
|
|
end
|
|
|
|
describe "run/1 error handling" do
|
|
test "skips profiles with invalid YAML", %{profiles_dir: profiles_dir, vendors_dir: vendors_dir, temp_dir: temp_dir} do
|
|
File.write!(Path.join(profiles_dir, "valid.yaml"), """
|
|
os: valid
|
|
group: valid
|
|
text: Valid
|
|
""")
|
|
|
|
File.write!(Path.join(profiles_dir, "invalid.yaml"), """
|
|
this is: [invalid yaml that won't parse
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
# Should still generate valid vendor
|
|
assert output =~ "Generated 1 vendor modules"
|
|
assert File.exists?(Path.join(vendors_dir, "valid.ex"))
|
|
end)
|
|
end
|
|
|
|
test "skips profiles without os field", %{profiles_dir: profiles_dir, vendors_dir: _vendors_dir, temp_dir: temp_dir} do
|
|
File.write!(Path.join(profiles_dir, "no_os.yaml"), """
|
|
group: test
|
|
text: Test
|
|
""")
|
|
|
|
File.cd!(temp_dir, fn ->
|
|
output =
|
|
capture_io(fn ->
|
|
GenVendorModules.run([])
|
|
end)
|
|
|
|
assert output =~ "0 new vendors to generate"
|
|
end)
|
|
end
|
|
end
|
|
end
|