diff --git a/Changes b/Changes index 6f49c112..b5cbddb2 100644 --- a/Changes +++ b/Changes @@ -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 diff --git a/lib/SNMP/Info/Layer3/Juniper.pm b/lib/SNMP/Info/Layer3/Juniper.pm index 490ececf..b5e64861 100644 --- a/lib/SNMP/Info/Layer3/Juniper.pm +++ b/lib/SNMP/Info/Layer3/Juniper.pm @@ -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 +=item F + =back =head2 Inherited Classes' MIBs @@ -689,7 +748,7 @@ beginning. Returns serial number -(C) +(C) =item $juniper->mac() @@ -702,7 +761,15 @@ to in a unique fashion. The name, model, or detailed description of the device. -(C) +(C) + +=item $juniper->version() + +(C) + +=item $juniper->vc_model() + +(C) =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) +Returns (C) or (C) depending upon switch +software version =item $juniper->v_name() -(C) +Returns (C) or (C) depending upon switch +software version =item $juniper->v_type() -(C) +Returns (C) or (C) depending upon switch +software version =item $juniper->i_trunk() @@ -754,6 +820,12 @@ Returns a mapping between C and the PVID or default VLAN. Returns reference to hash of arrays: key = C, 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, value = array of VLAN +IDs. These are the VLANs which are members of the untagged egress list for +the port. + =back =head2 Pseudo F information diff --git a/xt/lib/Test/SNMP/Info/Layer3/Juniper.pm b/xt/lib/Test/SNMP/Info/Layer3/Juniper.pm index 5e40f1fa..92ab3910 100644 --- a/xt/lib/Test/SNMP/Info/Layer3/Juniper.pm +++ b/xt/lib/Test/SNMP/Info/Layer3/Juniper.pm @@ -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;