use
Dancer
qw/:syntax :script !to_json !from_json/
;
our
@EXPORT
= ();
our
@EXPORT_OK
=
qw/
load_cache_for_device
snmpwalk_to_cache
gather_every_mib_object
dump_cache_to_browserdata
add_snmpinfo_aliases
get_oidmap_from_database
get_oidmap_from_mibs_files
get_mibs_for
get_munges
/
;
our
%EXPORT_TAGS
= (
all
=> \
@EXPORT_OK
);
sub
load_cache_for_device {
my
$device
=
shift
;
return
{}
unless
(
$device
->is_pseudo or not
$device
->in_storage);
if
(
$device
->is_pseudo and
my
$snapshot
=
$device
->snapshot) {
return
thaw( decode_base64(
$snapshot
->cache ) );
}
my
$pseudo_cache
= catfile( catdir((
$ENV
{NETDISCO_HOME} ||
$ENV
{HOME}),
'logs'
,
'snapshots'
),
$device
->ip );
if
(-f
$pseudo_cache
and not
$device
->in_storage) {
my
$content
= read_text(
$pseudo_cache
);
if
(
$content
=~ m/^\.1/) {
my
%oids
= ();
my
@lines
=
split
/\n/,
$content
;
foreach
my
$line
(
@lines
) {
my
(
$oid
,
$val
) =
$line
=~ m/^(\S+) = (?:[^:]+: )?(.+)$/;
next
unless
$oid
and
$val
;
$val
=
''
if
$val
=~ m/^[^:]+: ?$/;
$val
=~ s/^"//;
$val
=~ s/"$//;
$oids
{
$oid
} =
$val
;
}
return
snmpwalk_to_cache(
%oids
);
}
else
{
my
$cache
= thaw( decode_base64(
$content
) );
return
add_snmpinfo_aliases(
$cache
);
}
}
return
{};
}
sub
snmpwalk_to_cache {
my
%oids
=
@_
;
return
()
unless
scalar
keys
%oids
;
my
%oidmap
=
reverse
get_oidmap_from_database();
my
%leaves
= ();
OID:
foreach
my
$orig_oid
(
keys
%oids
) {
my
$oid
=
$orig_oid
;
my
$idx
=
''
;
while
(
length
(
$oid
) and !
exists
$oidmap
{
$oid
}) {
$oid
=~ s/\.(\d+)$//;
$idx
= ((
defined
$idx
and
length
$idx
) ?
"${1}.${idx}"
: $1);
}
if
(
exists
$oidmap
{
$oid
}) {
$idx
=~ s/^\.//;
my
$qleaf
=
$oidmap
{
$oid
};
my
$key
=
$oid
.
'~~'
.
$qleaf
;
if
(
$idx
eq 0) {
$leaves
{
$key
} =
$oids
{
$orig_oid
};
}
else
{
delete
$leaves
{
$key
}
if
defined
$leaves
{
$key
} and
ref
q{}
eq
ref
$leaves
{
$key
};
$leaves
{
$key
}->{
$idx
} =
$oids
{
$orig_oid
};
}
next
OID;
}
}
my
$info
= SNMP::Info->new({
Offline
=> 1,
Cache
=> {},
Session
=> {},
MibDirs
=> [ get_mibdirs() ],
AutoSpecify
=> 0,
IgnoreNetSNMPConf
=> 1,
Debug
=> (
$ENV
{INFO_TRACE} || 0),
DebugSNMP
=> (
$ENV
{SNMP_TRACE} || 0),
});
foreach
my
$attr
(
keys
%leaves
) {
my
(
$oid
,
$qleaf
) =
split
m/~~/,
$attr
;
my
$val
=
$leaves
{
$attr
};
my
$row
= schema(
'netdisco'
)->resultset(
'SNMPObject'
)->find(
$oid
);
if
(
$row
and
$row
->enum) {
my
%emap
=
map
{
reverse
split
m/\(/ }
map
{ s/\)//;
$_
}
@{
$row
->enum };
if
(
ref
q{}
eq
ref
$val
) {
$val
=
$emap
{
$val
}
if
exists
$emap
{
$val
};
}
elsif
(
ref
{} eq
ref
$val
) {
foreach
my
$k
(
keys
%$val
) {
$val
->{
$k
} =
$emap
{
$val
->{
$k
} }
if
exists
$emap
{
$val
->{
$k
} };
}
}
}
my
$leaf
=
$qleaf
;
$leaf
=~ s/.+:://;
my
$snmpqleaf
=
$qleaf
;
$snmpqleaf
=~ s/[-:]/_/g;
$info
->_cache(
$leaf
,
$leaves
{
$attr
});
$info
->_cache(
$snmpqleaf
,
$leaves
{
$attr
});
}
debug
sprintf
"snmpwalk_to_cache: cache size: %d"
,
scalar
keys
%{
$info
->cache };
add_snmpinfo_aliases(
$info
);
return
$info
->cache;
}
sub
gather_every_mib_object {
my
(
$device
,
$snmp
,
@extra
) =
@_
;
my
@mibs
=
keys
%{
$snmp
->mibs() };
my
@extra_mibs
= get_mibs_for(
@extra
);
debug
sprintf
"covering %d MIBs"
, (
scalar
@mibs
+
scalar
@extra_mibs
);
SNMP::loadModules(
$_
)
for
@extra_mibs
;
my
%oidmap
= get_oidmap_from_database(
@mibs
,
@extra_mibs
);
debug
sprintf
"gathering %d MIB Objects"
,
scalar
keys
%oidmap
;
foreach
my
$qleaf
(
sort
{sortable_oid(
$oidmap
{
$a
}) cmp sortable_oid(
$oidmap
{
$b
})}
keys
%oidmap
) {
my
$leaf
=
$qleaf
;
$leaf
=~ s/.+:://;
my
$snmpqleaf
=
$qleaf
;
$snmpqleaf
=~ s/[-:]/_/g;
$snmp
->
$snmpqleaf
;
next
unless
exists
$snmp
->cache->{
'_'
.
$snmpqleaf
};
$snmp
->_cache(
$leaf
,
$snmp
->
$snmpqleaf
);
}
}
sub
dump_cache_to_browserdata {
my
(
$device
,
$snmp
) =
@_
;
my
%qoidmap
= get_oidmap_from_database();
my
%oidmap
= get_leaf_to_qleaf_map();
my
%munges
= get_munges(
$snmp
);
my
$cache
=
$snmp
->cache;
my
%oids
= ();
foreach
my
$key
(
keys
%$cache
) {
next
unless
$key
and
$key
=~ m/^_/;
my
$snmpqleaf
=
$key
;
$snmpqleaf
=~ s/^_//;
my
$qleaf
=
$snmpqleaf
;
$qleaf
=~ s/__/::/;
$qleaf
=~ s/_/-/g;
my
$leaf
=
$qleaf
;
$leaf
=~ s/.+:://;
next
unless
exists
$qoidmap
{
$qleaf
}
or (
exists
$oidmap
{
$leaf
} and
exists
$qoidmap
{
$oidmap
{
$leaf
} });
my
$oid
=
exists
$qoidmap
{
$qleaf
} ?
$qoidmap
{
$qleaf
} :
$qoidmap
{
$oidmap
{
$leaf
} };
my
$data
=
exists
$cache
->{
'store'
}{
$snmpqleaf
} ?
$cache
->{
'store'
}{
$snmpqleaf
}
:
$cache
->{
$key
};
next
unless
defined
$data
;
push
@{
$oids
{
$oid
} }, {
oid
=>
$oid
,
oid_parts
=> [
grep
{
length
} (
split
m/\./,
$oid
) ],
leaf
=>
$leaf
,
qleaf
=>
$qleaf
,
munge
=> (
$munges
{
$snmpqleaf
} ||
$munges
{
$leaf
}),
value
=> encode_base64( nfreeze( [
$data
] ) ),
};
}
%oids
=
map
{ (
$_
=> [
sort
{
length
(
$b
->{qleaf}) <=>
length
(
$a
->{qleaf})} @{
$oids
{
$_
} }]) }
keys
%oids
;
schema(
'netdisco'
)->txn_do(
sub
{
my
$gone
=
$device
->oids->
delete
;
debug
sprintf
'removed %d oids from db'
,
$gone
;
$device
->oids->populate([
sort
{sortable_oid(
$a
->{oid}) cmp sortable_oid(
$b
->{oid})}
map
{
delete
$_
->{qleaf};
$_
}
map
{
$oids
{
$_
}->[0] }
keys
%oids
]);
debug
sprintf
'added %d new oids to db'
,
scalar
keys
%oids
;
});
}
sub
add_snmpinfo_aliases {
my
$info
=
shift
or
return
{};
if
(not blessed
$info
) {
$info
= SNMP::Info->new({
Offline
=> 1,
Cache
=>
$info
,
Session
=> {},
MibDirs
=> [ get_mibdirs() ],
AutoSpecify
=> 0,
IgnoreNetSNMPConf
=> 1,
Debug
=> (
$ENV
{INFO_TRACE} || 0),
DebugSNMP
=> (
$ENV
{SNMP_TRACE} || 0),
});
}
my
%globals
= %{
$info
->globals };
my
%funcs
= %{
$info
->funcs };
while
(
my
(
$alias
,
$leaf
) =
each
%globals
) {
next
if
$leaf
=~ m/\.\d+$/;
$info
->_cache(
$alias
,
$info
->
$leaf
)
if
$info
->
$leaf
;
}
while
(
my
(
$alias
,
$leaf
) =
each
%funcs
) {
$info
->_cache(
$alias
, dclone
$info
->
$leaf
)
if
ref
q{}
ne
ref
$info
->
$leaf
;
}
my
%propfix
= (
chassisId
=>
'serial1'
,
ospfRouterId
=>
'router_ip'
,
bgpIdentifier
=>
'bgp_id'
,
bgpLocalAs
=>
'bgp_local_as'
,
ifPhysAddress
=>
'mac'
,
qw(
model model
serial serial
os_ver os_ver
os os
)
,
);
foreach
my
$prop
(
keys
%propfix
) {
my
$val
=
$info
->
$prop
;
$val
= [
values
%$val
]->[0]
if
ref
$val
eq
'HASH'
;
$info
->_cache(
$propfix
{
$prop
},
$val
);
}
$info
->_cache(
'sysUpTime'
,
$info
->sysUpTimeInstance->{
''
})
if
ref
{} eq
ref
$info
->sysUpTimeInstance
and not
$info
->sysUpTime;
while
(
my
$method
= <DATA>) {
$method
=~ s/\s//g;
next
unless
length
$method
and not
$info
->
$method
;
$info
->_cache(
$method
,
''
)
if
exists
$globals
{
$method
};
$info
->_cache(
$method
, {})
if
exists
$funcs
{
$method
};
}
debug
sprintf
"add_snmpinfo_aliases: cache size: %d"
,
scalar
keys
%{
$info
->cache };
return
$info
->cache;
}
sub
get_leaf_to_qleaf_map {
debug
"loading database leaf to qleaf map"
;
my
%oidmap
=
map
{ (
$_
->{leaf} => (
join
'::'
,
$_
->{mib},
$_
->{leaf}) ) }
schema(
'netdisco'
)->resultset(
'SNMPObject'
)
->search({
num_children
=> 0,
leaf
=> {
'!~'
=>
'anonymous#\d+$'
},
-or
=> [
type
=> {
'<>'
=>
''
},
access
=> {
'~'
=>
'^(read|write)'
},
\
'oid_parts[array_length(oid_parts,1)] = 0'
],
},{
columns
=> [
qw/mib leaf/
],
order_by
=>
'oid_parts'
})
->hri->all;
debug
sprintf
"loaded %d mapped objects"
,
scalar
keys
%oidmap
;
return
%oidmap
;
}
sub
get_oidmap_from_database {
my
@mibs
=
@_
;
debug
"loading netdisco-mibs object cache (database)"
;
my
%oidmap
=
map
{ ((
join
'::'
,
$_
->{mib},
$_
->{leaf}) =>
$_
->{oid}) }
schema(
'netdisco'
)->resultset(
'SNMPObject'
)
->search({
(
scalar
@mibs
? (
mib
=> {
-in
=> \
@mibs
}) : ()),
num_children
=> 0,
leaf
=> {
'!~'
=>
'anonymous#\d+$'
},
-or
=> [
type
=> {
'<>'
=>
''
},
access
=> {
'~'
=>
'^(read|write)'
},
\
'oid_parts[array_length(oid_parts,1)] = 0'
],
},{
columns
=> [
qw/mib oid leaf/
],
order_by
=>
'oid_parts'
})
->hri->all;
if
(not
scalar
@mibs
) {
debug
sprintf
"loaded %d MIB objects"
,
scalar
keys
%oidmap
;
}
return
%oidmap
;
}
sub
get_oidmap_from_mibs_files {
debug
"loading netdisco-mibs object cache (netdisco-mibs)"
;
my
$home
= (setting(
'mibhome'
) || catdir((
$ENV
{NETDISCO_HOME} ||
$ENV
{HOME}),
'netdisco-mibs'
));
my
$reports
= catdir(
$home
,
'EXTRAS'
,
'reports'
);
my
@maps
=
map
{ (splitdir(
$_
))[-1] }
grep
{ ! m/^(?:EXTRAS)$/ }
grep
{ ! m/\./ }
grep
{ -f }
glob
(catfile(
$reports
,
'*_oids'
));
my
@report
= ();
push
@report
, read_lines( catfile(
$reports
,
$_
),
'latin-1'
)
for
(
qw(rfc_oids net-snmp_oids cisco_oids)
,
@maps
);
my
%oidmap
= ();
foreach
my
$line
(
@report
) {
my
(
$oid
,
$qual_leaf
,
$rest
) =
split
m/,/,
$line
;
next
unless
defined
$oid
and
defined
$qual_leaf
;
next
if
exists
$oidmap
{
$oid
};
my
(
$mib
,
$leaf
) =
split
m/::/,
$qual_leaf
;
$oidmap
{
$oid
} =
$leaf
;
}
debug
sprintf
"loaded %d MIB objects"
,
scalar
keys
%oidmap
;
return
%oidmap
;
}
sub
get_mibs_for {
my
@extra
=
@_
;
my
@mibs
= ();
my
$home
= (setting(
'mibhome'
) || catdir((
$ENV
{NETDISCO_HOME} ||
$ENV
{HOME}),
'netdisco-mibs'
));
my
$cachedir
= catdir(
$home
,
'EXTRAS'
,
'indexes'
,
'cache'
);
foreach
my
$item
(
@extra
) {
next
unless
$item
;
if
(
$item
=~ m/^[a-z0-9-]+$/) {
push
@mibs
,
map
{ (
split
m/\s+/)[0] }
read_lines( catfile(
$cachedir
,
$item
),
'latin-1'
);
}
elsif
(
$item
=~ m/::/) {
Module::Load::load
$item
;
$item
.=
'::MIBS'
;
{
no
strict
'refs'
;
push
@mibs
,
keys
%${item};
}
}
else
{
push
@mibs
,
$item
;
}
}
return
@mibs
;
}
sub
get_munges {
my
$snmp
=
shift
;
my
%munge_set
= ();
my
%munge
= %{
$snmp
->munge() };
my
%funcs
= %{
$snmp
->funcs() };
my
%globals
= %{
$snmp
->globals() };
while
(
my
(
$alias
,
$leaf
) =
each
%globals
) {
$munge_set
{
$leaf
} = subname(
$munge
{
$leaf
})
if
exists
$munge
{
$leaf
};
$munge_set
{
$leaf
} = subname(
$munge
{
$alias
})
if
exists
$munge
{
$alias
};
}
while
(
my
(
$alias
,
$leaf
) =
each
%funcs
) {
$munge_set
{
$leaf
} = subname(
$munge
{
$leaf
})
if
exists
$munge
{
$leaf
};
$munge_set
{
$leaf
} = subname(
$munge
{
$alias
})
if
exists
$munge
{
$alias
};
}
return
%munge_set
;
}
true;