#67 Juniper EX4300 Missing/Wrong information

Modify Q-Bridge methods to work on both pre-Enhanced Layer 2 Software (ELS) such as EX4200 and newer Enhanced Layer 2 Software (ELS) based switches such as EX4300 and QFX series
This commit is contained in:
Eric A. Miller
2018-04-19 18:23:48 -04:00
parent 4919cb0f69
commit 2aca6d9300
3 changed files with 1000 additions and 132 deletions

View File

@@ -20,6 +20,7 @@ Version 3.55
[BUG FIXES]
* #67 Juniper EX4300 Missing/Wrong information
* #61 ZyXEL (X)GS1900 family MIB - Fix loop in layers method
* Fix ISA in AMAP and EDP classes
* Add missing MIB in L2::Trapeze, L2::NWSS2300, and L3::Dell

View File

@@ -52,40 +52,48 @@ $VERSION = '3.54';
'JUNIPER-L2ALD-MIB' => 'jnxL2aldVlanFdbId',
);
%GLOBALS = ( %SNMP::Info::Layer3::GLOBALS,
%SNMP::Info::LLDP::GLOBALS,
'serial' => 'jnxBoxSerialNo.0',
'mac' => 'dot1dBaseBridgeAddress',
'box_descr' => 'jnxBoxDescr',
'version' => 'jnxVirtualChassisMemberSWVersion.0',
'vc_model' => 'jnxVirtualChassisMemberModel.0',
);
%FUNCS = ( %SNMP::Info::Layer3::FUNCS,
%SNMP::Info::LLDP::FUNCS,
# JUNIPER-VLAN-MIB::jnxExVlanTable
'v_index' => 'jnxExVlanTag',
'v_type' => 'jnxExVlanType',
'vlan_name' => 'jnxExVlanName',
# JUNIPER-VLAN-MIB::jnxExVlanPortGroupTable
'i_trunk' => 'jnxExVlanPortAccessMode',
# JUNPIER-MIB
'e_contents_type' => 'jnxContentsType',
'e_containers_type' => 'jnxContainersType',
'e_hwver' => 'jnxContentsRevision',
'v_fdb_id' => 'jnxL2aldVlanFdbId',
'v_vlan_tag' => 'jnxL2aldVlanTag',
'v_vlan_name' => 'jnxL2aldVlanName',
%GLOBALS = (
%SNMP::Info::Layer3::GLOBALS,
%SNMP::Info::LLDP::GLOBALS,
'serial' => 'jnxBoxSerialNo',
'mac' => 'dot1dBaseBridgeAddress',
'box_descr' => 'jnxBoxDescr',
'version' => 'jnxVirtualChassisMemberSWVersion',
'vc_model' => 'jnxVirtualChassisMemberModel',
);
%MUNGE = ( %SNMP::Info::Layer3::MUNGE,
%SNMP::Info::LLDP::MUNGE,
'e_containers_type' => \&SNMP::Info::munge_e_type,
'e_contents_type' => \&SNMP::Info::munge_e_type,
%FUNCS = (
%SNMP::Info::Layer3::FUNCS,
%SNMP::Info::LLDP::FUNCS,
# JUNIPER-VLAN-MIB::jnxExVlanPortGroupTable
'i_trunk' => 'jnxExVlanPortAccessMode',
# JUNIPER-MIB
'e_contents_type' => 'jnxContentsType',
'e_containers_type' => 'jnxContainersType',
'e_hwver' => 'jnxContentsRevision',
# JUNIPER-VLAN-MIB::jnxExVlanTable
'jnx_v_name' => 'jnxExVlanName',
'jnx_v_index' => 'jnxExVlanTag',
'jnx_v_type' => 'jnxExVlanType',
# JUNIPER-L2ALD-MIB::jnxL2aldVlanTable
# Used in Enhanced Layer 2 Software (ELS)
# replaces JUNIPER-VLAN-MIB::jnxExVlanTable
# in newer switches
'jnx_els_v_name' => 'jnxL2aldVlanName',
'jnx_els_v_index' => 'jnxL2aldVlanTag',
'jnx_els_v_type' => 'jnxL2aldVlanType',
'jnx_els_v_fdb_id' => 'jnxL2aldVlanFdbId',
);
%MUNGE = (
%SNMP::Info::Layer3::MUNGE,
%SNMP::Info::LLDP::MUNGE,
'e_containers_type' => \&SNMP::Info::munge_e_type,
'e_contents_type' => \&SNMP::Info::munge_e_type,
);
sub vendor {
@@ -173,44 +181,61 @@ sub i_trunk {
foreach (keys %$access)
{
my $old_key = $_;
m/^\d+\.(\d+)$/o;
my $new_key = $1;
$i_trunk{$new_key} = $access->{$old_key};
my $old_key = $_;
my $new_key = $old_key;
$new_key = $1 if m/^\d+\.(\d+)$/;
$i_trunk{$new_key} = $access->{$old_key};
}
return \%i_trunk;
}
# Note: For overridden VLAN methods where we have to distinguish between newer
# ELS and older models we try the newer methods first assuming they will be
# more common to save on network queries. Short circuit operation depends on
# leaf not present and snmp query returning undef
sub qb_fdb_index {
my $juniper = shift;
my $juniper = shift;
my $partial = shift;
return $juniper->jnxExVlanTag($partial);
# Lots of indirection here
my $v_index = $juniper->jnx_els_v_index($partial);
if ( ref {} eq ref $v_index and scalar keys %$v_index ) {
my $fdb_index = $juniper->jnx_els_v_fdb_id($partial);
my $qb_fdb_index = {};
for my $key ( keys(%$v_index) ) {
my $vlan = $v_index->{$key};
next unless defined $vlan;
my $fdb = $fdb_index->{$key};
next unless defined $fdb;
$qb_fdb_index->{$fdb} = $vlan;
}
return $qb_fdb_index;
}
$juniper->jnx_v_index($partial);
}
# 'v_type' => 'jnxExVlanType',
sub v_type {
my $juniper = shift;
my $partial = shift;
my $v_type = $juniper->jnxExVlanType($partial);
return $v_type;
return $juniper->jnx_els_v_type($partial)
|| $juniper->jnx_v_type($partial);
}
# 'v_index' => 'jnxExVlanTag',
sub v_index {
my ($juniper) = shift;
my ($partial) = shift;
my ($v_index) = $juniper->jnxExVlanTag($partial);
my ($v_vlan_index) = $juniper->v_vlan_tag($partial);
return $v_index unless $v_vlan_index;
return $v_vlan_index;
return $juniper->jnx_els_v_index($partial)
|| $juniper->jnx_v_index($partial);
}
sub i_vlan {
my $juniper = shift;
my $partial = shift;
@@ -223,13 +248,15 @@ sub i_vlan {
$partial = $r_index{$partial};
}
my $v_index = $juniper->jnxExVlanTag();
my $v_index = $juniper->v_index();
my $i_pvid = $juniper->qb_i_vlan($partial) || {};
my $i_vlan = {};
foreach my $bport ( keys %$i_pvid ) {
my $q_vlan = $i_pvid->{$bport};
my $vlan = $v_index->{$q_vlan} || $q_vlan;
my $vlan = $q_vlan;
# Use defined as check since VLAN can be zero
$vlan = $v_index->{$q_vlan} if defined $v_index->{$q_vlan};
my $ifindex = $index->{$bport};
unless ( defined $ifindex ) {
print " Port $bport has no bp_index mapping. Skipping.\n"
@@ -240,11 +267,11 @@ sub i_vlan {
}
return $i_vlan;
}
sub v_name {
my $juniper = shift;
my $name = $juniper->v_vlan_name();
return $juniper->vlan_name() unless $name;
return $name;
return $juniper->jnx_els_v_name() || $juniper->jnx_v_name();
}
# Index doesn't use VLAN ID, so override the HOA private method here to
@@ -254,7 +281,7 @@ sub _vlan_hoa {
my ( $v_ports, $partial ) = @_;
my $index = $juniper->bp_index();
my $v_index = $juniper->jnxExVlanTag($partial);
my $v_index = $juniper->v_index($partial);
my $vlan_hoa = {};
foreach my $idx ( keys %$v_ports ) {
@@ -282,36 +309,58 @@ sub _vlan_hoa {
return $vlan_hoa;
}
# Older switches use RFC 4363 standard PortList definition, newer ELS switches
# return an ASCII-encoded, comma separated string of port indexes - wow
sub i_vlan_membership {
my $juniper = shift;
my $juniper = shift;
my $partial = shift;
# Use presence of JUNIPER-VLAN-MIB::jnxExVlanTag to indicate if we should
# treat as RFC 4363 standard PortList
my $old_index = $juniper->jnx_v_index();
if ( scalar keys %$old_index ) {
return $juniper->SUPER::i_vlan_membership($partial);
}
my $res;
my $dot1qVlanStaticEgressPorts = $juniper->dot1qVlanCurrentEgressPorts($partial) || $juniper->dot1qVlanStaticEgressPorts($partial);
# This isn't a PortList so use _raw to prevent munge
my $v_ports = $juniper->qb_cv_egress_raw($partial)
|| $juniper->qb_v_egress_raw($partial);
my $bp_index = $juniper->bp_index();
foreach my $vlan (keys %$dot1qVlanStaticEgressPorts) {
my @bp_indexes = split /,/, $dot1qVlanStaticEgressPorts->{$vlan};
push @{$res->{$bp_index->{$_}}}, $vlan for @bp_indexes;
foreach my $vlan ( keys %$v_ports ) {
my @bp_indexes = split /,/, $v_ports->{$vlan};
push @{ $res->{ $bp_index->{$_} } }, $vlan for @bp_indexes;
}
return $res;
}
sub qb_fw_vlan {
sub i_vlan_membership_untagged {
my $juniper = shift;
my $partial = shift;
my $qb_fw_vlan = $juniper->SUPER::qb_fw_vlan();
my $v_fdb_id = $juniper->v_fdb_id();
my $v_vlan_tag = $juniper->v_vlan_tag();
return $qb_fw_vlan unless $v_fdb_id && $v_vlan_tag;
my %fdb_id_to_tag = reverse %$v_fdb_id;
# Use presence of JUNIPER-VLAN-MIB::jnxExVlanTag to indicate if we should
# treat as RFC 4363 standard PortList
my $old_index = $juniper->jnx_v_index();
foreach my $key (keys %$qb_fw_vlan) {
my $v = $qb_fw_vlan->{$key};
$qb_fw_vlan->{$key} = $v_vlan_tag->{$fdb_id_to_tag{$v}};
if ( scalar keys %$old_index ) {
return $juniper->SUPER::i_vlan_membership_untagged($partial);
}
return $qb_fw_vlan;
my $res;
# This isn't a PortList so use _raw to prevent munge
my $v_ports = $juniper->qb_cv_untagged_raw($partial)
|| $juniper->qb_v_untagged_raw($partial);
my $bp_index = $juniper->bp_index();
foreach my $vlan ( keys %$v_ports ) {
my @bp_indexes = split /,/, $v_ports->{$vlan};
push @{ $res->{ $bp_index->{$_} } }, $vlan for @bp_indexes;
}
return $res;
}
# Pseudo ENTITY-MIB methods
@@ -336,24 +385,27 @@ sub _e_is_virtual {
sub _e_virtual_index {
my $juniper = shift;
my $containers = $juniper->jnxContainersWithin() || {};
my $containers = $juniper->jnxContainersWithin() || {};
my $members = $juniper->jnxVirtualChassisMemberRole() || {};
my %v_index;
foreach my $key (keys %$containers) {
foreach my $member ( keys %$members ) {
# Virtual chassis members start at zero
$member++;
# We will be duplicating and eliminating some keys,
# but this is for the benefit of e_parent()
my $index = sprintf ("%02d", $key) . sprintf ("%02d", $member) . "0000";
my $iid = "$key\.$member\.0\.0";
$v_index{$iid} = $index;
}
unless ($containers->{$key}) {
my $index = sprintf ("%02d", $key) . "000000";
$v_index{$key} = $index;
}
foreach my $key ( keys %$containers ) {
foreach my $member ( keys %$members ) {
# Virtual chassis members start at zero
$member++;
# We will be duplicating and eliminating some keys,
# but this is for the benefit of e_parent()
my $index = sprintf( "%02d", $key )
. sprintf( "%02d", $member ) . "0000";
my $iid = "$key\.$member\.0\.0";
$v_index{$iid} = $index;
}
unless ( $containers->{$key} ) {
my $index = sprintf( "%02d", $key ) . "000000";
$v_index{$key} = $index;
}
}
return \%v_index;
}
@@ -398,7 +450,7 @@ sub e_class {
my $type = $fru_type->{$iid} || 0;
my $container = $c_type->{$iid} || 0;
if ( $type =~ /power/i ) {
$e_class{$iid} = 'powerSupply';
}
@@ -438,7 +490,7 @@ sub e_descr {
my $content_descr = $contents->{$iid} || 0;
my $container_descr = $containers->{$iid} || 0;
if ($content_descr) {
$e_descr{$iid} = $content_descr;
}
@@ -554,41 +606,46 @@ sub e_vendor {
sub e_pos {
my $juniper = shift;
# We could look at index levels, but his will work as well
# We could look at index levels, but this will work as well
return $juniper->e_index();
}
sub e_parent {
my $juniper = shift;
my $e_idx = $juniper->e_index() || {};
my $c_within = $juniper->jnxContainersWithin() || {};
my $e_descr = $juniper->e_descr() || {};
my $e_idx = $juniper->e_index() || {};
my $c_within = $juniper->jnxContainersWithin() || {};
my $e_descr = $juniper->e_descr() || {};
my $is_virtual = $juniper->_e_is_virtual();
my %e_parent;
foreach my $iid ( keys %$e_idx ) {
next unless $iid;
my ($idx, $l1,$l2, $l3) = split /\./, $iid;
my $within = $c_within->{$idx};
my $descr = $e_descr->{$iid};
if ( !$is_virtual and ($iid =~ /^(\d+)\.\d+/) ) {
$e_parent{$iid} = sprintf ("%02d", $1) . "000000";
my ( $idx, $l1, $l2, $l3 ) = split /\./, $iid;
my $within = $c_within->{$idx};
my $descr = $e_descr->{$iid};
if ( !$is_virtual and ( $iid =~ /^(\d+)\.\d+/ ) ) {
$e_parent{$iid} = sprintf( "%02d", $1 ) . "000000";
}
elsif ( $is_virtual
and ( $descr =~ /chassis/i )
and ( $iid =~ /^(\d+)\.(\d+)(\.0)+?/ ) )
{
$e_parent{$iid} = sprintf( "%02d", $1 ) . "000000";
}
elsif ( $is_virtual and ( $iid =~ /^(\d+)\.(\d+)(\.0)+?/ ) ) {
$e_parent{$iid}
= sprintf( "%02d", $within ) . sprintf( "%02d", $2 ) . "0000";
}
elsif ( $is_virtual and ( $iid =~ /^(\d+)\.(\d+)\.[1-9]+/ ) ) {
$e_parent{$iid}
= sprintf( "%02d", $1 ) . sprintf( "%02d", $2 ) . "0000";
}
elsif ( defined $within and $iid =~ /\d+/ ) {
$e_parent{$iid} = sprintf( "%02d", $within ) . "000000";
}
elsif ( $is_virtual and ($descr =~ /chassis/i) and ($iid =~ /^(\d+)\.(\d+)(\.0)+?/) ) {
$e_parent{$iid} = sprintf ("%02d", $1) . "000000";
}
elsif ( $is_virtual and ($iid =~ /^(\d+)\.(\d+)(\.0)+?/) ) {
$e_parent{$iid} = sprintf ("%02d", $within) . sprintf ("%02d", $2) . "0000";
}
elsif ( $is_virtual and ($iid =~ /^(\d+)\.(\d+)\.[1-9]+/) ) {
$e_parent{$iid} = sprintf ("%02d", $1) . sprintf ("%02d", $2) . "0000";
}
elsif ( defined $within and $iid =~ /\d+/ ) {
$e_parent{$iid} = sprintf ("%02d", $within) . "000000";
}
else {
next;
}
@@ -648,6 +705,8 @@ Subclass for Juniper Devices running JUNOS
=item F<JUNIPER-VIRTUALCHASSIS-MIB>
=item F<JUNIPER-L2ALD-MIB>
=back
=head2 Inherited Classes' MIBs
@@ -689,7 +748,7 @@ beginning.
Returns serial number
(C<jnxBoxSerialNo.0>)
(C<jnxBoxSerialNo>)
=item $juniper->mac()
@@ -702,7 +761,15 @@ to in a unique fashion.
The name, model, or detailed description of the device.
(C<jnxBoxDescr.0>)
(C<jnxBoxDescr>)
=item $juniper->version()
(C<jnxVirtualChassisMemberSWVersion>)
=item $juniper->vc_model()
(C<jnxVirtualChassisMemberModel>)
=back
@@ -723,23 +790,22 @@ to a hash.
=item $juniper->qb_fdb_index()
Returns reference to hash: key = VLAN ID, value = FDB ID.
=item $juniper->qb_fw_vlan()
Returns reference to hash of forwarding table entries VLAN ID
Returns reference to hash: key = FDB ID, value = VLAN ID.
=item $juniper->v_index()
(C<jnxExVlanTag>)
Returns (C<jnxL2aldVlanTag>) or (C<jnxExVlanTag>) depending upon switch
software version
=item $juniper->v_name()
(C<jnxExVlanName>)
Returns (C<jnxL2aldVlanName>) or (C<jnxExVlanName>) depending upon switch
software version
=item $juniper->v_type()
(C<jnxExVlanType>)
Returns (C<jnxL2aldVlanType>) or (C<jnxExVlanType>) depending upon switch
software version
=item $juniper->i_trunk()
@@ -754,6 +820,12 @@ Returns a mapping between C<ifIndex> and the PVID or default VLAN.
Returns reference to hash of arrays: key = C<ifIndex>, value = array of VLAN
IDs. These are the VLANs which are members of the egress list for the port.
=item $juniper->i_vlan_membership_untagged()
Returns reference to hash of arrays: key = C<ifIndex>, value = array of VLAN
IDs. These are the VLANs which are members of the untagged egress list for
the port.
=back
=head2 Pseudo F<ENTITY-MIB> information

View File

@@ -33,14 +33,6 @@ use Test::Class::Most parent => 'My::Test::Class';
use SNMP::Info::Layer3::Juniper;
# Remove this startup override once we have full method coverage
sub startup : Tests(startup => 1) {
my $test = shift;
$test->SUPER::startup();
$test->todo_methods(1);
}
sub setup : Tests(setup) {
my $test = shift;
$test->SUPER::setup;
@@ -48,15 +40,818 @@ sub setup : Tests(setup) {
# Start with a common cache that will serve most tests
my $d_string = 'Juniper Networks, Inc. ex4200-24t internet router, ';
$d_string .= 'kernel JUNOS 10.4R6.5 ';
my $cache_data = {
'_layers' => 6,
'_description' => $d_string,
# JUNIPER-CHASSIS-DEFINES-MIB::jnxProductNameEX4200
'_id' => '.1.3.6.1.4.1.2636.1.1.1.2.31',
'store' => {},
'_id' => '.1.3.6.1.4.1.2636.1.1.1.2.31',
'_version' => '14.1X53-D40.8',
'_serial' => 'AB0123456789',
'_jnxExVlanPortAccessMode' => 1,
'_jnx_v_name' => 1,
'_jnx_v_index' => 1,
'_jnx_v_type' => 1,
# Newer table doesn't exist in EX4200, but in cache to simplify testing
# method code
'_jnx_els_v_name' => 1,
'_jnx_els_v_index' => 1,
'_jnx_els_v_type' => 1,
'_jnx_els_v_fdb_id' => 1,
'_bp_index' => 1,
'_qb_i_vlan' => 1,
'_qb_v_egress' => 1,
'_qb_v_untagged' => 1,
'_qb_fw_port' => 1,
# For pseudo entity testing
'_box_descr' => 'Juniper Virtual Chassis Ethernet Switch',
'_jnxVirtualChassisMemberRole' => 'master',
'_jnxContainersWithin' => 1,
'_jnxContentsDescr' => 1,
'_jnxContentsSerialNo' => 1,
'_jnxFruType' => 1,
'_jnxContainersDescr' => 1,
'_e_containers_type' => 1,
'_jnxContentsPartNo' => 1,
'_e_contents_type' => 1,
'_jnxContainersWithin' => 1,
'store' => {
'jnxExVlanPortAccessMode' => {'2.514' => 'access', '7.513' => 'trunk'},
'jnx_v_name' => {2 => 'default', 3 => 'management'},
'jnx_v_index' => {2 => 0, 3 => 120},
'jnx_v_type' => {2 => 'static', 3 => 'static'},
# Newer table doesn't exist in EX4200, but in cache to simplify testing
# method code
'jnx_els_v_name' =>
{6 => 'VLAN0114_VPN78087', 7 => 'VLAN2088_VPN78117', 8 => 'default'},
'jnx_els_v_index' => {6 => 114, 7 => 2088, 8 => 1},
'jnx_els_v_type' => {6 => 'static', 7 => 'static', 8 => 'static'},
'jnx_els_v_fdb_id' => {6 => 393216, 7 => 458752, 8 => 524288},
'bp_index' => {4 => 662, 491 => 509, 492 => 510},
'qb_i_vlan' => {4 => 0, 491 => 113, 492 => 2088},
'qb_v_egress' => {114 => '4', 2088 => '491,492'},
'qb_v_untagged' => {114 => '4', 2088 => '491,492'},
'qb_fw_port' =>
{'393216.0.19.149.30.221.37' => 4, '458752.0.9.245.16.59.121' => 492},
'jnxContainersWithin' => {1 => 0, 2 => 1, 4 => 1, 7 => 1, 8 => 7, 9 => 1},
'jnxContentsDescr' => {
'1.1.0.0' => '',
'2.1.1.0' => 'Power Supply: Power Supply 0 @ 0/0/*',
'2.1.2.0' => 'Power Supply: Power Supply 1 @ 0/1/*',
'2.2.1.0' => 'Power Supply: Power Supply 0 @ 1/0/*',
'2.2.2.0' => 'Power Supply: Power Supply 1 @ 1/1/*',
'4.1.1.1' => 'FAN 0 @ 0/0/0',
'4.1.2.1' => 'FAN 1 @ 0/1/0',
'4.2.1.1' => 'FAN 0 @ 1/0/0',
'4.2.2.1' => 'FAN 1 @ 1/1/0',
'7.1.0.0' => 'FPC: EX4300-48T @ 0/*/*',
'7.2.0.0' => 'FPC: EX4300-48T @ 1/*/*',
'8.1.1.0' => 'PIC: 48x 10/100/1000 Base-T @ 0/0/*',
'8.1.2.0' => 'PIC: 4x 40GE QSFP+ @ 0/1/*',
'8.1.3.0' => 'PIC: 4x 1G/10G SFP/SFP+ @ 0/2/*',
'8.2.1.0' => 'PIC: 48x 10/100/1000 Base-T @ 1/0/*',
'8.2.2.0' => 'PIC: 4x 40GE QSFP+ @ 1/1/*',
'8.2.3.0' => 'PIC: 4x 1G/10G SFP/SFP+ @ 1/2/*',
'9.1.0.0' => 'Routing Engine 0',
'9.2.0.0' => 'Routing Engine 1'
},
'jnxContentsSerialNo' => {
'2.1.1.0' => '1EDD1234567',
'2.1.2.0' => '1EDD2345678',
'2.2.1.0' => '1EDD3456789',
'2.2.2.0' => '1EDD4567890',
'7.1.0.0' => 'PE1234567890',
'7.2.0.0' => 'PE0123456789',
'8.1.1.0' => 'BUILTIN',
'8.1.2.0' => 'BUILTIN',
'8.1.3.0' => 'MY1234567890',
'8.2.1.0' => 'BUILTIN',
'8.2.2.0' => 'BUILTIN',
'8.2.3.0' => 'MY0123456789',
'9.1.0.0' => 'PE2345678901',
'9.2.0.0' => 'PE3456789012'
},
'jnxFruType' => {
'2.1.1.0' => 'powerEntryModule',
'2.1.2.0' => 'powerEntryModule',
'2.2.1.0' => 'powerEntryModule',
'2.2.2.0' => 'powerEntryModule',
'4.1.1.1' => 'fan',
'4.1.2.1' => 'fan',
'4.2.1.1' => 'fan',
'4.2.2.1' => 'fan',
'7.1.0.0' => 'flexiblePicConcentrator',
'7.2.0.0' => 'flexiblePicConcentrator',
'8.1.1.0' => 'portInterfaceCard',
'8.1.2.0' => 'portInterfaceCard',
'8.1.3.0' => 'portInterfaceCard',
'8.2.1.0' => 'portInterfaceCard',
'8.2.2.0' => 'portInterfaceCard',
'8.2.3.0' => 'portInterfaceCard',
'9.1.0.0' => 'routingEngine',
'9.2.0.0' => 'routingEngine'
},
'jnxContentsPartNo' => {
'2.1.1.0' => '740-046876',
'2.1.2.0' => '740-046876',
'2.2.1.0' => '740-046876',
'2.2.2.0' => '740-046876',
'7.1.0.0' => '650-044932',
'7.2.0.0' => '650-044932',
'8.1.1.0' => 'BUILTIN',
'8.1.2.0' => 'BUILTIN',
'8.1.3.0' => '611-063980',
'8.2.1.0' => 'BUILTIN',
'8.2.2.0' => 'BUILTIN',
'8.2.3.0' => '611-063980',
'9.1.0.0' => '650-044932',
'9.2.0.0' => '650-044932'
},
# These are cached raw
'e_contents_type' => {
'1.1.0.0' => '.1.3.6.1.4.1.2636.1.1.2.1.63.0',
'2.1.1.0' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.1.0',
'2.1.2.0' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.1.0',
'2.2.1.0' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.1.0',
'2.2.2.0' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.1.0',
'4.1.1.1' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.2.0',
'4.1.2.1' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.2.0',
'4.2.1.1' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.2.0',
'4.2.2.1' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.2.0',
'7.1.0.0' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.0',
'7.2.0.0' => '.1.3.6.1.4.1.2636.1.1.3.2.63.1.0',
'8.1.1.0' => '.1.3.6.1.4.1.2636.1.1.1.4.63.2.0',
'8.1.2.0' => '.1.3.6.1.4.1.2636.1.1.3.3.12.1.319.0',
'8.1.3.0' => '.1.3.6.1.4.1.2636.1.1.3.3.12.1.320.0',
'8.2.1.0' => '.1.3.6.1.4.1.2636.1.1.1.4.63.2.0',
'8.2.2.0' => '.1.3.6.1.4.1.2636.1.1.3.3.12.1.319.0',
'8.2.3.0' => '.1.3.6.1.4.1.2636.1.1.3.3.12.1.320.0',
'9.1.0.0' => '.1.3.6.1.4.1.2636.1.1.2.1.63.1.0',
'9.2.0.0' => '.1.3.6.1.4.1.2636.1.1.2.1.63.1.0'
},
'jnxContainersDescr' => {
1 => 'chassis frame',
2 => 'Power Supply slot',
4 => 'FAN slot',
7 => 'FPC slot',
8 => 'PIC slot',
9 => 'Routing Engine slot'
},
# These are cached raw
'e_containers_type' => {
1 => '.1.3.6.1.4.1.2636.1.1.2.1.63.0',
2 => '.1.3.6.1.4.1.2636.1.1.2.2.63.1.1.0',
4 => '.1.3.6.1.4.1.2636.1.1.2.2.63.1.2.0',
7 => '.1.3.6.1.4.1.2636.1.1.2.2.63.1.0',
8 => '.1.3.6.1.4.1.2636.1.1.2.3.63.1.0',
9 => '.1.3.6.1.4.1.2636.1.1.2.1.63.1.0'
},
'jnxContainersWithin' => {1 => 0, 2 => 1, 4 => 1, 7 => 1, 8 => 7, 9 => 1}
}
};
$test->{info}->cache($cache_data);
}
sub vendor : Tests(2) {
my $test = shift;
can_ok($test->{info}, 'vendor');
is($test->{info}->vendor(), 'juniper', q(Vendor returns 'juniper'));
}
sub os : Tests(2) {
my $test = shift;
can_ok($test->{info}, 'os');
is($test->{info}->os(), 'junos', q(OS returns 'junos'));
}
sub layers : Tests(5) {
my $test = shift;
can_ok($test->{info}, 'layers');
is($test->{info}->layers(), '00000110', q(Original layers unmodified));
# Set to layer 3 only and with presence of FDB 'qb_fw_port' will turn on
# layer 2
$test->{info}{_layers} = 4;
is($test->{info}->layers(),
'00000110', q(Layer2 added due to presence of FDB));
# Delete FDB cache flag, layers still = 4 binary (3 only)
delete $test->{info}{_qb_fw_port};
is($test->{info}->layers(), '00000100', q(No layer2 without FDB));
$test->{info}->clear_cache();
is($test->{info}->layers(), undef, q(No data returns undef layers));
}
sub os_ver : Tests(5) {
my $test = shift;
can_ok($test->{info}, 'os_ver');
is($test->{info}->os_ver(),
'14.1X53-D40.8',
q(OS version returned from 'jnxVirtualChassisMemberSWVersion'));
delete $test->{info}{_version};
is($test->{info}->os_ver(),
'10.4R6.5', q(OS version returned from 'sysDescr'));
delete $test->{info}{_description};
$test->{info}{_lldp_sysdesc}
= 'Juniper Networks, Inc. srx240h-poe , version 12.1R3.5 Build date: ';
is($test->{info}->os_ver(),
'12.1R3.5', q(OS version returned from 'lldpLocSysDesc'));
$test->{info}->clear_cache();
is($test->{info}->os_ver(), undef, q(No data returns undef OS version));
}
sub model : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'model');
is($test->{info}->model(), 'EX4200', q(Model translates id));
$test->{info}{_id} = '.100.3.6.1.4.1.6527.1.3.1';
is($test->{info}->model(), '.100.3.6.1.4.1.6527.1.3.1', q(Model uses id));
$test->{info}{_vc_model} = 'qfx5100-48s-6q';
is($test->{info}->model(),
'QFX5100-48S-6Q', q(Model uses 'jnxVirtualChassisMemberModel'));
}
sub serial : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'serial');
is($test->{info}->serial(),
'AB0123456789', q(Serial returns 'jnxBoxSerialNo'));
$test->{info}->clear_cache();
is($test->{info}->serial(), undef, q(No data returns undef serial));
}
sub i_trunk : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'i_trunk');
my $expected = {'514' => 'access', '513' => 'trunk'};
cmp_deeply($test->{info}->i_trunk(),
$expected, q(Interface trunk status returns expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->i_trunk(), {}, q(No data returns empty hash));
}
sub qb_fdb_index : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'qb_fdb_index');
my $expected = {393216 => 114, 458752 => 2088, 524288 => 1};
cmp_deeply($test->{info}->qb_fdb_index(),
$expected, q(ELS VLAN to FDB index returned expected values));
delete $test->{info}{'_jnx_els_v_index'};
$expected = {2 => 0, 3 => 120};
cmp_deeply($test->{info}->qb_fdb_index(),
$expected, q(Older VLAN to FDB index returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->qb_fdb_index(), undef, q(No data returns undef));
}
sub v_type : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'v_type');
my $expected = {6 => 'static', 7 => 'static', 8 => 'static'};
cmp_deeply($test->{info}->v_type(),
$expected, q(ELS VLAN types returned expected values));
delete $test->{info}{'_jnx_els_v_type'};
$expected = {2 => 'static', 3 => 'static'};
cmp_deeply($test->{info}->v_type(),
$expected, q(Older VLAN types returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->v_type(), undef, q(No data returns undef));
}
sub v_index : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'v_index');
my $expected = {6 => 114, 7 => 2088, 8 => 1};
cmp_deeply($test->{info}->v_index(),
$expected, q(ELS VLAN index returned expected values));
delete $test->{info}{'_jnx_els_v_index'};
$expected = {2 => 0, 3 => 120};
cmp_deeply($test->{info}->v_index(),
$expected, q(Older VLAN index returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->v_index(), undef, q(No data returns undef));
}
sub i_vlan : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'i_vlan');
my $expected = {662 => 0, 509 => 113, 510 => 2088};
cmp_deeply($test->{info}->i_vlan(),
$expected, q(ELS PVID returned expected values));
delete $test->{info}{'_jnx_els_v_index'};
$test->{info}{store}{bp_index} = {513 => 505, 514 => 507};
$test->{info}{store}{qb_i_vlan} = {513 => 3, 514 => 2};
$expected = {505 => 120, 507 => 0};
cmp_deeply($test->{info}->i_vlan(),
$expected, q(Older PVID returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->i_vlan(), {}, q(No data returns empty hash));
}
sub v_name : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'v_name');
my $expected
= {6 => 'VLAN0114_VPN78087', 7 => 'VLAN2088_VPN78117', 8 => 'default'};
cmp_deeply($test->{info}->v_name(),
$expected, q(ELS VLAN names returned expected values));
delete $test->{info}{'_jnx_els_v_name'};
$expected = {2 => 'default', 3 => 'management'};
cmp_deeply($test->{info}->v_name(),
$expected, q(Older VLAN names returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->v_name(), undef, q(No data returns undef));
}
sub i_vlan_membership : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'i_vlan_membership');
# Delete this so we can test new method first
delete $test->{info}{'_jnx_v_index'};
my $expected = {662 => [114], 509 => [2088], 510 => [2088]};
cmp_deeply($test->{info}->i_vlan_membership(),
$expected, q(ELS VLAN membership returned expected values));
# Restore so we can trigger test for older devices
$test->{info}{'_jnx_v_index'} = 1;
delete $test->{info}{'_jnx_els_v_index'};
my $padding = '00000000000000000000000000000000' x 4;
# This has bits 516 - 536 on
my $portlist2 = $padding . '1FFFFF';
# This has bits 512 - 515 on
my $portlist3 = $padding . 'E0';
# To simplify the expected value, we're only going to define the mapping
# for five ports
$test->{info}{store}{bp_index}
= {513 => 505, 514 => 507, 515 => 509, 516 => 511, 536 => 551};
$test->{info}{store}{qb_v_egress}
= {2 => pack("H*", $portlist2), 3 => pack("H*", $portlist3)};
$expected
= {505 => [120], 507 => [120], 509 => [120], 511 => [0], 551 => [0]};
cmp_deeply($test->{info}->i_vlan_membership(),
$expected, q(VLAN membership returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->i_vlan_membership(),
undef, q(No data returns undef));
}
# Same code as i_vlan_membership, untagged just uses different leaf
sub i_vlan_membership_untagged : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'i_vlan_membership_untagged');
# Delete this so we can test new method first
delete $test->{info}{'_jnx_v_index'};
my $expected = {662 => [114], 509 => [2088], 510 => [2088]};
cmp_deeply($test->{info}->i_vlan_membership_untagged(),
$expected, q(ELS VLAN membership untagged returned expected values));
# Restore so we can trigger test for older devices
$test->{info}{'_jnx_v_index'} = 1;
delete $test->{info}{'_jnx_els_v_index'};
my $padding = '00000000000000000000000000000000' x 4;
# This has bits 516 - 536 on
my $portlist2 = $padding . '1FFFFF';
# This has bits 512 - 515 on
my $portlist3 = $padding . 'E0';
# To simplify the expected value, we're only going to define the mapping
# for five ports
$test->{info}{store}{bp_index}
= {513 => 505, 514 => 507, 515 => 509, 516 => 511, 536 => 551};
$test->{info}{store}{qb_v_untagged}
= {2 => pack("H*", $portlist2), 3 => pack("H*", $portlist3)};
$expected
= {505 => [120], 507 => [120], 509 => [120], 511 => [0], 551 => [0]};
cmp_deeply($test->{info}->i_vlan_membership_untagged(),
$expected, q(VLAN membership untagged returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->i_vlan_membership_untagged(),
undef, q(No data returns undef));
}
# These is no longer defined in the class, but tested due to past issues with
# VLAN mapping due to changes with the introduction of ELS. See issue #67
# Juniper EX4300 Missing/Wrong information #67
sub qb_fw_vlan : Tests(4) {
my $test = shift;
can_ok($test->{info}, 'qb_fw_vlan');
my $expected
= {'393216.0.19.149.30.221.37' => 114, '458752.0.9.245.16.59.121' => 2088};
cmp_deeply($test->{info}->qb_fw_vlan(),
$expected,
q(ELS forwarding table entries to VLAN IDs returned expected values));
delete $test->{info}{'_jnx_els_v_index'};
$test->{info}{store}{qb_fw_port}
= {'2.0.19.149.30.221.37' => 513, '3.0.9.245.16.59.121' => 507};
$expected = {'2.0.19.149.30.221.37' => 0, '3.0.9.245.16.59.121' => 120};
cmp_deeply($test->{info}->qb_fw_vlan(),
$expected,
q(Older forwarding table entries to VLAN IDs returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->qb_fw_vlan(), {}, q(No data returns empty hash));
}
sub e_index : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_index');
my $expected = {
'1' => '01000000',
'1.1.0.0' => '01010000',
'2' => '02000000',
'2.1.1.0' => '02010100',
'2.1.2.0' => '02010200',
'2.2.1.0' => '02020100',
'2.2.2.0' => '02020200',
'4' => '04000000',
'4.1.1.1' => '04010101',
'4.1.2.1' => '04010201',
'4.2.1.1' => '04020101',
'4.2.2.1' => '04020201',
'7' => '07000000',
'7.1.0.0' => '07010000',
'7.2.0.0' => '07020000',
'8' => '08000000',
'8.1.1.0' => '08010100',
'8.1.2.0' => '08010200',
'8.1.3.0' => '08010300',
'8.2.1.0' => '08020100',
'8.2.2.0' => '08020200',
'8.2.3.0' => '08020300',
'9' => '09000000',
'9.1.0.0' => '09010000',
'9.2.0.0' => '09020000'
};
cmp_deeply($test->{info}->e_index(),
$expected, q(Entity index returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_index(), {}, q(No data returns empty hash));
}
sub e_class : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_class');
my $expected = {
'1' => 'chassis',
'1.1.0.0' => 'container',
'2' => 'container',
'2.1.1.0' => 'powerSupply',
'2.1.2.0' => 'powerSupply',
'2.2.1.0' => 'powerSupply',
'2.2.2.0' => 'powerSupply',
'4' => 'container',
'4.1.1.1' => 'fan',
'4.1.2.1' => 'fan',
'4.2.1.1' => 'fan',
'4.2.2.1' => 'fan',
'7' => 'container',
'7.1.0.0' => 'module',
'7.2.0.0' => 'module',
'8' => 'container',
'8.1.1.0' => 'module',
'8.1.2.0' => 'module',
'8.1.3.0' => 'module',
'8.2.1.0' => 'module',
'8.2.2.0' => 'module',
'8.2.3.0' => 'module',
'9' => 'container',
'9.1.0.0' => 'module',
'9.2.0.0' => 'module'
};
cmp_deeply($test->{info}->e_class(),
$expected, q(Entity classes returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_class(), {}, q(No data returns empty hash));
}
sub e_descr : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_descr');
my $expected = {
'1' => 'Juniper Virtual Chassis Ethernet Switch',
'1.1.0.0' => 'chassis frame',
'2' => 'Power Supply slot',
'2.1.1.0' => 'Power Supply: Power Supply 0 @ 0/0/*',
'2.1.2.0' => 'Power Supply: Power Supply 1 @ 0/1/*',
'2.2.1.0' => 'Power Supply: Power Supply 0 @ 1/0/*',
'2.2.2.0' => 'Power Supply: Power Supply 1 @ 1/1/*',
'4' => 'FAN slot',
'4.1.1.1' => 'FAN 0 @ 0/0/0',
'4.1.2.1' => 'FAN 1 @ 0/1/0',
'4.2.1.1' => 'FAN 0 @ 1/0/0',
'4.2.2.1' => 'FAN 1 @ 1/1/0',
'7' => 'FPC slot',
'7.1.0.0' => 'FPC: EX4300-48T @ 0/*/*',
'7.2.0.0' => 'FPC: EX4300-48T @ 1/*/*',
'8' => 'PIC slot',
'8.1.1.0' => 'PIC: 48x 10/100/1000 Base-T @ 0/0/*',
'8.1.2.0' => 'PIC: 4x 40GE QSFP+ @ 0/1/*',
'8.1.3.0' => 'PIC: 4x 1G/10G SFP/SFP+ @ 0/2/*',
'8.2.1.0' => 'PIC: 48x 10/100/1000 Base-T @ 1/0/*',
'8.2.2.0' => 'PIC: 4x 40GE QSFP+ @ 1/1/*',
'8.2.3.0' => 'PIC: 4x 1G/10G SFP/SFP+ @ 1/2/*',
'9' => 'Routing Engine slot',
'9.1.0.0' => 'Routing Engine 0',
'9.2.0.0' => 'Routing Engine 1'
};
cmp_deeply($test->{info}->e_descr(),
$expected, q(Entity descriptions returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_descr(), {}, q(No data returns empty hash));
}
sub e_serial : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_serial');
my $expected = {
'1' => 'AB0123456789',
'2.1.1.0' => '1EDD1234567',
'2.1.2.0' => '1EDD2345678',
'2.2.1.0' => '1EDD3456789',
'2.2.2.0' => '1EDD4567890',
'7.1.0.0' => 'PE1234567890',
'7.2.0.0' => 'PE0123456789',
'8.1.3.0' => 'MY1234567890',
'8.2.3.0' => 'MY0123456789',
'9.1.0.0' => 'PE2345678901',
'9.2.0.0' => 'PE3456789012'
};
cmp_deeply($test->{info}->e_serial(),
$expected, q(Entity serial numbers returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_serial(), {}, q(No data returns empty hash));
}
sub e_fru : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_fru');
my $expected = {
'1' => 'false',
'1.1.0.0' => 'false',
'2' => 'false',
'2.1.1.0' => 'true',
'2.1.2.0' => 'true',
'2.2.1.0' => 'true',
'2.2.2.0' => 'true',
'4' => 'false',
'4.1.1.1' => 'false',
'4.1.2.1' => 'false',
'4.2.1.1' => 'false',
'4.2.2.1' => 'false',
'7' => 'false',
'7.1.0.0' => 'true',
'7.2.0.0' => 'true',
'8' => 'false',
'8.1.1.0' => 'false',
'8.1.2.0' => 'false',
'8.1.3.0' => 'true',
'8.2.1.0' => 'false',
'8.2.2.0' => 'false',
'8.2.3.0' => 'true',
'9' => 'false',
'9.1.0.0' => 'true',
'9.2.0.0' => 'true'
};
cmp_deeply($test->{info}->e_fru(),
$expected, q(Entity FRUs returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_fru(), {}, q(No data returns empty hash));
}
sub e_type : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_type');
my $expected = {
'1' => 'jnxChassisEX4300',
'2' => 'jnxEX4300SlotPower',
'1.1.0.0' => 'jnxChassisEX4300',
'2.1.1.0' => 'jnxEX4300Power',
'2.1.2.0' => 'jnxEX4300Power',
'2.2.1.0' => 'jnxEX4300Power',
'2.2.2.0' => 'jnxEX4300Power',
'4' => 'jnxEX4300SlotFan',
'4.1.1.1' => 'jnxEX4300Fan',
'4.1.2.1' => 'jnxEX4300Fan',
'4.2.1.1' => 'jnxEX4300Fan',
'4.2.2.1' => 'jnxEX4300Fan',
'7' => 'jnxEX4300SlotFPC',
'7.1.0.0' => 'jnxEX4300FPC',
'7.2.0.0' => 'jnxEX4300FPC',
'8' => 'jnxEX4300MediaCardSpacePIC',
'8.1.1.0' => 'jnxProductEX4300port48T',
'8.1.2.0' => 'jnxPicEX4300QSFP4Port',
'8.1.3.0' => 'jnxPicEX4300UplinkSFPPlus4Port',
'8.2.1.0' => 'jnxProductEX4300port48T',
'8.2.2.0' => 'jnxPicEX4300QSFP4Port',
'8.2.3.0' => 'jnxPicEX4300UplinkSFPPlus4Port',
'9' => 'jnxEX4300RE0',
'9.1.0.0' => 'jnxEX4300RE0',
'9.2.0.0' => 'jnxEX4300RE0'
};
cmp_deeply($test->{info}->e_type(),
$expected, q(Entity types returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_type(), {}, q(No data returns empty hash));
}
sub e_vendor : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_vendor');
my $expected = {
'1' => 'juniper',
'2' => 'juniper',
'1.1.0.0' => 'juniper',
'2.1.1.0' => 'juniper',
'2.1.2.0' => 'juniper',
'2.2.1.0' => 'juniper',
'2.2.2.0' => 'juniper',
'4' => 'juniper',
'4.1.1.1' => 'juniper',
'4.1.2.1' => 'juniper',
'4.2.1.1' => 'juniper',
'4.2.2.1' => 'juniper',
'7' => 'juniper',
'7.1.0.0' => 'juniper',
'7.2.0.0' => 'juniper',
'8' => 'juniper',
'8.1.1.0' => 'juniper',
'8.1.2.0' => 'juniper',
'8.1.3.0' => 'juniper',
'8.2.1.0' => 'juniper',
'8.2.2.0' => 'juniper',
'8.2.3.0' => 'juniper',
'9' => 'juniper',
'9.1.0.0' => 'juniper',
'9.2.0.0' => 'juniper'
};
cmp_deeply($test->{info}->e_vendor(),
$expected, q(Entity vendor returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_vendor(), {}, q(No data returns empty hash));
}
sub e_pos : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_pos');
my $expected = {
'1' => '01000000',
'1.1.0.0' => '01010000',
'2' => '02000000',
'2.1.1.0' => '02010100',
'2.1.2.0' => '02010200',
'2.2.1.0' => '02020100',
'2.2.2.0' => '02020200',
'4' => '04000000',
'4.1.1.1' => '04010101',
'4.1.2.1' => '04010201',
'4.2.1.1' => '04020101',
'4.2.2.1' => '04020201',
'7' => '07000000',
'7.1.0.0' => '07010000',
'7.2.0.0' => '07020000',
'8' => '08000000',
'8.1.1.0' => '08010100',
'8.1.2.0' => '08010200',
'8.1.3.0' => '08010300',
'8.2.1.0' => '08020100',
'8.2.2.0' => '08020200',
'8.2.3.0' => '08020300',
'9' => '09000000',
'9.1.0.0' => '09010000',
'9.2.0.0' => '09020000'
};
cmp_deeply($test->{info}->e_pos(),
$expected, q(Entity position returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_pos(), {}, q(No data returns empty hash));
}
sub e_parent : Tests(3) {
my $test = shift;
can_ok($test->{info}, 'e_parent');
my $expected = {
'1' => '00000000',
'1.1.0.0' => '01000000',
'2' => '01000000',
'2.1.1.0' => '02000000',
'2.1.2.0' => '02000000',
'2.2.1.0' => '02000000',
'2.2.2.0' => '02000000',
'4' => '01000000',
'4.1.1.1' => '04000000',
'4.1.2.1' => '04000000',
'4.2.1.1' => '04000000',
'4.2.2.1' => '04000000',
'7' => '01000000',
'7.1.0.0' => '07000000',
'7.2.0.0' => '07000000',
'8' => '07000000',
'8.1.1.0' => '08000000',
'8.1.2.0' => '08000000',
'8.1.3.0' => '08000000',
'8.2.1.0' => '08000000',
'8.2.2.0' => '08000000',
'8.2.3.0' => '08000000',
'9' => '01000000',
'9.1.0.0' => '09000000',
'9.2.0.0' => '09000000'
};
cmp_deeply($test->{info}->e_parent(),
$expected, q(Entity parent returned expected values));
$test->{info}->clear_cache();
cmp_deeply($test->{info}->e_parent(), {}, q(No data returns empty hash));
}
1;