RT# 75817 - updated UI to be more intuitive
[freeside.git] / httemplate / misc / xmlhttp-mib-browse.html
1 %#<% Data::Format::HTML->new->format($index{by_path}) %>
2 % my $json = Cpanel::JSON::XS->new->canonical;
3 <% $json->encode($result) %>
4 <%init>
5 #<%once>  #enable me in production
6 use SNMP;
7 SNMP::initMib();
8 my $mib = \%SNMP::MIB;
9
10 # make an index of the leaf nodes
11 my %index = (
12   by_objectID => {}, # {.1.3.6.1.2.1.1.1}
13   by_fullname => {}, # {iso.org.dod.internet.mgmt.mib-2.system.sysDescr}
14   by_path     => {}, # {iso}{org}{dod}{internet}{mgmt}{mib-2}{system}{sysDescr}
15   module  => {}, #{SNMPv2-MIB}{by_path}{iso}{org}...
16                  #{SNMPv2-MIB}{by_fullname}{iso.org...}
17 );
18
19 my %name_of_oid = (); # '.1.3.6.1' => 'iso.org.dod.internet'
20
21 # build up path names
22 my $fullname;
23 $fullname = sub {
24   my $oid = shift;
25   return $name_of_oid{$oid} if exists $name_of_oid{$oid};
26
27   my $object = $mib->{$oid};
28   my $myname = '.' . $object->{label};
29   # cut off the last element and recurse
30   $oid =~ /^(\.[\d\.]+)?(\.\d+)$/;
31   if ( length($1) ) {
32     $myname = $fullname->($1) . $myname;
33   }
34   return $name_of_oid{$oid} = $myname
35 };
36
37 my @oids = keys(%$mib); # dotted numeric OIDs
38 foreach my $oid (@oids) {
39   my $object = {};
40   %$object = %{ $mib->{$oid} }; # untie it
41   # and remove references
42   delete $object->{parent};
43   delete $object->{children};
44   delete $object->{nextNode};
45   $index{by_objectID}{$oid} = $object;
46   my $myname = $fullname->($oid);
47   $object->{fullname} = $myname;
48   $index{by_fullname}{$myname} = $object;
49   my $moduleID = $object->{moduleID};
50   $index{module}{$moduleID} ||= { by_fullname => {}, by_path => {} };
51   $index{module}{$moduleID}{by_fullname}{$myname} = $object;
52 }
53 my @names = sort {$a cmp $b} keys %{ $index{by_fullname} };
54 foreach my $myname (@names) {
55   my $obj = $index{by_fullname}{$myname};
56   my $moduleID = $obj->{moduleID};
57   my @parts = split('\.', $myname);
58   shift @parts; # always starts with an empty string
59   for ($index{by_path}, $index{module}{$moduleID}{by_path}) {
60     my $subindex = $_;
61     for my $this_part (@parts) {
62       $subindex = $subindex->{$this_part} ||= {};
63     }
64     # $subindex now = $index{by_path}{foo}{bar}{baz}.
65     # set {''} = the object with that name.
66     # and set object $index{by_path}{foo}{bar}{baz}{''} = 
67     # the object named .foo.bar.baz
68     $subindex->{''} = $obj;
69   }
70 }
71
72 #</%once>
73 #<%init>
74 # no ACL for this
75 my $sub = $cgi->param('sub');
76 my $result = {};
77 if ( $sub eq 'search' ) {
78   warn "search: ".$cgi->param('arg')."\n";
79   my ($module, $string) = split(':', $cgi->param('arg'), 2);
80   my $idx; # the branch of the index to use for this search
81   if ( $module eq 'ANY' ) {
82     $idx = \%index;
83   } elsif (exists($index{module}{$module}) ) {
84     $idx = $index{module}{$module};
85   } else {
86     warn "unknown MIB moduleID: $module\n";
87     $idx = {}; # will return nothing, because you've somehow sent a bad moduleID
88   }
89   if ( exists($index{by_fullname}{$string}) ) {
90     warn "exact match\n";
91     # don't make this module-selective--if the path matches an existing 
92     # object, return that object
93     %$result = %{ $index{by_fullname}{$string} }; # put the object info in $result
94     #warn Dumper $result;
95   }
96   my @choices; # menu options to return
97   if ( $string =~ /^[\.\d]+$/ ) {
98     # then this is a numeric path
99     # ignore the module filter, and return everything starting with $string
100     if ( $string =~ /^\./ ) {
101       @choices = grep /^\Q$string\E/, keys %{$index{by_objectID}};
102     } else {
103       # or everything containing it
104       @choices = grep /\Q$string\E/, keys %{$index{by_objectID}};
105     }
106     @choices = map { $index{by_objectID}{$_}->{fullname} } @choices;
107   } elsif ( $string eq '' or $string =~ /^\./ ) {
108     # then this is an absolute path
109     my @parts = split('\.', $string);
110     shift @parts;
111     my $subindex = $idx->{by_path};
112     my $path = '';
113     @choices = keys %$subindex;
114     # walk all the specified path parts
115     foreach my $this_part (@parts) {
116       # stop before walking off the map
117       last if !exists($subindex->{$this_part});
118       $subindex = $subindex->{$this_part};
119       $path .= '.' . $this_part;
120       @choices = grep {$_} keys %$subindex;
121     }
122     # skip uninteresting nodes: those that aren't accessible nodes (have no
123     # data type), and have only one path forward
124     while ( scalar(@choices) == 1
125             and (!exists $subindex->{''} or $subindex->{''}->{type} eq '') ) {
126
127       $subindex = $subindex->{ $choices[0] };
128       $path .= '.' . $choices[0];
129       @choices = grep {$_} keys %$subindex;
130
131     }
132
133     # if we are on an existing node, and the entered path didn't exactly
134     # match another node, return the current node as the result
135     if (!keys %$result and exists($subindex->{''})) {
136       %$result = %{ $subindex->{''} };
137     }
138     # prepend the path up to this point
139     foreach (@choices) {
140       $_ = $path.'.'.$_;
141       # also label accessible nodes for the UI
142       if ( exists($subindex->{$_}{''}) and $subindex->{$_}{''}{'type'} ) {
143         $_ .= '-';
144       }
145     }
146     # also include one level above the originally requested path, 
147     # for tree-like navigation
148     if ( $string =~ /^(.+)\.[^\.]+/ ) {
149       unshift @choices, $1;
150     }
151   } else {
152     # then this is a full-text search
153     warn "/$string/\n";
154     @choices = grep /\Q$string\E/i, keys(%{ $idx->{by_fullname} });
155   }
156   @choices = sort @choices;
157   $result->{choices} = \@choices;
158 } elsif ( $sub eq 'get_module_list' ) {
159   $result = { modules => [ sort keys(%{ $index{module} }) ] };
160 }
161 </%init>