towerops/test/snmpkit/snmp_mgr/mib_extraction_test.exs

137 lines
4.4 KiB
Elixir

defmodule SnmpKit.SnmpMgr.MIB.ExtractionTest do
use ExUnit.Case, async: true
alias SnmpKit.SnmpLib.MIB.Parser
# Test the actual extract_mib_mappings private function behavior
@tag :yecc_required
test "extract_mib_mappings resolves UBNT-MIB OIDs" do
mib_path = Path.join(:code.priv_dir(:towerops), "mibs/ubnt/UBNT-MIB")
{:ok, mib_content} = File.read(mib_path)
{:ok, parsed} = Parser.parse(mib_content)
# Simulate state with standard MIBs
mock_state = %{
name_to_oid: %{
"enterprises" => [1, 3, 6, 1, 4, 1],
"mib-2" => [1, 3, 6, 1, 2, 1],
"system" => [1, 3, 6, 1, 2, 1, 1]
}
}
# Call the extraction logic manually
result = extract_mib_mappings_manually(parsed, mock_state, mib_content)
# Check that new OIDs were resolved
assert map_size(result.name_to_oid) > map_size(mock_state.name_to_oid),
"Should have more OIDs after extraction"
# Check specific UBNT OIDs
assert Map.has_key?(result.name_to_oid, "ubnt")
assert Map.has_key?(result.name_to_oid, "ubntMIB")
assert Map.has_key?(result.name_to_oid, "ubntAFLTU")
# Verify the resolved values
assert result.name_to_oid["ubnt"] == [1, 3, 6, 1, 4, 1, 41_112]
assert result.name_to_oid["ubntMIB"] == [1, 3, 6, 1, 4, 1, 41_112, 1]
assert result.name_to_oid["ubntAFLTU"] == [1, 3, 6, 1, 4, 1, 41_112, 1, 10]
end
# Manually implement the extraction logic for testing
defp extract_mib_mappings_manually(parsed, state, mib_content) do
definitions = Map.get(parsed, :definitions, [])
# Build OID definitions
oid_defs =
definitions
|> Enum.filter(&(Map.get(&1, :__type__) in [:object_type, :object_identifier, :module_identity]))
|> Enum.reduce(%{}, fn def, acc ->
name = Map.get(def, :name)
oid = Map.get(def, :oid)
parent = Map.get(def, :parent)
sub_index = Map.get(def, :sub_index)
cond do
name && oid -> Map.put(acc, name, oid)
name && parent && sub_index -> Map.put(acc, name, {String.to_atom(parent), sub_index})
name && parent -> Map.put(acc, name, {String.to_atom(parent), nil})
true -> acc
end
end)
# Fix missing indices
oid_defs = fix_missing_indices(oid_defs, mib_content)
# Resolve OIDs
name_to_oid = resolve_all_oids(oid_defs, state.name_to_oid)
%{name_to_oid: name_to_oid, name_to_meta: %{}}
end
defp fix_missing_indices(oid_defs, mib_content) do
missing =
Enum.filter(oid_defs, fn
{_, {_, nil}} -> true
_ -> false
end)
Enum.reduce(missing, oid_defs, fn {name, _}, acc ->
regex = ~r/#{name}\s+OBJECT\s+IDENTIFIER\s+::=\s+\{\s+(\w+)\s+(\d+)\s+\}/
case Regex.run(regex, mib_content) do
[_, parent, index_str] ->
index = String.to_integer(index_str)
Map.put(acc, name, {String.to_atom(parent), <<index>>})
_ ->
acc
end
end)
end
defp resolve_all_oids(oid_defs, existing_oids) do
resolve_iteratively(oid_defs, existing_oids, 10)
end
defp resolve_iteratively(_oid_defs, resolved, 0), do: resolved
defp resolve_iteratively(oid_defs, resolved, iterations_left) do
{new_resolved, remaining} =
Enum.reduce(oid_defs, {resolved, %{}}, fn {name, oid_def}, {res_acc, rem_acc} ->
case resolve_single(oid_def, res_acc) do
{:ok, oid_list} -> {Map.put(res_acc, name, oid_list), rem_acc}
{:error, _} -> {res_acc, Map.put(rem_acc, name, oid_def)}
end
end)
if map_size(remaining) == map_size(oid_defs) do
new_resolved
else
resolve_iteratively(remaining, new_resolved, iterations_left - 1)
end
end
defp resolve_single(oid_list, _) when is_list(oid_list) do
if Enum.all?(oid_list, &is_integer/1), do: {:ok, oid_list}, else: {:error, :not_resolved}
end
defp resolve_single({parent_name, index_binary}, resolved) when is_atom(parent_name) and is_binary(index_binary) do
parent_str = Atom.to_string(parent_name)
case Map.get(resolved, parent_str) do
parent_oid when is_list(parent_oid) ->
index =
case String.to_charlist(index_binary) do
[code_point | _] -> code_point
[] -> :binary.decode_unsigned(index_binary)
end
{:ok, parent_oid ++ [index]}
_ ->
{:error, :parent_not_resolved}
end
end
defp resolve_single(_, _), do: {:error, :invalid_format}
end