%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(snmpc).
%% API
-export([compile/1, compile/2, compile/3,
mib_to_hrl/1, mib_to_hrl/3,
is_consistent/1]).
%% Debug
-export([look_at/1]).
%% Internal Exports
-export([init/3]).
-include_lib("stdlib/include/erl_compile.hrl").
-include("snmp_types.hrl").
-include("snmpc.hrl").
-include("snmpc_lib.hrl").
-record(dldata, {deprecated, relaxed_row_name_assign_check}).
look_at(Mib) ->
io:format("~p ~n", [snmpc_lib:look_at(Mib)]).
%%-----------------------------------------------------------------
%% Misc compiler stuff
%%-----------------------------------------------------------------
is_consistent(Filenames) ->
snmpc_lib:is_consistent(Filenames).
mib_to_hrl(MibName) ->
snmpc_mib_to_hrl:convert(MibName).
mib_to_hrl(MibName, HrlFile, Opts) ->
snmpc_mib_to_hrl:compile(MibName, HrlFile, Opts).
%%%-----------------------------------------------------------------
%%% Interface for erl_compile.
%%%-----------------------------------------------------------------
compile(Input, _Output, Options) ->
case compile(Input, make_options(Options)) of
{ok, _} ->
ok;
{error, Reason} ->
io:format("~p", [Reason]),
error
end.
%% Converts generic options to format expected by compile/2
make_options(#options{includes = Incs,
outdir = Outdir,
warning = Warning,
specific = Spec}) ->
OutdirOpt = {outdir, Outdir},
WarningOpt =
case Warning of
0 -> {warnings, false};
_ -> {warnings, true}
end,
IncludeOpt =
{i, case Incs of
[] ->
[""];
_ ->
lists:map(fun(Dir) -> Dir++"/" end, Incs)
end},
[WarningOpt, OutdirOpt, IncludeOpt | Spec].
%% Returns: {ok, File}|{error, Reason}
compile([AtomFilename]) when is_atom(AtomFilename) ->
compile(atom_to_list(AtomFilename), []), % from cmd line
halt();
compile(FileName) ->
compile(FileName, []).
%%----------------------------------------------------------------------
%% Options:
%% {deprecated, bool()} true
%% {group_check, bool()} true
%% {db, volatile|persistent|mnesia} volatile
%% {i, [import_dir_string()]} ["./"]
%% {il, [import_lib_dir_string()]} []
%% {warnings, bool()} true
%% {outdir, string()} "./"
%% description
%% reference
%% imports
%% module_identity
%% {module, string()}
%% no_defs
%% relaxed_row_name_assign_check
%% (hidden) {verbosity, trace|debug|log|info|silence} silence
%% (hidden) version
%% (hidden) options
%%----------------------------------------------------------------------
compile(FileName, Options) when is_list(FileName) ->
true = snmpc_misc:is_string(FileName),
DefOpts = [{deprecated, true},
{group_check, true},
{i, ["./"]},
{db, volatile},
{warnings, true},
{outdir, "./"},
{il, []}],
Opts = update_options(DefOpts, Options),
case check_options(Opts) of
ok ->
maybe_display_version(Opts),
maybe_display_options(Opts),
Pid = spawn_link(?MODULE,init,[self(),FileName,Opts]),
receive
{compile_result,R} -> R;
{'EXIT',Pid, Reason} when Reason =/= normal ->
exit(Reason)
end;
{error, Reason} ->
{error, Reason}
end.
maybe_display_version(Opts) ->
case lists:member(version, Opts) of
true ->
Vsn = (catch get_version()),
io:format("version: ~s~n", [Vsn]);
false ->
ok
end.
get_version() ->
MI = ?MODULE:module_info(),
Attr = get_info(attributes, MI),
Vsn = get_info(app_vsn, Attr),
Comp = get_info(compile, MI),
Time = get_info(time, Comp),
{Year, Month, Day, Hour, Min, Sec} = Time,
io_lib:format("~s [~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w]",
[Vsn, Year, Month, Day, Hour, Min, Sec]).
maybe_display_options(Opts) ->
case lists:member(options, Opts) of
true ->
{F, A} = get_options(Opts, [], []),
io:format("options: " ++ F ++ "~n", A);
false ->
ok
end.
get_options([], Formats, Args) ->
{lists:concat(lists:reverse(Formats)), lists:reverse(Args)};
get_options([{deprecated, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n deprecated: ~w"|Formats], [Val|Args]);
get_options([{group_check, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n group_check: ~w"|Formats], [Val|Args]);
get_options([{db, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n db: ~w"|Formats], [Val|Args]);
get_options([{i, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n i: ~p"|Formats], [Val|Args]);
get_options([{il, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n il: ~p"|Formats], [Val|Args]);
get_options([{outdir, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n outdir: ~s"|Formats], [Val|Args]);
get_options([{description, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n description: ~w"|Formats], [Val|Args]);
get_options([description|Opts], Formats, Args) ->
get_options(Opts, ["~n description"|Formats], Args);
get_options([{reference, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n reference: ~w"|Formats], [Val|Args]);
get_options([reference|Opts], Formats, Args) ->
get_options(Opts, ["~n reference"|Formats], Args);
get_options([{warnings, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n warnings: ~w"|Formats], [Val|Args]);
get_options([{verbosity, Val}|Opts], Formats, Args) ->
get_options(Opts, ["~n verbosity: ~w"|Formats], [Val|Args]);
get_options([imports|Opts], Formats, Args) ->
get_options(Opts, ["~n imports"|Formats], Args);
get_options([module_identity|Opts], Formats, Args) ->
get_options(Opts, ["~n module_identity"|Formats], Args);
get_options([relaxed_row_name_assign_check|Opts], Formats, Args) ->
get_options(Opts, ["~n relaxed_row_name_assign_check"|Formats], Args);
get_options([_|Opts], Formats, Args) ->
get_options(Opts, Formats, Args).
get_info(Key, Info) ->
case lists:keysearch(Key, 1, Info) of
{value, {Key, Val}} ->
Val;
false ->
throw("undefined")
end.
% p(F, A) ->
% io:format("DBG: " ++ F ++ "~n", A).
update_options([], Options) ->
Options;
update_options([{Key,DefVal}|DefOpts], Options) ->
case snmpc_misc:assq(Key, Options) of
false ->
update_options(DefOpts, [{Key,DefVal}|Options]);
{value, Val} when Key =:= i ->
Options1 =
lists:keyreplace(Key, 1, Options, {Key, Val++DefVal}),
update_options(DefOpts, Options1);
{value, Val} when Key =:= il ->
Options1 =
lists:keyreplace(Key, 1, Options, {Key, Val++DefVal}),
update_options(DefOpts, Options1);
{value, DefVal} -> %% Same value, no need to update
update_options(DefOpts, Options);
{value, Val} -> %% New value, so update
Options1 =
lists:keyreplace(Key, 1, Options, {Key, Val}),
update_options(DefOpts, Options1)
end.
check_options([]) -> ok;
check_options([no_symbolic_info|T]) -> check_options(T);
check_options([{outdir, Str} | T]) when is_list(Str) ->
check_options(T);
check_options([{debug, Atom} | T]) when is_atom(Atom) ->
check_options(T);
check_options([{deprecated, Atom} | T]) when is_atom(Atom) ->
check_options(T);
check_options([{group_check, Atom} | T]) when is_atom(Atom) ->
check_options(T);
check_options([{warnings, Bool} | T]) ->
check_bool(warnings, Bool),
check_options(T);
check_options([{db, volatile} | T]) ->
check_options(T);
check_options([{db, persistent} | T]) ->
check_options(T);
check_options([{db, mnesia} | T]) ->
check_options(T);
check_options([{i, [Str|_]} | T]) when is_list(Str) ->
check_options(T);
check_options([{il, []} | T]) ->
check_options(T);
check_options([{il, [Str|_]} | T]) when is_list(Str) ->
check_options(T);
check_options([{description, Bool}| T]) ->
check_bool(description, Bool),
check_options(T);
check_options([description| T]) -> %% same as {description, true}
check_options(T);
check_options([{reference, Bool}| T]) ->
check_bool(reference, Bool),
check_options(T);
check_options([reference| T]) -> %% same as {reference, true}
check_options(T);
check_options([{verbosity, V} | T]) when is_atom(V) ->
snmpc_lib:vvalidate(V),
check_options(T);
check_options([version| T]) ->
check_options(T);
check_options([options| T]) ->
check_options(T);
check_options([imports| T]) ->
check_options(T);
check_options([module_identity| T]) ->
check_options(T);
check_options([relaxed_row_name_assign_check| T]) ->
check_options(T);
check_options([{module, M} | T]) when is_atom(M) ->
check_options(T);
check_options([no_defs| T]) ->
check_options(T);
check_options([Opt|_]) ->
{error, {invalid_option, Opt}}.
check_bool(_Key, Bool) when (Bool =:= true) orelse (Bool =:= false) ->
ok;
check_bool(Key, Val) ->
{error, {invalid_option, {Key, Val}}}.
get_group_check(Options) ->
snmpc_lib:key1search(group_check, Options, true).
get_deprecated(Options) ->
snmpc_lib:key1search(deprecated, Options, true).
get_description(Options) ->
get_bool_option(description, Options).
get_reference(Options) ->
get_bool_option(reference, Options).
get_relaxed_row_name_assign_check(Options) ->
lists:member(relaxed_row_name_assign_check, Options).
get_bool_option(Option, Options) ->
case lists:member(Option, Options) of
false ->
snmpc_lib:key1search(Option, Options, false);
true ->
true
end.
make_description(Message) ->
case get(description) of
true ->
Message;
_ ->
undefined
end.
make_reference(undefined) ->
[];
make_reference(Reference) ->
case get(reference) of
true ->
[{reference, Reference}];
_ ->
[]
end.
%%----------------------------------------------------------------------
%% verbosity stuff
%%----------------------------------------------------------------------
%% Verbosity level is selected from three (historical reasons)
%% options: warnings, debug and verbosity
%% - If warnings is true, then verbosity is _atleast_ warning
%% (even if the verbosity flag is set to silence)
%% - If debug is true, the verbosity is _atleast_ log
%% - Otherwise, verbosity is used as is.
get_verbosity(Options) ->
WarningsSeverity =
case snmpc_lib:key1search(warnings, Options) of
true ->
warning;
_ ->
silence
end,
case snmpc_lib:key1search(verbosity, Options) of
undefined ->
%% Backward compatible: If not defined then try debug and convert
case snmpc_lib:key1search(debug, Options, false) of
true ->
log;
false ->
WarningsSeverity
end;
silence ->
WarningsSeverity;
Verbosity ->
Verbosity
end.
%%----------------------------------------------------------------------
%% The compile process.
%%----------------------------------------------------------------------
init(From, MibFileName, Options) ->
{A,B,C} = now(),
random:seed(A,B,C),
put(options, Options),
put(verbosity, get_verbosity(Options)),
put(description, get_description(Options)),
put(reference, get_reference(Options)),
File = filename:rootname(MibFileName, ".mib"),
put(filename, filename:basename(File ++ ".mib")),
R = case catch c_impl(File) of
{ok, OutFile} -> {ok, OutFile};
{'EXIT',error} -> {error, compilation_failed};
Error -> exit(Error)
end,
From ! {compile_result, R}.
c_impl(File) ->
{ok, PData} = parse(File),
?vtrace("Syntax analysis:"
"~n PData: ~p", [PData]),
MibName = compile_parsed_data(PData),
?vtrace("Compiler output:"
"~n CDATA: ~p", [get(cdata)]),
save(File, MibName, get(options)).
compile_parsed_data(#pdata{mib_name = MibName,
imports = Imports,
defs = Definitions}) ->
snmpc_lib:import(Imports),
update_imports(Imports),
Opts = get(options),
Deprecated = get_deprecated(Opts),
RelChk = get_relaxed_row_name_assign_check(Opts),
Data = #dldata{deprecated = Deprecated,
relaxed_row_name_assign_check = RelChk},
definitions_loop(Definitions, Data),
MibName.
update_imports(Imports) ->
case lists:member(imports, get(options)) of
true ->
IMPs = do_update_imports(Imports, []),
CDATA = get(cdata),
put(cdata, CDATA#cdata{imports = IMPs});
false ->
ok
end.
do_update_imports([], Acc) ->
lists:reverse(Acc);
do_update_imports([{{Mib, ImportsFromMib0},_Line}|Imports], Acc) ->
ImportsFromMib = [Name || {_, Name} <- ImportsFromMib0],
Import = {Mib, ImportsFromMib},
do_update_imports(Imports, [Import|Acc]).
update_status(Name, Status) ->
#cdata{status_ets = Ets} = get(cdata),
ets:insert(Ets, {Name, Status}).
%% A deprecated object
definitions_loop([{#mc_object_type{name = ObjName, status = deprecated},
Line}|T],
#dldata{deprecated = false} = Data) ->
%% May be implemented but the compiler chooses not to.
?vinfo2("object_type ~w is deprecated => ignored", [ObjName], Line),
update_status(ObjName, deprecated),
definitions_loop(T, Data);
%% A obsolete object
definitions_loop([{#mc_object_type{name = ObjName, status = obsolete},
Line}|T],
Data) ->
?vlog2("object_type ~w is obsolete => ignored", [ObjName], Line),
%% No need to implement a obsolete object
update_status(ObjName, obsolete),
ensure_macro_imported('OBJECT-TYPE', Line),
definitions_loop(T, Data);
%% Defining a table
definitions_loop([{#mc_object_type{name = NameOfTable,
syntax = {{sequence_of, SeqName}, _},
max_access = Taccess,
kind = Kind,
status = Tstatus,
description = Desc1,
units = Tunits,
reference = Ref,
name_assign = Tindex},
Tline},
{#mc_object_type{name = NameOfEntry,
syntax = {{type, SeqName}, TEline},
max_access = 'not-accessible',
kind = {table_entry, IndexingInfo},
status = Estatus,
description = Desc2,
units = Eunits,
name_assign = {NameOfTable,[1]}},
Eline},
{#mc_sequence{name = SeqName,
fields = FieldList},
Sline}|ColsEtc],
Data) ->
?vlog("defloop -> "
"[object_type(sequence_of),object_type(type,[1]),sequence]:"
"~n NameOfTable: ~p"
"~n SeqName: ~p"
"~n Taccess: ~p"
"~n Kind: ~p"
"~n Tstatus: ~p"
"~n Tindex: ~p"
"~n Tunits: ~p"
"~n Tline: ~p"
"~n NameOfEntry: ~p"
"~n TEline: ~p"
"~n IndexingInfo: ~p"
"~n Estatus: ~p"
"~n Eunits: ~p"
"~n Ref: ~p"
"~n Eline: ~p"
"~n FieldList: ~p"
"~n Sline: ~p",
[NameOfTable,SeqName,Taccess,Kind,Tstatus,
Tindex,Tunits,Tline,
NameOfEntry,TEline,IndexingInfo,Estatus,Eunits,Ref,Eline,
FieldList,Sline]),
update_status(NameOfTable, Tstatus),
update_status(NameOfEntry, Estatus),
update_status(SeqName, undefined),
ensure_macro_imported('OBJECT-TYPE', Tline),
test_table(NameOfTable,Taccess,Kind,Tindex,Tline),
{Tfather,Tsubindex} = Tindex,
snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex),
Description1 = make_description(Desc1),
TableME = #me{aliasname = NameOfTable,
entrytype = table,
access = 'not-accessible',
description = Description1,
units = Tunits},
snmpc_lib:register_oid(TEline,NameOfEntry,NameOfTable,[1]),
Description2 = make_description(Desc2),
TableEntryME = #me{aliasname = NameOfEntry,
entrytype = table_entry,
assocList = [{table_entry_with_sequence, SeqName}],
access = 'not-accessible',
description = Description2,
units = Eunits},
{ColMEs, RestObjs} =
define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []),
TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable,
IndexingInfo, ColMEs),
snmpc_lib:add_cdata(#cdata.mes,
[TableEntryME,
TableME#me{assocList=[{table_info,
TableInfo} | make_reference(Ref)]} |
ColMEs]),
definitions_loop(RestObjs, Data);
definitions_loop([{#mc_object_type{name = NameOfTable,
syntax = {{sequence_of, SeqName}, _},
max_access = Taccess,
kind = Kind,
status = Tstatus,
description = Desc1,
units = Tunits,
reference = Ref,
name_assign = Tindex},
Tline},
{#mc_object_type{name = NameOfEntry,
syntax = {{type, SeqName}, TEline},
max_access = 'not-accessible',
kind = {table_entry, IndexingInfo},
status = Estatus,
description = Desc2,
units = Eunits,
name_assign = {NameOfTable,[Idx]} = BadOID},
Eline},
{#mc_sequence{name = SeqName,
fields = FieldList},
Sline}|ColsEtc],
#dldata{relaxed_row_name_assign_check = true} = Data)
when is_integer(Idx) andalso (Idx > 1) ->
?vlog("defloop -> "
"[object_type(sequence_of),object_type(type,[~w]),sequence]:"
"~n NameOfTable: ~p"
"~n SeqName: ~p"
"~n Taccess: ~p"
"~n Kind: ~p"
"~n Tstatus: ~p"
"~n Tindex: ~p"
"~n Tunits: ~p"
"~n Tline: ~p"
"~n NameOfEntry: ~p"
"~n TEline: ~p"
"~n IndexingInfo: ~p"
"~n Estatus: ~p"
"~n Eunits: ~p"
"~n Ref: ~p"
"~n Eline: ~p"
"~n FieldList: ~p"
"~n Sline: ~p",
[Idx,
NameOfTable,SeqName,Taccess,Kind,Tstatus,
Tindex,Tunits,Tline,
NameOfEntry,TEline,IndexingInfo,Estatus,Eunits,Ref,Eline,
FieldList,Sline]),
update_status(NameOfTable, Tstatus),
update_status(NameOfEntry, Estatus),
update_status(SeqName, undefined),
ensure_macro_imported('OBJECT-TYPE', Tline),
?vwarning2("Bad TableEntry OID definition (~w)",
[BadOID], Eline),
test_table(NameOfTable,Taccess,Kind,Tindex,Tline),
{Tfather,Tsubindex} = Tindex,
snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex),
Description1 = make_description(Desc1),
TableME = #me{aliasname = NameOfTable,
entrytype = table,
access = 'not-accessible',
description = Description1,
units = Tunits},
snmpc_lib:register_oid(TEline,NameOfEntry,NameOfTable,[Idx]),
Description2 = make_description(Desc2),
TableEntryME = #me{aliasname = NameOfEntry,
entrytype = table_entry,
assocList = [{table_entry_with_sequence, SeqName}],
access = 'not-accessible',
description = Description2,
units = Eunits},
{ColMEs, RestObjs} =
define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []),
TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable,
IndexingInfo, ColMEs),
snmpc_lib:add_cdata(#cdata.mes,
[TableEntryME,
TableME#me{assocList=[{table_info,
TableInfo} | make_reference(Ref)]} |
ColMEs]),
definitions_loop(RestObjs, Data);
definitions_loop([{#mc_object_type{name = NameOfTable,
syntax = {{sequence_of, SeqName},_},
max_access = Taccess,
kind = Kind,
status = Tstatus,
description = Desc1,
units = Tunits,
reference = Ref,
name_assign = Tindex}, Tline},
{#mc_object_type{name = NameOfEntry,
syntax = {{type, SeqName},_},
max_access = 'not-accessible',
kind = {table_entry,IndexingInfo},
status = Estatus,
description = Desc2,
units = Eunits,
name_assign = BadOID}, Eline},
{#mc_sequence{name = SeqName,
fields = FieldList}, Sline}|ColsEtc],
Data) ->
?vlog("defloop -> "
"[object_type(sequence_of),object_type(type),sequence(fieldList)]:"
"~n NameOfTable: ~p"
"~n SeqName: ~p"
"~n Taccess: ~p"
"~n Kind: ~p"
"~n Tstatus: ~p"
"~n Tindex: ~p"
"~n Tunits: ~p"
"~n Tline: ~p"
"~n NameOfEntry: ~p"
"~n IndexingInfo: ~p"
"~n Estatus: ~p"
"~n BadOID: ~p"
"~n Eunits: ~p"
"~n Ref: ~p"
"~n Eline: ~p"
"~n FieldList: ~p"
"~n Sline: ~p",
[NameOfTable,SeqName,Taccess,Kind,Tstatus,
Tindex,Tunits,Tline,
NameOfEntry,IndexingInfo,Estatus,BadOID,Eunits,Ref,Eline,
FieldList,Sline]),
update_status(NameOfTable, Tstatus),
update_status(NameOfEntry, Estatus),
update_status(SeqName, undefined),
ensure_macro_imported('OBJECT-TYPE', Tline),
snmpc_lib:print_error("Bad TableEntry OID definition (~w)",
[BadOID],Eline),
test_table(NameOfTable,Taccess,Kind,Tindex,Tline),
{Tfather,Tsubindex} = Tindex,
snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex),
Description1 = make_description(Desc1),
TableME = #me{aliasname = NameOfTable,
entrytype = table,
access = 'not-accessible',
description = Description1,
units = Tunits},
Description2 = make_description(Desc2),
TableEntryME = #me{aliasname = NameOfEntry,
entrytype = table_entry,
access = 'not-accessible',
assocList = [{table_entry_with_sequence,SeqName}],
description = Description2,
units = Eunits},
{ColMEs, RestObjs} =
define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []),
TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable,
IndexingInfo, ColMEs),
snmpc_lib:add_cdata(#cdata.mes,
[TableEntryME,
TableME#me{assocList=[{table_info,
TableInfo} | make_reference(Ref)]} |
ColMEs]),
definitions_loop(RestObjs, Data);
definitions_loop([{#mc_new_type{name = NewTypeName,
macro = Macro,
syntax = OldType,
display_hint = DisplayHint},Line}|T],
Data) ->
?vlog2("defloop -> new_type:"
"~n Macro: ~p"
"~n NewTypeName: ~p"
"~n OldType: ~p"
"~n DisplayHint: ~p",
[Macro, NewTypeName, OldType, DisplayHint], Line),
ensure_macro_imported(Macro,Line),
Types = (get(cdata))#cdata.asn1_types,
case lists:keysearch(NewTypeName, #asn1_type.aliasname, Types) of
{value,_} ->
snmpc_lib:print_error("Type ~w already defined.",
[NewTypeName],Line);
false ->
%% NameOfOldType = element(2,OldType),
ASN1 = snmpc_lib:make_ASN1type(OldType),
snmpc_lib:add_cdata(#cdata.asn1_types,
[ASN1#asn1_type{aliasname = NewTypeName,
imported = false,
display_hint = DisplayHint}])
end,
definitions_loop(T, Data);
%% Plain variable
definitions_loop([{#mc_object_type{name = NewVarName,
syntax = Type,
max_access = Access,
kind = {variable, DefVal},
status = Status,
description = Desc1,
units = Units,
name_assign = {Parent,SubIndex}},Line} |T],
Data) ->
?vlog2("defloop -> object_type (variable):"
"~n NewVarName: ~p"
"~n Type: ~p"
"~n Access: ~p"
"~n DefVal: ~p"
"~n Status: ~p"
"~n Units: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[NewVarName, Type, Access, DefVal,
Status, Units, Parent, SubIndex], Line),
update_status(NewVarName, Status),
snmpc_lib:test_father(Parent, NewVarName, SubIndex, Line),
ASN1type = snmpc_lib:make_ASN1type(Type),
snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex),
Description1 = make_description(Desc1),
NewME = #me{aliasname = NewVarName,
asn1_type = ASN1type,
entrytype = variable,
access = Access,
description = Description1,
units = Units,
assocList = DefVal},
NewME2 = snmpc_lib:resolve_defval(NewME),
%% hmm, should this be done in resolve_defval?
VI = snmpc_lib:make_variable_info(NewME2),
snmpc_lib:add_cdata(#cdata.mes,
[NewME2#me{assocList = [{variable_info, VI}]}]),
definitions_loop(T, Data);
definitions_loop([{#mc_module_identity{name = NewVarName,
last_updated = LU,
organization = Org,
contact_info = CI,
description = Desc,
revisions = Revs0,
name_assign = {Parent, SubIndex}},
Line}|T],
Data) ->
?vlog2("defloop -> module-identity: "
"~n NewVarName: ~p"
"~n LU: ~p"
"~n Org: ~p"
"~n CI: ~p"
"~n Desc: ~p"
"~n Revs0: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[NewVarName, LU, Org, CI, Desc, Revs0, Parent, SubIndex], Line),
ensure_macro_imported('MODULE-IDENTITY', Line),
snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex),
Revs = [{R,D}||#mc_revision{revision = R,description = D} <- Revs0],
MI = #module_identity{last_updated = LU,
organization = Org,
contact_info = CI,
description = Desc,
revisions = Revs},
CDATA = get(cdata),
put(cdata, CDATA#cdata{module_identity = MI}),
snmpc_lib:add_cdata(
#cdata.mes,
[snmpc_lib:makeInternalNode2(false, NewVarName)]),
definitions_loop(T, Data);
definitions_loop([{#mc_internal{name = NewVarName,
macro = Macro,
parent = Parent,
sub_index = SubIndex},Line}|T],
Data) ->
?vlog2("defloop -> internal:"
"~n NewVarName: ~p"
"~n Macro: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[NewVarName, Macro, Parent, SubIndex], Line),
ensure_macro_imported(Macro, Line),
snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex),
snmpc_lib:add_cdata(
#cdata.mes,
[snmpc_lib:makeInternalNode2(false, NewVarName)]),
definitions_loop(T, Data);
%% A trap message
definitions_loop([{#mc_trap{name = TrapName,
enterprise = EnterPrise,
vars = Variables,
description = Desc1,
num = SpecificCode}, Line}|T],
Data) ->
?vlog2("defloop -> trap:"
"~n TrapName: ~p"
"~n EnterPrise: ~p"
"~n Variables: ~p"
"~n SpecificCode: ~p",
[TrapName, EnterPrise, Variables, SpecificCode], Line),
update_status(TrapName, undefined),
CDATA = get(cdata),
snmpc_lib:check_trap_name(EnterPrise, Line, CDATA#cdata.mes),
Descriptions = make_description(Desc1),
Trap = #trap{trapname = TrapName,
enterpriseoid = EnterPrise,
specificcode = SpecificCode,
%% oidobjects: Store Variables temporary here.
%% This will be replaced later in the
%% get_final_mib function by a call to
%% the update_trap_objects function.
oidobjects = Variables,
description = Descriptions},
lists:foreach(fun(Trap2) -> snmpc_lib:check_trap(Trap2, Trap, Line) end,
CDATA#cdata.traps),
snmpc_lib:add_cdata(#cdata.traps, [Trap]),
definitions_loop(T, Data);
definitions_loop([{#mc_object_type{name = NameOfEntry,
syntax = Type,
max_access = Eaccess,
kind = {table_entry, Index},
status = Estatus,
name_assign = SubIndex},Eline}|T],
Data) ->
?vlog("defloop -> object_type (table_entry):"
"~n NameOfEntry: ~p"
"~n Type: ~p"
"~n Eaccess: ~p"
"~n Index: ~p"
"~n Estatus: ~p"
"~n SubIndex: ~p"
"~n SubIndex: ~p"
"~n Eline: ~p",
[NameOfEntry, Type, Eaccess, Index, Estatus, SubIndex, Eline]),
update_status(NameOfEntry, Estatus),
snmpc_lib:print_error("Misplaced TableEntry definition (~w)",
[NameOfEntry], Eline),
definitions_loop(T, Data);
definitions_loop([{#mc_notification{name = TrapName,
status = deprecated}, Line}|T],
false) ->
?vinfo2("defloop -> notification ~w is deprecated => ignored",
[TrapName], Line),
update_status(TrapName, deprecated),
ensure_macro_imported('NOTIFICATION-TYPE', Line),
definitions_loop(T, false);
definitions_loop([{#mc_notification{name = TrapName,
status = obsolete}, Line}|T],
Data) ->
?vlog2("defloop -> notification ~w is obsolete => ignored",
[TrapName], Line),
update_status(TrapName, obsolete),
ensure_macro_imported('NOTIFICATION-TYPE', Line),
definitions_loop(T, Data);
definitions_loop([{#mc_notification{name = TrapName,
vars = Variables,
status = Status,
description = Desc,
name_assign = {Parent, SubIndex}},Line}|T],
Data) ->
?vlog2("defloop -> notification:"
"~n TrapName: ~p"
"~n Variables: ~p"
"~n Status: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[TrapName, Variables, Status, Parent, SubIndex], Line),
update_status(TrapName, Status),
ensure_macro_imported('NOTIFICATION-TYPE', Line),
CDATA = get(cdata),
snmpc_lib:register_oid(Line, TrapName, Parent, SubIndex),
Descriptions = make_description(Desc),
Notif = #notification{trapname = TrapName,
description = Descriptions,
%% oidobjects: Store Variables temporary here.
%% This will be replaced later in the
%% get_final_mib function by a call to
%% the update_trap_objects function.
oidobjects = Variables},
snmpc_lib:check_notification(Notif, Line, CDATA#cdata.traps),
snmpc_lib:add_cdata(#cdata.traps, [Notif]),
definitions_loop(T, Data);
definitions_loop([{#mc_agent_capabilities{name = Name,
status = Status,
description = Desc,
reference = Ref,
modules = Mods,
name_assign = {Parent, SubIdx}},Line}|T], Data) ->
?vlog2("defloop -> agent_capabilities ~p:"
"~n Status: ~p"
"~n Desc: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[Name, Status, Desc, Parent, SubIdx], Line),
ensure_macro_imported('AGENT-CAPABILITIES', Line),
snmpc_lib:register_oid(Line, Name, Parent, SubIdx),
NewME = snmpc_lib:makeInternalNode2(false, Name),
Description = make_description(Desc),
Reference =
case Ref of
undefined ->
[];
_ ->
[{reference, Ref}]
end,
Modules =
case Mods of
undefined ->
[];
[] ->
[];
_ ->
[{modules, Mods}]
end,
AssocList = Reference ++ Modules,
NewME2 = NewME#me{description = Description,
assocList = AssocList},
snmpc_lib:add_cdata(#cdata.mes, [NewME2]),
definitions_loop(T, Data);
definitions_loop([{#mc_module_compliance{name = Name},Line}|T], Data) ->
?vlog2("defloop -> module_compliance:"
"~n Name: ~p", [Name], Line),
ensure_macro_imported('MODULE-COMPLIANCE', Line),
definitions_loop(T, Data);
definitions_loop([{#mc_object_group{name = Name,
objects = GroupObjects,
status = Status,
description = Desc,
reference = Ref,
name_assign = {Parent,SubIndex}}, Line}|T],
Data) ->
?vlog2("defloop -> object_group ~p:"
"~n GroupObjects: ~p"
"~n Status: ~p"
"~n Desc: ~p"
"~n Ref: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[Name, GroupObjects, Status, Desc, Ref, Parent, SubIndex], Line),
ensure_macro_imported('OBJECT-GROUP', Line),
GroupBool = get_group_check(get(options)),
case GroupBool of
true ->
snmpc_lib:add_cdata(#cdata.objectgroups,
[{Name,GroupObjects,Line}]),
%% Check that the group members has been defined
%% and that they have the correct status
snmpc_lib:check_object_group(Name, GroupObjects,
Line, Status);
_ ->
ok
end,
update_status(Name, Status),
snmpc_lib:test_father(Parent, Name, SubIndex, Line),
snmpc_lib:register_oid(Line, Name, Parent, SubIndex),
Description = make_description(Desc),
NewME = #me{aliasname = Name,
entrytype = group,
access = 'not-accessible',
description = Description,
assocList = [{kind, object},
{objects, GroupObjects}]},
snmpc_lib:add_cdata(#cdata.mes, [NewME]),
definitions_loop(T, Data);
definitions_loop([{#mc_notification_group{name = Name,
objects = GroupObjects,
status = Status,
description = Desc,
reference = Ref,
name_assign = {Parent,SubIndex}},
Line}
|T], Data) ->
?vlog2("defloop -> notification_group ~p:"
"~n GroupObjects: ~p"
"~n Status: ~p"
"~n Desc: ~p"
"~n Ref: ~p"
"~n Parent: ~p"
"~n SubIndex: ~p",
[Name, GroupObjects, Status, Desc, Ref, Parent, SubIndex], Line),
ensure_macro_imported('NOTIFICATION-GROUP', Line),
GroupBool = get_group_check(get(options)),
case GroupBool of
true ->
snmpc_lib:add_cdata(#cdata.notificationgroups,
[{Name,GroupObjects,Line}]),
%% Check that the group members has been defined
%% and that they have the correct status
snmpc_lib:check_notification_group(Name, GroupObjects,
Line, Status);
_ ->
ok
end,
update_status(Name, Status),
snmpc_lib:test_father(Parent, Name, SubIndex, Line),
snmpc_lib:register_oid(Line, Name, Parent, SubIndex),
Description = make_description(Desc),
NewME = #me{aliasname = Name,
entrytype = group,
access = 'not-accessible',
description = Description,
assocList = [{kind, notification},
{objects, GroupObjects}]},
snmpc_lib:add_cdata(#cdata.mes, [NewME]),
definitions_loop(T, Data);
definitions_loop([{#mc_object_type{name = NameOfTable,
syntax = {{sequence_of, SeqName},_},
status = Tstatus},Tline},
Entry, Seq|T],
Data) ->
?vlog("defloop -> object_type (sequence_of): "
"~n NameOfTable: ~p"
"~n SeqName: ~p"
"~n Tline: ~p"
"~n Entry: ~p"
"~n Seq: ~p",
[NameOfTable, SeqName, Tline, Entry, Seq]),
update_status(NameOfTable, Tstatus),
case Entry of
{#mc_object_type{syntax = {{type, SeqName},_line},
max_access = 'not-accessible',
kind = {table_entry, _IndexingInfo},
name_assign = {_NameOfTable,[1]}}, _Eline} ->
case Seq of
{#mc_sequence{name = SeqName}, Sline} ->
snmpc_lib:error("Internal error. Correct incorrect "
"table (~p,~w).",[SeqName,Sline],
Tline);
_ ->
?vinfo("defloop -> Invalid sequence: ~p", [Seq]),
snmpc_lib:print_error(
"Invalid SEQUENCE OF '~p'.",
[safe_elem(1,safe_elem(2,Seq))],Tline)
end;
Else ->
?vinfo("defloop -> Invalid table entry: "
"~n ~p", [Else]),
snmpc_lib:print_error(
"Invalid TableEntry '~p' (check STATUS, Sequence name, Oid)",
[safe_elem(1,safe_elem(2,Entry))],Tline)
end,
definitions_loop(T, Data);
definitions_loop([{#mc_object_type{name = NameOfTable,
syntax = {{sequence_of, SeqName},_},
status = Tstatus},Tline}|T],
Data) ->
?vlog("defloop -> object_type (sequence_of):"
"~n object_type: ~p"
"~n sequence_of: ~p"
"~n Tline: ~p", [NameOfTable, SeqName, Tline]),
update_status(NameOfTable, Tstatus),
snmpc_lib:print_error("Invalid statements following table ~p.",
[NameOfTable],Tline),
definitions_loop(T, Data);
definitions_loop([{#mc_sequence{name = SeqName,
fields = _FieldList},Line}|T],
Data) ->
?vwarning2("Unexpected SEQUENCE ~w => ignoring", [SeqName], Line),
definitions_loop(T, Data);
definitions_loop([{Obj,Line}|T], Data) ->
?vinfo2("defloop -> unknown error"
"~n Obj: ~p", [Obj], Line),
snmpc_lib:print_error("Unknown Error in MIB. "
"Can't describe the error better than this: ~999p ignored."
" Please send a trouble report to [email protected].",
[Obj], Line),
definitions_loop(T, Data);
definitions_loop([], _Data) ->
?vlog("defloop -> done", []),
ok.
safe_elem(N,T) ->
case catch(element(N,T)) of
{'EXIT',_} ->
"no more information available";
X -> X
end.
%% A correct column
define_cols([{#mc_object_type{name = NameOfCol,
syntax = Type1,
max_access = Access,
kind = {variable,Defval},
status = Status,
description = Desc,
units = Units,
name_assign = {NameOfEntry,[SubIndex]}},
Oline}|Rest],
SubIndex,
[{NameOfCol,Type2}|Fields], NameOfEntry, TableName, ColMEs) ->
?vlog("defcols -> object_type (variable):"
"~n NameOfCol: ~p"
"~n Type1: ~p"
"~n Access: ~p"
"~n Defval: ~p"
"~n Status ~p"
"~n Units ~p"
"~n NameOfEntry ~p"
"~n Oline: ~p",
[NameOfCol, Type1, Access, Defval, Status, Units,
NameOfEntry, Oline]),
update_status(NameOfCol, Status),
Deprecated = get_deprecated(get(options)),
ASN1type = snmpc_lib:make_ASN1type(Type1),
case (snmpc_lib:make_ASN1type(Type2))#asn1_type.bertype of
T2 when T2 == ASN1type#asn1_type.bertype -> ok;
_Else ->
snmpc_lib:error(
"Types for ~p differs from the SEQUENCE definition. ",
[NameOfCol],Oline)
end,
NewAccess = % a simple way to get the obsolete behaviour
if
Status =:= obsolete ->
%% Be quiet and don't implement
'not-accessible';
(Status =:= deprecated) andalso (Deprecated =:= false) ->
%% The compiler chooses not to implement the column.
?vinfo2("object_type ~w is deprecated => ignored",
[NameOfCol], Oline),
'not-accessible';
true -> Access
end,
snmpc_lib:register_oid(Oline,NameOfCol,NameOfEntry,[SubIndex]),
Description = make_description(Desc),
ColumnME = snmpc_lib:resolve_defval(
#me{oid = SubIndex,
aliasname = NameOfCol,
asn1_type = ASN1type,
entrytype = table_column,
access = NewAccess,
description = Description,
units = Units, %% Propably not usefull
assocList = [{table_name,TableName} | Defval]}),
define_cols(Rest,SubIndex+1,Fields,NameOfEntry,TableName,
[ColumnME|ColMEs]);
%% A "hole" (non-consecutive columns) in the table.
%% Implemented as a not-accessible column so Col always is index in
%% row tuple.
define_cols([{#mc_object_type{name = NameOfCol,
syntax = Type1,
max_access = Access,
kind = Kind,
status = Status,
name_assign = {NameOfEntry,[SubIndex]}},
Oline}|Rest],
ExpectedSubIndex, Fields, NameOfEntry, TableName, ColMEs)
when SubIndex > ExpectedSubIndex ->
?vlog("defcols -> object_type (non consecutive cols):"
"~n NameOfCol: ~p"
"~n Type1: ~p"
"~n Access: ~p"
"~n Status ~p"
"~n NameOfEntry ~p"
"~n Oline: ~p",
[NameOfCol, Type1, Access, Status, NameOfEntry, Oline]),
update_status(NameOfCol, Status),
Int = {{type, 'INTEGER'},Oline},
GeneratedColumn =
%% be sure to use an invalid column name here!
{#mc_object_type{name = '$no_name$',
syntax = Int,
max_access = 'not-accessible',
kind = {variable, [{defval,0}]},
status = current,
description = undefined,
name_assign = {NameOfEntry, [ExpectedSubIndex]}},
Oline},
define_cols([GeneratedColumn,
{#mc_object_type{name = NameOfCol,
syntax = Type1,
max_access = Access,
kind = Kind,
status = Status,
description = undefined,
name_assign = {NameOfEntry,[SubIndex]}},
Oline}|Rest], ExpectedSubIndex,
[{'$no_name$', Int}|Fields], NameOfEntry, TableName,ColMEs) ;
%% Ok. done. All fields are eaten.
define_cols(Rest, _SubIndex, [], _NameOfEntry, _TableName, ColMEs) ->
{ColMEs, Rest};
%% Error Handling
%% The name of the field and object is the same
define_cols([{#mc_object_type{name = NameOfCol,
kind = Kind,
name_assign = SubIndex}, Oline}|Rest],
SubIndex2, [{NameOfCol, _Type2}|Fields],
NameOfEntry, TableName, ColMEs) ->
?vlog("defcols -> object_type (name of field and object is the same):"
"~n NameOfCol: ~p"
"~n Kind: ~p"
"~n SubIndex: ~p"
"~n Oline: ~p"
"~n SubIndex2: ~p"
"~n NameOfEntry ~p"
"~n TableName ~p",
[NameOfCol,Kind,SubIndex,Oline,SubIndex2,NameOfEntry,TableName]),
SIok = case SubIndex of
{Parent,[_SI]} when Parent =/= NameOfEntry ->
snmpc_lib:print_error(
"Invalid parent ~p for table column ~p (should be ~p).",
[Parent,NameOfCol,NameOfEntry],Oline),
error;
{NameOfEntry,[SubIndex2]} ->
ok;
{NameOfEntry,[SI]} ->
snmpc_lib:print_error(
"Invalid column number ~p for column ~p.",
[SI, NameOfCol], Oline),
error;
_Q ->
snmpc_lib:print_error(
"Invalid parent for column ~p.",[NameOfCol],Oline),
error
end,
Kok = case Kind of
{variable,_} ->
ok;
_Q2 ->
snmpc_lib:print_error(
"Expected a table column.",[],Oline),
error
end,
case {SIok, Kok} of
{ok, ok} ->
snmpc_lib:print_error("Invalid table column definition for"
" ~p.",[NameOfCol],Oline);
_Q4 ->
done % already reported
end,
define_cols(Rest,SubIndex2+1,Fields,NameOfEntry,TableName,ColMEs);
%% It's an object-type but everything else is wrong
define_cols([{#mc_object_type{name = NameOfCol},Oline}|Rest],SubIndex2,Fields,
NameOfEntry,TableName,ColMEs) ->
snmpc_lib:print_error(
"Number of columns differs from SEQUENCE definition (object:~p).",
[NameOfCol],Oline),
define_cols(Rest,SubIndex2+1,Fields,NameOfEntry,TableName,ColMEs);
define_cols([{Obj,Line}|Tl], _SubIndex,_,_,_,ColMEs) ->
snmpc_lib:print_error("Corrupt table definition.",[],Line),
{ColMEs,[{Obj,Line}|Tl]};
define_cols(Rest, _SubIndex,_,_,_,ColMEs) ->
snmpc_lib:print_error("Corrupt table definition.",[]),
{ColMEs,Rest}.
ensure_macro_imported(dummy, _Line) -> ok;
ensure_macro_imported(Macro, Line) ->
Macros = (get(cdata))#cdata.imported_macros,
case lists:member(Macro, Macros) of
true -> ok;
false ->
snmpc_lib:print_error("Macro ~p not imported.", [Macro],
Line)
end.
test_table(NameOfTable, Taccess, Kind, _Tindex, Tline) ->
if
Taccess =/= 'not-accessible' ->
snmpc_lib:print_error(
"Table ~w must have STATUS not-accessible",
[NameOfTable],Tline),
error;
Kind =/= {variable,[]} ->
snmpc_lib:print_error(
"Bad table definition (~w).",
[NameOfTable],Tline),
error;
true ->
ok
end.
save(Filename, MibName, Options) ->
R = filename:rootname(Filename),
File1 = filename:basename(R),
File3 = snmpc_misc:to_upper(File1),
case snmpc_misc:to_upper(atom_to_list(MibName)) of
File3 ->
{value, OutDirr} = snmpc_misc:assq(outdir, Options),
OutDir = snmpc_misc:ensure_trailing_dir_delimiter(OutDirr),
File2 = (OutDir ++ File1) ++ ".bin",
{ok, MIB} = snmpc_lib:get_final_mib(File1, Options),
case get(errors) of
undefined ->
case file:write_file(File2, term_to_binary(MIB)) of
ok ->
{ok, File2};
_Err ->
snmpc_lib:error(
"Couldn't write file \"~s\".",[File2])
end;
E ->
?vlog("save failed: "
"~n ~p", [E]),
{'EXIT',error}
end;
MibNameL ->
snmpc_lib:error("Mibname (~s) differs from filename (~s).",
[MibNameL, File1])
end.
%% parse takes a text file as a input and the output is a list of tokens.
%% Input: FileName (file of mibs)
%% Output: {ok, Mib} where MIB is a tuple of Tokens.
%% {error, {LineNbr, Mod, Msg} an error on line number LineNb.
parse(FileName) ->
?vtrace("parse -> start tokenizer for ~p", [FileName]),
case snmpc_tok:start_link(reserved_words(),
[{file, FileName ++ ".mib"},
{forget_stringdata, true}]) of
{error,ReasonStr} ->
snmpc_lib:error(lists:flatten(ReasonStr),[]);
{ok, TokPid} ->
?vtrace("parse -> tokenizer start, now get tokens", []),
Toks = snmpc_tok:get_all_tokens(TokPid),
?vtrace("parse -> tokens: ~p", [Toks]),
set_version(Toks),
%% io:format("parse -> lexical analysis: ~n~p~n", [Toks]),
%% ?vtrace("parse -> lexical analysis: ~n~p", [Toks]),
CDataArg =
case lists:keysearch(module, 1, get(options)) of
{value, {module, M}} -> {module, M};
_ -> {file, FileName ++ ".funcs"}
end,
put(cdata,snmpc_lib:make_cdata(CDataArg)),
?vtrace("parse -> stop tokenizer and then do the actual parse", []),
snmpc_tok:stop(TokPid),
Res = if
is_list(Toks) ->
snmpc_mib_gram:parse(Toks);
true ->
Toks
end,
?vtrace("parse -> parsed result: ~n~p", [Res]),
case Res of
{ok, PData} ->
{ok, PData};
{error, {LineNbr, Mod, Msg}} ->
case catch format_yecc_error(LineNbr, Msg) of
{Line, Format, Data} ->
snmpc_lib:error(Format,Data,Line);
_Q -> % sorry, have to use ugly yecc printouts
Str = apply(Mod, format_error, [Msg]),
snmpc_lib:error("~s",[Str],LineNbr)
end
end
end.
set_version(Toks) when is_list(Toks) ->
%% MODULE-IDENTITY _must_ be invoked in SNMPv2 according to RFC1908
case lists:keymember('MODULE-IDENTITY',1,Toks) of
true ->
put(snmp_version,2);
false ->
put(snmp_version,1)
end;
set_version(_) ->
put(snmp_version,1).
%% YeccGeneratedFile:format_error/1 is bad.
format_yecc_error(Line, [ErrMsg, [${,Category, $,, _LineStr,$,, Value, $}]]) ->
{Line, "~s \"~s\" (~s).", [ErrMsg, Value, Category]}.
%% The same as the (quoted) Terminals in the snmpc_mib_gram.yrl
reserved_words() ->
[
'ACCESS',
'BEGIN',
'BIT',
'CONTACT-INFO',
'Counter',
'DEFINITIONS',
'DEFVAL',
'DESCRIPTION',
'DISPLAY-HINT',
'END',
'ENTERPRISE',
'FROM',
'Gauge',
'IDENTIFIER',
'IDENTIFIER',
'IMPORTS',
'INDEX',
'INTEGER',
'IpAddress',
'LAST-UPDATED',
'NetworkAddress',
'OBJECT',
'OBJECT',
'OBJECT-TYPE',
'OCTET',
'OF',
'Opaque',
'REFERENCE',
'SEQUENCE',
'SIZE',
'STATUS',
'STRING',
'SYNTAX',
'TRAP-TYPE',
'TimeTicks',
'VARIABLES',
%% v2
'LAST-UPDATED',
'ORGANIZATION',
'CONTACT-INFO',
'MODULE-IDENTITY',
'NOTIFICATION-TYPE',
'MODULE-COMPLIANCE',
'OBJECT-GROUP',
'NOTIFICATION-GROUP',
'REVISION',
'OBJECT-IDENTITY',
'MAX-ACCESS',
'UNITS',
'AUGMENTS',
'IMPLIED',
'OBJECTS',
'TEXTUAL-CONVENTION',
'OBJECT-GROUP',
'NOTIFICATION-GROUP',
'NOTIFICATIONS',
'MODULE-COMPLIANCE',
'AGENT-CAPABILITIES',
'PRODUCT-RELEASE',
'SUPPORTS',
'INCLUDES',
'MODULE',
'MANDATORY-GROUPS',
'GROUP',
'WRITE-SYNTAX',
'MIN-ACCESS',
'BITS'
]
.