%#<% Data::Format::HTML->new->format($index{by_path}) %> % my $json = Cpanel::JSON::XS->new->canonical; <% $json->encode($result) %> <%init> #<%once> #enable me in production use SNMP; SNMP::initMib(); my $mib = \%SNMP::MIB; # make an index of the leaf nodes my %index = ( by_objectID => {}, # {.1.3.6.1.2.1.1.1} by_fullname => {}, # {iso.org.dod.internet.mgmt.mib-2.system.sysDescr} by_path => {}, # {iso}{org}{dod}{internet}{mgmt}{mib-2}{system}{sysDescr} module => {}, #{SNMPv2-MIB}{by_path}{iso}{org}... #{SNMPv2-MIB}{by_fullname}{iso.org...} ); my %name_of_oid = (); # '.1.3.6.1' => 'iso.org.dod.internet' # build up path names my $fullname; $fullname = sub { my $oid = shift; return $name_of_oid{$oid} if exists $name_of_oid{$oid}; my $object = $mib->{$oid}; my $myname = '.' . $object->{label}; # cut off the last element and recurse $oid =~ /^(\.[\d\.]+)?(\.\d+)$/; if ( length($1) ) { $myname = $fullname->($1) . $myname; } return $name_of_oid{$oid} = $myname }; my @oids = keys(%$mib); # dotted numeric OIDs foreach my $oid (@oids) { my $object = {}; %$object = %{ $mib->{$oid} }; # untie it # and remove references delete $object->{parent}; delete $object->{children}; delete $object->{nextNode}; $index{by_objectID}{$oid} = $object; my $myname = $fullname->($oid); $object->{fullname} = $myname; $index{by_fullname}{$myname} = $object; my $moduleID = $object->{moduleID}; $index{module}{$moduleID} ||= { by_fullname => {}, by_path => {} }; $index{module}{$moduleID}{by_fullname}{$myname} = $object; } my @names = sort {$a cmp $b} keys %{ $index{by_fullname} }; foreach my $myname (@names) { my $obj = $index{by_fullname}{$myname}; my $moduleID = $obj->{moduleID}; my @parts = split('\.', $myname); shift @parts; # always starts with an empty string for ($index{by_path}, $index{module}{$moduleID}{by_path}) { my $subindex = $_; for my $this_part (@parts) { $subindex = $subindex->{$this_part} ||= {}; } # $subindex now = $index{by_path}{foo}{bar}{baz}. # set {''} = the object with that name. # and set object $index{by_path}{foo}{bar}{baz}{''} = # the object named .foo.bar.baz $subindex->{''} = $obj; } } # #<%init> # no ACL for this my $sub = $cgi->param('sub'); my $result = {}; if ( $sub eq 'search' ) { warn "search: ".$cgi->param('arg')."\n"; my ($module, $string) = split(':', $cgi->param('arg'), 2); my $idx; # the branch of the index to use for this search if ( $module eq 'ANY' ) { $idx = \%index; } elsif (exists($index{module}{$module}) ) { $idx = $index{module}{$module}; } else { warn "unknown MIB moduleID: $module\n"; $idx = {}; # will return nothing, because you've somehow sent a bad moduleID } if ( exists($index{by_fullname}{$string}) ) { warn "exact match\n"; # don't make this module-selective--if the path matches an existing # object, return that object %$result = %{ $index{by_fullname}{$string} }; # put the object info in $result #warn Dumper $result; } my @choices; # menu options to return if ( $string =~ /^[\.\d]+$/ ) { # then this is a numeric path # ignore the module filter, and return everything starting with $string if ( $string =~ /^\./ ) { @choices = grep /^\Q$string\E/, keys %{$index{by_objectID}}; } else { # or everything containing it @choices = grep /\Q$string\E/, keys %{$index{by_objectID}}; } @choices = map { $index{by_objectID}{$_}->{fullname} } @choices; } elsif ( $string eq '' or $string =~ /^\./ ) { # then this is an absolute path my @parts = split('\.', $string); shift @parts; my $subindex = $idx->{by_path}; my $path = ''; @choices = keys %$subindex; # walk all the specified path parts foreach my $this_part (@parts) { # stop before walking off the map last if !exists($subindex->{$this_part}); $subindex = $subindex->{$this_part}; $path .= '.' . $this_part; @choices = grep {$_} keys %$subindex; } # skip uninteresting nodes: those that aren't accessible nodes (have no # data type), and have only one path forward while ( scalar(@choices) == 1 and (!exists $subindex->{''} or $subindex->{''}->{type} eq '') ) { $subindex = $subindex->{ $choices[0] }; $path .= '.' . $choices[0]; @choices = grep {$_} keys %$subindex; } # if we are on an existing node, and the entered path didn't exactly # match another node, return the current node as the result if (!keys %$result and exists($subindex->{''})) { %$result = %{ $subindex->{''} }; } # prepend the path up to this point foreach (@choices) { $_ = $path.'.'.$_; # also label accessible nodes for the UI if ( exists($subindex->{$_}{''}) and $subindex->{$_}{''}{'type'} ) { $_ .= '-'; } } # also include one level above the originally requested path, # for tree-like navigation if ( $string =~ /^(.+)\.[^\.]+/ ) { unshift @choices, $1; } } else { # then this is a full-text search warn "/$string/\n"; @choices = grep /\Q$string\E/i, keys(%{ $idx->{by_fullname} }); } @choices = sort @choices; $result->{choices} = \@choices; } elsif ( $sub eq 'get_module_list' ) { $result = { modules => [ sort keys(%{ $index{module} }) ] }; }