add SNMP support for modular devices
authorAaron Dummer <aaron@dummer.info>
Fri, 26 Dec 2014 21:25:47 +0000 (13:25 -0800)
committerAaron Dummer <aaron@dummer.info>
Fri, 26 Dec 2014 21:25:47 +0000 (13:25 -0800)
- Cisco 2801 (#779)
- Cisco 2821 (#775)
- Cisco 7206VXR (#483)
- Cisco WS-C6509-E (#338)
- Csico WS-C4503 (#528)
- HP J8698A (#351)
trigger_snmpportfinder: add support for network chassis objects
doSwitchSNMPMining: renamed to doGenericSNMPMining
generatePortsForCatModule: removed unused function
nextMACAddress: idem

display ports & IPs of contained objects (#797)
commitUpdatePort: remove superfluous $object_id parameter
findAllEndpoints: also find IPv6 endpoints
amplifyCell: retrieve child object details (names, ports, IP allocations)
getDictionaryEntry: new function to return details of a single entry
getObjectPortsAndLinks: add $include_children parameter
getObjectIPAllocationList: new wrapper function returns both IPv4 and IPv6 allocations
fetchObjectIPvNAllocationList: new generic function to return either IPv4 or IPv6 info
getObjectIPvNAllocationList: idem
getObjectIPv4AllocationList: deprecated, left for compatibility reasons
getObjectIPv6AllocationList: idem
getObjectIPv4Allocations: use getObjectIPvNAllocationList
getObjectIPv6Allocations: idem
renderObjectPortRow: migrated to renderObject, function was and would always be only used once
renderObject: display details of child objects inline
renderPortsForObject: idem
renderIPForObject: idem

ChangeLog
wwwroot/inc/database.php
wwwroot/inc/dictionary.php
wwwroot/inc/functions.php
wwwroot/inc/interface.php
wwwroot/inc/ophandlers.php
wwwroot/inc/snmp.php
wwwroot/inc/triggers.php
wwwroot/inc/upgrade.php

index 0a70aa0..bd5a0bf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 0.21.0
        update: explicitly require PHP version 5.3.0 or newer
+       update: SNMP support for Cisco 2801 (#779), 2821 (#775), 7206VXR (#483),
+               WS-C6509-E (#338), WS-C4503 (#528), HP J8698A (#351) by Aaron Dummer
+       update: display ports & IPs of contained objects (#797)
 0.20.9
        update: SNMP support for Cisco CGS-2520-24TC (#1259), WS-C2960X-24TS-LL (#1205),
                HP J8693A (#1251), HP J9728A (#1255), Linksys SRW2024P (#1183)
index a18b1bb..162b813 100644 (file)
@@ -726,11 +726,12 @@ function amplifyCell (&$record, $dummy = NULL)
        switch ($record['realm'])
        {
        case 'object':
-               $record['ports'] = getObjectPortsAndLinks ($record['id']);
-               $record['ipv4'] = getObjectIPv4Allocations ($record['id']);
-               $record['ipv6'] = getObjectIPv6Allocations ($record['id']);
+               $record['ports'] = getObjectPortsAndLinks ($record['id'], TRUE);
+               $record['ipv4'] = getObjectIPv4Allocations ($record['id'], TRUE);
+               $record['ipv6'] = getObjectIPv6Allocations ($record['id'], TRUE);
                $record['nat4'] = getNATv4ForObject ($record['id']);
                $record['files'] = getFilesOfEntity ($record['realm'], $record['id']);
+               $record['children'] = getObjectContentsList ($record['id']);
                break;
        case 'file':
                $record['links'] = getFileLinks ($record['id']);
@@ -892,10 +893,36 @@ END;
        return $ret;
 }
 
-function getObjectPortsAndLinks ($object_id)
+function getObjectPortsAndLinks ($object_id, $include_children = FALSE)
 {
-       $ret = fetchPortList ("Port.object_id = ?", array ($object_id));
-       return sortPortList ($ret, TRUE);
+       $ret = sortPortList (fetchPortList ('Port.object_id = ?', array ($object_id)), TRUE);
+       if ($include_children)
+       {
+               // sort children by object name
+               $objects = array ();
+               if (count ($ret))
+               {
+                       $i = key ($ret);
+                       $objects[$ret[$i]['object_name']] = $ret;
+               }
+               foreach (getObjectContentsList ($object_id) as $child_id)
+               {
+                       $child_ports = fetchPortList ('Port.object_id = ?', array ($child_id));
+                       if (count ($child_ports))
+                       {
+                               $i = key ($child_ports);
+                               $objects[$child_ports[$i]['object_name']] = sortPortList ($child_ports, TRUE);
+                       }
+               }
+               ksort ($objects, SORT_NATURAL);
+
+               // flatten the sorted array
+               $ret = array ();
+               foreach ($objects as $object_name => $ports)
+                       foreach ($ports as $port)
+                               $ret[$port['id']] = $port;
+       }
+       return $ret;
 }
 
 // Fetch the object type via SQL.
@@ -1014,7 +1041,7 @@ function commitUpdateObject ($object_id, $new_name, $new_label, $new_has_problem
 // used by getEntityRelatives for sorting
 function compare_name ($a, $b)
 {
-       return strnatcmp($a['name'], $b['name']);
+       return strnatcmp ($a['name'], $b['name']);
 }
 
 // find either parents or children of a record
@@ -1743,7 +1770,7 @@ function getPortReservationComment ($port_id)
 // The fifth argument may be either explicit 'NULL' or some (already quoted by the upper layer)
 // string value. In case it is omitted, we just assign it its current value.
 // It would be nice to simplify this semantics later.
-function commitUpdatePort ($object_id, $port_id, $port_name, $port_type_id, $port_label, $port_l2address, $port_reservation_comment)
+function commitUpdatePort ($port_id, $port_name, $port_type_id, $port_label, $port_l2address, $port_reservation_comment)
 {
        $db_l2address = l2addressForDatabase ($port_l2address);
        global $dbxlink;
@@ -1752,7 +1779,7 @@ function commitUpdatePort ($object_id, $port_id, $port_name, $port_type_id, $por
                $dbxlink->exec ('LOCK TABLES Port WRITE');
        try
        {
-               if ($do_locks && alreadyUsedL2Address ($db_l2address, $object_id))
+               if ($do_locks && alreadyUsedL2Address ($db_l2address, $portinfo['object_id']))
                {
                        // FIXME: it is more correct to throw InvalidArgException here
                        // and convert it to InvalidRequestArgException at upper level,
@@ -1772,11 +1799,7 @@ function commitUpdatePort ($object_id, $port_id, $port_name, $port_type_id, $por
                                'reservation_comment' => $reservation_comment,
                                'l2address' => nullEmptyStr ($db_l2address),
                        ),
-                       array
-                       (
-                               'id' => $port_id,
-                               'object_id' => $object_id
-                       )
+                       array ('id' => $port_id)
                );
                if ($do_locks)
                        $dbxlink->exec ('UNLOCK TABLES');
@@ -2013,74 +2036,119 @@ function fetchIPv6LogEntry ($ip_bin)
        return $result->fetchAll (PDO::FETCH_ASSOC);
 }
 
-// wrapper around getObjectIPv4AllocationList and getObjectIPv6AllocationList
-function getObjectIPAllocationList ($object_id)
+function getObjectIPAllocationList ($object_id, $include_children = FALSE)
 {
        return
-               getObjectIPv4AllocationList ($object_id) +
-               getObjectIPv6AllocationList ($object_id);
+               getObjectIPvNAllocationList ($object_id, 4, $include_children) +
+               getObjectIPvNAllocationList ($object_id, 6, $include_children);
 }
 
-// Returns all IPv4 addresses allocated to object, but does not attach detailed info about address
-// Used instead of getObjectIPv4Allocations if you need perfomance but 'addrinfo' value
-function getObjectIPv4AllocationList ($object_id)
+function fetchObjectIPvNAllocationList ($object_id, $version)
 {
-       $ret = array();
+       $ret = array ();
        $result = usePreparedSelectBlade
        (
-               'SELECT name AS osif, type, ip FROM IPv4Allocation ' .
-               'WHERE object_id = ?',
+               'SELECT A.name AS osif, A.type, A.ip, A.object_id, O.name AS object_name ' . 
+               "FROM IPv{$version}Allocation A LEFT JOIN Object O ON A.object_id = O.id " .
+               'WHERE A.object_id = ?',
                array ($object_id)
        );
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               $ret[ip4_int2bin ($row['ip'])] = array ('osif' => $row['osif'], 'type' => $row['type']);
+               $ret[] = array
+                       (
+                               'object_id' => $row['object_id'],
+                               'object_name' => $row['object_name'],
+                               'osif' => $row['osif'],
+                               'type' => $row['type'],
+                               'ip' => ($version == 4) ? ip4_int2bin ($row['ip']) : $row['ip']
+                       );
+       return $ret;
+}
+
+// Returns all IPvN addresses allocated to object, but does not attach detailed info about address
+// Used instead of getObjectIPAllocations if you need perfomance but 'addrinfo' value
+function getObjectIPvNAllocationList ($object_id, $version, $include_children = FALSE)
+{
+       $allocs = fetchObjectIPvNAllocationList ($object_id, $version);
+       // sort ports by name
+       $unsorted = $sorted = array ();
+       foreach ($allocs as $alloc)
+               $unsorted[$alloc['osif']][] = $alloc;
+       foreach (sortPortList ($unsorted) as $osif => $subarray)
+               foreach ($subarray as $alloc)
+                       $sorted[] = $alloc;
+       $ret = $sorted;
+       if ($include_children)
+       {
+               // sort children by object name and port name
+               $objects = array ();
+               if (count ($ret))
+               {
+                       $i = key ($ret);
+                       $objects[$ret[$i]['object_name']] = $ret;
+               }
+               foreach (getObjectContentsList ($object_id) as $child_id)
+               {
+                       $child_allocs = fetchObjectIPvNAllocationList ($child_id, $version);
+                       if (count ($child_allocs))
+                       {
+                               $i = key ($child_allocs);
+                               $unsorted = $sorted = array ();
+                               foreach ($child_allocs as $alloc)
+                                       $unsorted[$alloc['osif']][] = $alloc;
+                               foreach (sortPortList ($unsorted) as $osif => $subarray)
+                                       foreach ($subarray as $alloc)
+                                               $sorted[] = $alloc;
+                               $objects[$child_allocs[$i]['object_name']] = $sorted;
+                       }
+               }
+               ksort ($objects, SORT_NATURAL);
+
+               // sorting is done, flatten the array
+               $ret = array ();
+               foreach ($objects as $object_name => $allocs)
+                       foreach ($allocs as $alloc)
+                               $ret[] = $alloc;
+       }
        return $ret;
 }
 
-// Returns all IPv6 addresses allocated to object, but does not attach detailed info about address
-// Used instead of getObjectIPv6Allocations if you need perfomance but 'addrinfo' value
+// FIXME: deprecated, left here only for compatibility with home-made scripts
+function getObjectIPv4AllocationList ($object_id)
+{
+       return getObjectIPvNAllocationList ($object_id, 4);
+}
+
+// FIXME: deprecated, left here only for compatibility with home-made scripts
 function getObjectIPv6AllocationList ($object_id)
 {
-       $ret = array();
-       $result = usePreparedSelectBlade
-       (
-               'SELECT name AS osif, type, ip AS ip FROM IPv6Allocation ' .
-               'WHERE object_id = ?',
-               array ($object_id)
-       );
-       while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               $ret[$row['ip']] = array ('osif' => $row['osif'], 'type' => $row['type']);
-       return $ret;
+       return getObjectIPvNAllocationList ($object_id, 6);
 }
 
 // Return all IP addresses allocated to the object sorted by allocation name.
 // Attach detailed info about address to each alocation records.
 // Index result by binary ip
-function getObjectIPAllocations ($object_id)
+function getObjectIPAllocations ($object_id, $include_children = FALSE)
 {
-       return amplifyAllocationList (getObjectIPAllocationList ($object_id));
+       return amplifyAllocationList (getObjectIPAllocationList ($object_id, $include_children));
 }
-function getObjectIPv4Allocations ($object_id)
+function getObjectIPv4Allocations ($object_id, $include_children = FALSE)
 {
-       return amplifyAllocationList (getObjectIPv4AllocationList ($object_id));
+       return amplifyAllocationList (getObjectIPvNAllocationList ($object_id, 4, $include_children));
 }
-function getObjectIPv6Allocations ($object_id)
+function getObjectIPv6Allocations ($object_id, $include_children = FALSE)
 {
-       return amplifyAllocationList (getObjectIPv6AllocationList ($object_id));
+       return amplifyAllocationList (getObjectIPvNAllocationList ($object_id, 6, $include_children));
 }
 
 function amplifyAllocationList ($alloc_list)
 {
-       $ret = array();
-       $sorted = array();
-       foreach ($alloc_list as $ip_bin => $alloc)
-               $sorted[$alloc['osif']][$ip_bin] = $alloc;
-       foreach (sortPortList ($sorted) as $osif => $subarray)
-               foreach ($subarray as $ip_bin => $alloc)
-               {
-                       $alloc['addrinfo'] = getIPAddress ($ip_bin);
-                       $ret[$ip_bin] = $alloc;
-               }
+       $ret = array ();
+       foreach ($alloc_list as $alloc)
+       {
+               $alloc['addrinfo'] = getIPAddress ($alloc['ip']);
+               $ret[] = $alloc;
+       }
        return $ret;
 }
 
@@ -3526,6 +3594,16 @@ function commitDeleteChapter ($chapter_no = 0)
        usePreparedDeleteBlade ('Chapter', array ('id' => $chapter_no, 'sticky' => 'no'));
 }
 
+function getDictionaryEntry ($dict_key)
+{
+       $result = usePreparedSelectBlade
+       (
+               'SELECT * FROM Dictionary WHERE dict_key = ?',
+               array ($dict_key)
+       );
+       return $result->fetch (PDO::FETCH_ASSOC);
+}
+
 // This is a dictionary accessor. We perform link rendering, so the user sees
 // nice <select> drop-downs.
 function readChapter ($chapter_id = 0, $style = '')
index 5030d36..6ed2a7b 100644 (file)
@@ -217,8 +217,8 @@ $dictionary = array
        142 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst Express 500-24LC'),
        143 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 3750-24TS'),
        144 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 3750-E'),
-       145 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 4503'),
-       146 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 6513'),
+       145 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 4503'),
+       146 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 6513'),
        147 => array ('chapter_id' => 12, 'dict_value' => '[[Cisco%GPASS%Catalyst 4948 | http://www.cisco.com/en/US/products/ps6026/index.html]]'),
        148 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 6509-E%L9,1H%'),
        149 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 6509-NEB-A%L9,1H%'),
@@ -442,10 +442,10 @@ $dictionary = array
        369 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 6509%L9,1H%'),
        370 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%ME 6524GS-8S'),
        371 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%ME 6524GT-8S'),
-       372 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 4503-E'),
-       373 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 4506-E'),
-       374 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 4507R-E'),
-       375 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 4510R-E'),
+       372 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 4503-E'),
+       373 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 4506-E'),
+       374 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 4507R-E'),
+       375 => array ('chapter_id' => 30, 'dict_value' => 'Cisco%GPASS%Catalyst 4510R-E'),
        376 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%Catalyst 3750-24TE-M'),
        377 => array ('chapter_id' => 12, 'dict_value' => '[[Cisco%GPASS%Catalyst 4948-10GE | http://www.cisco.com/en/US/products/ps6230/index.html]]'),
        378 => array ('chapter_id' => 12, 'dict_value' => 'Cisco%GPASS%ME 4924-10GE'),
@@ -2105,6 +2105,14 @@ $dictionary = array
        2212 => array ('chapter_id' => 12, 'dict_value' => 'Linksys%GPASS%SRW2024P'),
        2213 => array ('chapter_id' => 12, 'dict_value' => 'HP ProCurve%GPASS%2920-48G J9728A'),
 
+       // merged from modular branch
+       2500 => array ('chapter_id' => 12, 'dict_value' => 'Cisco (blade)%GPASS%WS-X6K-SUP1-2GE'),
+       2501 => array ('chapter_id' => 12, 'dict_value' => 'Cisco (blade)%GPASS%WS-X6K-SUP1A-2GE'),
+       2502 => array ('chapter_id' => 12, 'dict_value' => 'Cisco (blade)%GPASS%WS-X6K-SUP2-2GE'),
+       2503 => array ('chapter_id' => 12, 'dict_value' => 'Cisco (blade)%GPASS%WS-X4515'),
+       2504 => array ('chapter_id' => 12, 'dict_value' => 'Cisco (blade)%GPASS%WS-X4548-GB-RJ45'),
+       2505 => array ('chapter_id' => 17, 'dict_value' => 'Cisco (blade)%GPASS%NPE-G2'),
+
 # Any new "default" dictionary records must go above this line (i.e., with
 # dict_key code less, than 50000). This is necessary to keep AUTO_INCREMENT
 # and dictionary updates working properly.
index 2db0a4b..fe0f22b 100644 (file)
@@ -993,10 +993,10 @@ function findAllEndpoints ($object_id, $fallback = '')
        foreach (getAttrValues ($object_id) as $record)
                if ($record['id'] == 3 && strlen ($record['value'])) // FQDN
                        return array ($record['value']);
-       $regular = array();
-       foreach (getObjectIPv4AllocationList ($object_id) as $ip_bin => $alloc)
+       $regular = array ();
+       foreach (getObjectIPAllocationList ($object_id) as $alloc)
                if ($alloc['type'] == 'regular')
-                       $regular[] = ip4_format ($ip_bin);
+                       $regular[] = ip_format ($alloc['ip']);
        // FIXME: add IPv6 allocations to this list
        if (!count ($regular) && strlen ($fallback))
                return array ($fallback);
@@ -4549,6 +4549,14 @@ function formatLinkedPort ($port_info, $a_class = '')
        );
 }
 
+// return a comparison function to be used for sorting 
+// TODO: refactor to make compatible with older PHP versions
+function buildNatCmpFunction ($key) {
+       return function ($a, $b) use ($key) {
+               return strnatcmp($a[$key], $b[$key]);
+       };
+}
+
 function compareDecomposedPortNames ($porta, $portb)
 {
        $ret = 0;
index 5dc63ba..2a12374 100644 (file)
@@ -1343,40 +1343,12 @@ function renderRackProblems ($rack_id)
        renderGridForm ($rack_id, 'applyRackProblemMask', 'Rack problems', 'Mark unusable atoms', 'F', 'U');
 }
 
-function renderObjectPortRow ($port, $is_highlighted)
-{
-       // highlight port name with yellow if it's name is not canonical
-       $canon_pn = shortenPortName ($port['name'], $port['object_id']);
-       $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
-
-       echo '<tr';
-       if ($is_highlighted)
-               echo ' class=highlight';
-       $a_class = isEthernetPort ($port) ? 'port-menu' : '';
-       echo "><td class='tdleft $name_class' NOWRAP><a name='port-${port['id']}' class='interactive-portname nolink $a_class'>${port['name']}</a></td>";
-       echo "<td class=tdleft>${port['label']}</td>";
-       echo "<td class=tdleft>" . formatPortIIFOIF ($port) . "</td><td class=tdleft><tt>${port['l2address']}</tt></td>";
-       if ($port['remote_object_id'])
-       {
-               echo "<td class=tdleft>" .
-                       formatPortLink ($port['remote_object_id'], $port['remote_object_name'], $port['remote_id'], NULL) .
-                       "</td>";
-               echo "<td class=tdleft>" . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') . "</td>";
-               $editable = permitted ('object', 'ports', 'editPort')
-                       ? 'editable'
-                       : '';
-               echo "<td class=tdleft><span class='rsvtext $editable id-${port['id']} op-upd-reservation-cable'>${port['cableid']}</span></td>";
-       }
-       else
-               echo implode ('', formatPortReservation ($port)) . '<td></td>';
-       echo "</tr>";
-}
-
 function renderObject ($object_id)
 {
        global $nextorder, $virtual_obj_types;
        $info = spotEntity ('object', $object_id);
        amplifyCell ($info);
+       $has_children = (count ($info['children'])) ? TRUE : FALSE;
        // Main layout starts.
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        echo "<tr><td colspan=2 align=center><h1>${info['dname']}</h1></td></tr>\n";
@@ -1384,7 +1356,7 @@ function renderObject ($object_id)
        echo "<tr><td class=pcleft>";
 
        // display summary portlet
-       $summary  = array();
+       $summary = array();
        if (strlen ($info['name']))
                $summary['Common name'] = $info['name'];
        elseif (considerConfiguredConstraint ($info, 'NAMEWARN_LISTSRC'))
@@ -1408,9 +1380,9 @@ function renderObject ($object_id)
                        $fmt_parents[] =  "<a href='".makeHref(array('page'=>$parent['page'], $parent['id_name'] => $parent['entity_id']))."'>${parent['name']}</a>";
                $summary[count($parents) > 1 ? 'Containers' : 'Container'] = implode ('<br>', $fmt_parents);
        }
-       $children = getEntityRelatives ('children', 'object', $object_id);
-       if (count ($children))
+       if ($has_children)
        {
+               $children = getEntityRelatives ('children', 'object', $object_id);
                $fmt_children = array();
                foreach ($children as $child)
                        $fmt_children[] = "<a href='".makeHref(array('page'=>$child['page'], $child['id_name']=>$child['entity_id']))."'>${child['name']}</a>";
@@ -1438,7 +1410,7 @@ function renderObject ($object_id)
                        )
                )."&"
        ));
-       renderEntitySummary ($info, 'summary', $summary);
+       renderEntitySummary ($info, 'Summary', $summary);
 
        if (strlen ($info['comment']))
        {
@@ -1450,7 +1422,7 @@ function renderObject ($object_id)
        $logrecords = getLogRecordsForObject ($_REQUEST['object_id']);
        if (count ($logrecords))
        {
-               startPortlet ('log records');
+               startPortlet ('Log records');
                echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='100%'>";
                $order = 'odd';
                foreach ($logrecords as $row)
@@ -1470,7 +1442,7 @@ function renderObject ($object_id)
 
        if (count ($info['ports']))
        {
-               startPortlet ('ports and links');
+               startPortlet ('Ports and links');
                $hl_port_id = 0;
                if (isset ($_REQUEST['hl_port_id']))
                {
@@ -1479,15 +1451,59 @@ function renderObject ($object_id)
                        addAutoScrollScript ("port-$hl_port_id");
                }
                echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
-               echo '<tr><th class=tdleft>Local name</th><th class=tdleft>Visible label</th>';
+               echo '<tr>';
+               if ($has_children)
+                       echo '<th class=tdleft>Object</th>';
+               echo '<th class=tdleft>Local name</th><th class=tdleft>Visible label</th>';
                echo '<th class=tdleft>Interface</th><th class=tdleft>L2 address</th>';
                echo '<th class=tdcenter colspan=2>Remote object and port</th>';
                echo '<th class=tdleft>Cable ID</th></tr>';
+               // note how many ports each object has
+               if ($has_children)
+               {
+                       $port_count = array ();
+                       foreach ($info['ports'] as $port)
+                               $port_count[$port['object_id']] = isset ($port_count[$port['object_id']]) ? $port_count[$port['object_id']] + 1 : 1;
+                       $last_object_id = 0;
+               }
                foreach ($info['ports'] as $port)
-                       callHook ('renderObjectPortRow', $port, ($hl_port_id == $port['id']));
+               {
+                       // highlight port name with yellow if it's name is not canonical
+                       $canon_pn = shortenPortName ($port['name'], $port['object_id']);
+                       $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
+
+                       $tr_class = ($hl_port_id == $port['id']) ? 'class=highlight' : '';
+                       echo "<tr $tr_class valign=top>";
+                       if ($has_children and $last_object_id != $port['object_id'])
+                       {
+                               $rowspan = $port_count[$port['object_id']] > 1 ? 'rowspan="' . $port_count[$port['object_id']] . '"' : '';
+                               $object_name = ($port['object_id'] == $object_id) ? $port['object_name'] : formatPortLink ($port['object_id'], $port['object_name'], NULL, NULL);
+                               echo "<td class=tdleft $rowspan>$object_name</td>";
+                               $last_object_id = $port['object_id'];
+                       }
+                       $a_class = isEthernetPort ($port) ? 'port-menu' : '';
+                       echo "<td class='tdleft $name_class' NOWRAP><a name='port-${port['id']}' class='interactive-portname nolink $a_class'>${port['name']}</a></td>";
+                       echo "<td class=tdleft>${port['label']}</td>";
+                       echo "<td class=tdleft>" . formatPortIIFOIF ($port) . "</td><td class=tdleft><tt>${port['l2address']}</tt></td>";
+                       if ($port['remote_object_id'])
+                       {
+                               echo "<td class=tdleft>" .
+                                       formatPortLink ($port['remote_object_id'], $port['remote_object_name'], $port['remote_id'], NULL) .
+                                       "</td>";
+                               echo "<td class=tdleft>" . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') . "</td>";
+                               $editable = permitted ('object', 'ports', 'editPort')
+                                       ? 'editable'
+                                       : '';
+                               echo "<td class=tdleft><span class='rsvtext $editable id-${port['id']} op-upd-reservation-cable'>${port['cableid']}</span></td>";
+                       }
+                       else
+                               echo implode ('', formatPortReservation ($port)) . '<td></td>';
+                       echo "</tr>";
+               }
+
                if (permitted (NULL, 'ports', 'set_reserve_comment'))
                        addJS ('js/inplace-edit.js');
-               echo "</table><br>";
+               echo '</table><br>';
                finishPortlet();
        }
 
@@ -1495,42 +1511,60 @@ function renderObject ($object_id)
        {
                startPortlet ('IP addresses');
                echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                       echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>network</th><th>routed by</th><th>peers</th></tr>\n";
-               else
-                       echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>peers</th></tr>\n";
+               $object_header = $has_children ? '<th>Object</th>' : '';
+               $extended_header = getConfigVar ('EXT_IPV4_VIEW') == 'yes' ? '<th>network</th><th>routed by</th>' : '';
+               echo "<tr class=tdleft>${object_header}<th>OS interface</th><th>IP address</th>${extended_header}<th>peers</th></tr>\n";
 
-               // group IP allocations by interface name instead of address family
-               $allocs_by_iface = array();
+               // group IP allocations by object and interface name instead of address family
+               $allocs_by_object = array();
                foreach (array ('ipv4', 'ipv6') as $ip_v)
-                       foreach ($info[$ip_v] as $ip_bin => $alloc)
-                               $allocs_by_iface[$alloc['osif']][$ip_bin] = $alloc;
-
-               // sort allocs array by portnames
-               foreach (sortPortList ($allocs_by_iface) as $iface_name => $alloclist)
-               {
-                       $is_first_row = TRUE;
-                       foreach ($alloclist as $alloc)
+                       foreach ($info[$ip_v] as $alloc)
                        {
-                               $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
-                               echo "<tr class='${rendered_alloc['tr_class']}' valign=top>";
+                               $allocs_by_object[$alloc['object_id']]['object_name'] = $alloc['object_name'];
+                               $allocs_by_object[$alloc['object_id']]['interfaces'][$alloc['osif']][$alloc['ip']] = $alloc;
+                       }
+               uasort ($allocs_by_object, buildNatCmpFunction ('object_name'));
+               foreach ($allocs_by_object as $alloc_object_id => $allocs_by_iface)
+               {
+                       $is_first_object_row = TRUE;
+                       $num_allocs = 0;
+                       foreach ($allocs_by_iface['interfaces'] as $iface_name => $alloclist)
+                               $num_allocs += count ($alloclist);
 
-                               // display iface name, same values are grouped into single cell
-                               if ($is_first_row)
-                               {
-                                       $rowspan = count ($alloclist) > 1 ? 'rowspan="' . count ($alloclist) . '"' : '';
-                                       echo "<td class=tdleft $rowspan>" . $iface_name . $rendered_alloc['td_name_suffix'] . "</td>";
-                                       $is_first_row = FALSE;
-                               }
-                               echo $rendered_alloc['td_ip'];
-                               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+                       // display each interface (already sorted by amplifyAllocationList)
+                       foreach ($allocs_by_iface['interfaces'] as $iface_name => $alloclist)
+                       {
+                               $is_first_iface_row = TRUE;
+                               foreach ($alloclist as $alloc)
                                {
-                                       echo $rendered_alloc['td_network'];
-                                       echo $rendered_alloc['td_routed_by'];
-                               }
-                               echo $rendered_alloc['td_peers'];
+                                       $rendered_alloc = callHook ('getRenderedAlloc', $alloc['object_id'], $alloc);
+                                       echo "<tr class='${rendered_alloc['tr_class']}' valign=top>";
 
-                               echo "</tr>\n";
+                                       // display iface name, same values are grouped into single cell
+                                       if ($has_children and $is_first_object_row)
+                                       {
+                                               $object_rowspan = $num_allocs > 1 ? 'rowspan="' . $num_allocs . '"' : '';
+                                               $object_name = ($alloc['object_id'] == $object_id) ? $alloc['object_name'] : formatPortLink ($alloc['object_id'], $alloc['object_name'], NULL, NULL);
+                                               echo "<td class=tdleft $object_rowspan>$object_name</td>";
+                                               $is_first_object_row = FALSE;
+                                       }
+
+                                       // display iface name, same values are grouped into single cell
+                                       if ($is_first_iface_row)
+                                       {
+                                               $iface_rowspan = count ($alloclist) > 1 ? 'rowspan="' . count ($alloclist) . '"' : '';
+                                               echo "<td class=tdleft $iface_rowspan>" . $iface_name . $rendered_alloc['td_name_suffix'] . '</td>';
+                                               $is_first_iface_row = FALSE;
+                                       }
+                                       echo $rendered_alloc['td_ip'];
+                                       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+                                       {
+                                               echo $rendered_alloc['td_network'];
+                                               echo $rendered_alloc['td_routed_by'];
+                                       }
+                                       echo $rendered_alloc['td_peers'];
+                                       echo "</tr>\n";
+                               }
                        }
                }
                echo "</table><br>\n";
@@ -1600,7 +1634,7 @@ function renderObject ($object_id)
        if (!in_array($info['objtype_id'], $virtual_obj_types))
        {
                // rackspace portlet
-               startPortlet ('rackspace allocation');
+               startPortlet ('Rackspace allocation');
                foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
                        renderRack ($rack_id, $object_id);
                echo '<br>';
@@ -1644,48 +1678,55 @@ function renderRackMultiSelect ($sname, $racks, $selected)
 function renderPortsForObject ($object_id)
 {
        $prefs = getPortListPrefs();
-       function printNewItemTR ($prefs)
+       function printNewItemTR ($prefs, $has_children)
        {
                printOpFormIntro ('addPort');
-               echo "<tr><td>";
+               echo '<tr><td>';
                printImageHREF ('add', 'add a port', TRUE);
-               echo "</td><td class='tdleft'><input type=text size=8 name=port_name tabindex=100></td>\n";
-               echo "<td><input type=text name=port_label tabindex=101></td><td>";
+               echo '</td>';
+               if ($has_children)
+                       echo '<td>&nbsp</td>';
+               echo "<td class='tdleft'><input type=text size=8 name=port_name tabindex=100></td>\n";
+               echo '<td><input type=text name=port_label tabindex=101></td><td>';
                printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id', 'tabindex' => 102), $prefs['selected']);
                echo "<td><input type=text name=port_l2address tabindex=103 size=18 maxlength=24></td>\n";
-               echo "<td colspan=4>&nbsp;</td><td>";
+               echo '<td colspan=4>&nbsp;</td><td>';
                printImageHREF ('add', 'add a port', TRUE, 104);
-               echo "</td></tr></form>";
+               echo '</td></tr></form>';
        }
        if (getConfigVar('ENABLE_MULTIPORT_FORM') == 'yes' || getConfigVar('ENABLE_BULKPORT_FORM') == 'yes' )
-               startPortlet ('Ports and interfaces');
+               startPortlet ('Ports and links');
        else
                echo '<br>';
        $object = spotEntity ('object', $object_id);
        amplifyCell ($object);
+       $has_children = (count ($object['children'])) ? TRUE : FALSE;
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes' && getConfigVar('ENABLE_BULKPORT_FORM') == 'yes'){
                echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-               echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>Start Number</th>";
+               echo '<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>Start Number</th>';
                echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
                printOpFormIntro ('addBulkPorts');
-               echo "<tr><td>";
+               echo '<tr><td>';
                printImageHREF ('add', 'add ports', TRUE);
                echo "</td><td><input type=text size=8 name=port_name tabindex=105></td>\n";
-               echo "<td><input type=text name=port_label tabindex=106></td><td>";
+               echo '<td><input type=text name=port_label tabindex=106></td><td>';
                printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id', 'tabindex' => 107), $prefs['selected']);
                echo "<td><input type=text name=port_numbering_start tabindex=108 size=3 maxlength=3></td>\n";
                echo "<td><input type=text name=port_numbering_count tabindex=109 size=3 maxlength=3></td>\n";
-               echo "<td>&nbsp;</td><td>";
+               echo '<td>&nbsp;</td><td>';
                printImageHREF ('add', 'add ports', TRUE, 110);
-               echo "</td></tr></form>";
+               echo '</td></tr></form>';
                echo "</table><br>\n";
        }
 
        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-       echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>L2 address</th>";
+       echo '<tr><th>&nbsp;</th>';
+       if ($has_children)
+               echo '<th class=tdleft>Object</th>';
+       echo '<th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>L2 address</th>';
        echo "<th class=tdcenter colspan=2>Remote object and port</th><th>Cable ID</th><th class=tdcenter>(Un)link or (un)reserve</th><th>&nbsp;</th></tr>\n";
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
-               printNewItemTR ($prefs);
+               printNewItemTR ($prefs, $has_children);
 
        // clear ports link
        echo getOpLink (array ('op'=>'deleteAll'), 'Clear port list', 'clear', '', 'need-confirmation');
@@ -1705,6 +1746,14 @@ function renderPortsForObject ($object_id)
                addAutoScrollScript ("port-$hl_port_id");
        }
        switchportInfoJS ($object_id); // load JS code to make portnames interactive
+       // note how many ports each object has
+       if ($has_children)
+       {
+               $port_count = array ();
+               foreach ($object['ports'] as $port)
+                       $port_count[$port['object_id']] = isset ($port_count[$port['object_id']]) ? $port_count[$port['object_id']] + 1 : 1;
+               $last_object_id = 0;
+       }
        foreach ($object['ports'] as $port)
        {
                // highlight port name with yellow if it's name is not canonical
@@ -1713,9 +1762,16 @@ function renderPortsForObject ($object_id)
 
                $tr_class = isset ($hl_port_id) && $hl_port_id == $port['id'] ? 'class="highlight"' : '';
                printOpFormIntro ('editPort', array ('port_id' => $port['id']));
-               echo "<tr $tr_class><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
+               echo "<tr $tr_class valign=top><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
                printImageHREF ('delete', 'Unlink and Delete this port');
                echo "</a></td>\n";
+               if ($has_children and $last_object_id != $port['object_id'])
+               {
+                       $rowspan = $port_count[$port['object_id']] > 1 ? 'rowspan="' . $port_count[$port['object_id']] . '"' : '';
+                       $object_name = ($port['object_id'] == $object_id) ? $port['object_name'] : formatPortLink ($port['object_id'], $port['object_name'], NULL, NULL);
+                       echo "<td class=tdleft $rowspan>$object_name</td>";
+                       $last_object_id = $port['object_id'];
+               }
                $a_class = isEthernetPort ($port) ? 'port-menu' : '';
                echo "<td class='tdleft $name_class' NOWRAP><input type=text name=name class='interactive-portname $a_class' value='${port['name']}' size=8></td>";
                echo "<td><input type=text name=label value='${port['label']}'></td>";
@@ -1821,27 +1877,33 @@ function renderPortsForObject ($object_id)
 
 function renderIPForObject ($object_id)
 {
-       function printNewItemTR ($default_type)
+       function printNewItemTR ($default_type, $has_children)
        {
                global $aat;
                printOpFormIntro ('add');
-               echo "<tr><td>"; // left btn
+               echo '<tr><td>';
                printImageHREF ('add', 'allocate', TRUE);
-               echo "</td>";
-               echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n"; // if-name
-               echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n"; // IP
+               echo '</td>';
+               if ($has_children)
+                       echo '<td>&nbsp</td>';
+               echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n";
+               echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n";
                if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                       echo "<td colspan=2>&nbsp;</td>"; // network, routed by
+                       echo '<td colspan=2>&nbsp;</td>';
                echo '<td>';
-               printSelect ($aat, array ('name' => 'bond_type', 'tabindex' => 102), $default_type); // type
-               echo "</td><td>&nbsp;</td><td>"; // misc
-               printImageHREF ('add', 'allocate', TRUE, 103); // right btn
-               echo "</td></tr></form>";
+               printSelect ($aat, array ('name' => 'bond_type', 'tabindex' => 102), $default_type);
+               echo '</td><td>&nbsp;</td><td>';
+               printImageHREF ('add', 'allocate', TRUE, 103);
+               echo '</td></tr></form>';
        }
        global $aat;
+       $children = getObjectContentsList ($object_id);
+       $has_children = (count ($children)) ? TRUE : FALSE;
        startPortlet ('Allocations');
        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
        echo '<th>&nbsp;</th>';
+       if ($has_children)
+               echo '<th>Object</th>';
        echo '<th>OS interface</th>';
        echo '<th>IP address</th>';
        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
@@ -1855,28 +1917,44 @@ function renderIPForObject ($object_id)
        echo '</tr>';
 
        $alloc_list = ''; // most of the output is stored here
-       $used_alloc_types = array();
-       foreach (getObjectIPAllocations ($object_id) as $alloc)
+       $used_alloc_types = array ();
+       $allocs = getObjectIPAllocations ($object_id, TRUE);
+       // note how many allocs each object has
+       if ($has_children)
+       {
+               $alloc_count = array ();
+               foreach ($allocs as $alloc)
+                       $alloc_count[$alloc['object_id']] = isset ($alloc_count[$alloc['object_id']]) ? $alloc_count[$alloc['object_id']] + 1 : 1;
+               $last_object_id = 0;
+       }
+       foreach ($allocs as $alloc)
        {
                if (! isset ($used_alloc_types[$alloc['type']]))
                        $used_alloc_types[$alloc['type']] = 0;
                $used_alloc_types[$alloc['type']]++;
 
-               $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
+               $rendered_alloc = callHook ('getRenderedAlloc', $alloc['object_id'], $alloc);
                $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
                $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
-
-               $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
-               $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
+               $alloc_list .= '<td>' . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . '</td>';
+               if ($has_children and $last_object_id != $alloc['object_id'])
+               {
+                       $rowspan = 1;
+                       $rowspan = $alloc_count[$alloc['object_id']] > 1 ? 'rowspan="' . $alloc_count[$alloc['object_id']] . '"' : '';
+                       $object_name = ($alloc['object_id'] == $object_id) ? $alloc['object_name'] : formatPortLink ($alloc['object_id'], $alloc['object_name'], NULL, NULL);
+                       $alloc_list .= "<td class=tdleft $rowspan>$object_name</td>";
+                       $last_object_id = $alloc['object_id'];
+               }
+               $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . '</td>';
                $alloc_list .= $rendered_alloc['td_ip'];
                if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                {
                        $alloc_list .= $rendered_alloc['td_network'];
                        $alloc_list .= $rendered_alloc['td_routed_by'];
                }
-               $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
+               $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . '</td>';
                $alloc_list .= $rendered_alloc['td_peers'];
-               $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
+               $alloc_list .= '<td>' .getImageHREF ('save', 'Save changes', TRUE) . '</td>';
 
                $alloc_list .= "</form></tr>\n";
        }
@@ -1885,7 +1963,7 @@ function renderIPForObject ($object_id)
 
        if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
                echo $alloc_list;
-       printNewItemTR ($most_popular_type);
+       printNewItemTR ($most_popular_type, $has_children);
        if (! $list_on_top)
                echo $alloc_list;
 
index 85574b7..8b28850 100644 (file)
@@ -83,7 +83,6 @@ $opspec_list['object-ports-delPort'] = array
        'arglist' => array
        (
                array ('url_argname' => 'port_id', 'table_colname' => 'id', 'assertion' => 'uint'),
-               array ('url_argname' => 'object_id', 'assertion' => 'uint'),
        ),
 );
 $opspec_list['object-ports-deleteAll'] = array
@@ -853,7 +852,7 @@ function editPortForObject ()
        assertStringArg ('reservation_comment', TRUE);
        genericAssertion ('l2address', 'l2address0');
        genericAssertion ('name', 'string');
-       commitUpdatePort ($sic['object_id'], $sic['port_id'], $sic['name'], $sic['port_type_id'], $sic['label'], $sic['l2address'], $sic['reservation_comment']);
+       commitUpdatePort ($sic['port_id'], $sic['name'], $sic['port_type_id'], $sic['label'], $sic['l2address'], $sic['reservation_comment']);
        if (array_key_exists ('cable', $_REQUEST))
                commitUpdatePortLink ($sic['port_id'], $sic['cable']);
        showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
@@ -957,7 +956,7 @@ http://www.cisco.com/en/US/products/hw/routers/ps274/products_tech_note09186a008
                }
                elseif (count ($port_ids) == 1) // update only single-socket ports
                {
-                       commitUpdatePort ($object_id, $port_ids[0], $port['name'], $port_type, $port['label'], $port['l2address']);
+                       commitUpdatePort ($port_ids[0], $port['name'], $port_type, $port['label'], $port['l2address']);
                        $updated_count++;
                }
        }
@@ -3725,7 +3724,7 @@ function renameObjectPorts()
                $canon_pn = shortenPortName ($port['name'], $port['object_id']);
                if ($canon_pn != $port['name'])
                {
-                       commitUpdatePort ($object_id, $port['id'], $canon_pn, $port['oif_id'], $port['label'], $port['l2address'], $port['reservation_comment']);
+                       commitUpdatePort ($port['id'], $canon_pn, $port['oif_id'], $port['label'], $port['l2address'], $port['reservation_comment']);
                        $n++;
                }
        }
index 97c36c0..807c1b1 100644 (file)
@@ -255,7 +255,7 @@ $iftable_processors['catalyst-chassis-25-to-26-1000SFP'] = array
        'pattern' => '@^GigabitEthernet([[:digit:]]+/)?(25|26)$@',
        'replacement' => 'gi\\1\\2',
        'dict_key' => '4-1077',
-       'label' => '\\2X',
+       'label' => '\\2',
        'try_next_proc' => FALSE,
 );
 
@@ -264,7 +264,7 @@ $iftable_processors['catalyst-chassis-any-100TX'] = array
        'pattern' => '@^FastEthernet([[:digit:]]+/)?([[:digit:]]+)$@',
        'replacement' => 'fa\\1\\2',
        'dict_key' => 19,
-       'label' => '\\2X',
+       'label' => '\\2',
        'try_next_proc' => FALSE,
 );
 
@@ -282,7 +282,7 @@ $iftable_processors['catalyst-chassis-any-1000T'] = array
        'pattern' => '@^GigabitEthernet([[:digit:]]+/)?([[:digit:]]+)$@',
        'replacement' => 'gi\\1\\2',
        'dict_key' => 24,
-       'label' => '\\2X',
+       'label' => '\\2',
        'try_next_proc' => FALSE,
 );
 
@@ -348,6 +348,15 @@ $iftable_processors['catalyst-chassis-1-to-2-combo-1000T'] = array (
        'try_next_proc' => TRUE,
 );
 
+$iftable_processors['catalyst-chassis-1-to-3-combo-1000SFP'] = array
+(
+       'pattern' => '@^GigabitEthernet([[:digit:]]+/)?(1|2|3)$@',
+       'replacement' => 'gi\\1\\2',
+       'dict_key' => '4-1077',
+       'label' => '\\2',
+       'try_next_proc' => TRUE,
+);
+
 $iftable_processors['catalyst-chassis-8-combo-1000SFP'] = array
 (
        'pattern' => '@^GigabitEthernet([[:digit:]]+/)?(8)$@',
@@ -555,6 +564,24 @@ $iftable_processors['nexus-any-10000SFP+'] = array
        'try_next_proc' => FALSE,
 );
 
+$iftable_processors['cisco-router-chassis-any-100TX'] = array
+(
+       'pattern' => '@^FastEthernet([[:digit:]]+/)?([[:digit:]]+)$@',
+       'replacement' => 'fe\\1\\2',
+       'dict_key' => 19,
+       'label' => 'FE \\1\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['cisco-router-chassis-any-1000T'] = array
+(
+       'pattern' => '@^GigabitEthernet([[:digit:]]+/)?([[:digit:]]+)$@',
+       'replacement' => 'ge\\1\\2',
+       'dict_key' => 24,
+       'label' => 'GE \\1\\2',
+       'try_next_proc' => FALSE,
+);
+
 $iftable_processors['ftos-any-1000T'] = array
 (
        'pattern' => '@^GigabitEthernet 0/(\d+)$@',
@@ -1817,8 +1844,143 @@ $iftable_processors['ibm-any-SFP+'] = array
        'try_next_proc' => FALSE,
 );
 
-global $known_switches;
-$known_switches = array // key is system OID w/o "enterprises" prefix
+$iftable_processors['catalyst-module-any-1000T'] = array
+(
+       'pattern' => '@^Gi(\d+)/(\d+)$@',
+       'replacement' => 'gi\\1/\\2',
+       'dict_key' => 24,
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['catalyst-module-any-1000SFP'] = array
+(
+       'pattern' => '@^Gi(\d+)/(\d+)$@',
+       'replacement' => 'gi\\1/\\2',
+       'dict_key' => '4-1077',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['catalyst-module-any-1000GBIC'] = array
+(
+       'pattern' => '@^Gi(\d+)/(\d+)$@',
+       'replacement' => 'gi\\1/\\2',
+       'dict_key' => '3-1078',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['catalyst-module-any-10000X2'] = array
+(
+       'pattern' => '@^TenGigabitEthernet(\d+)/(\d+)$@',
+       'replacement' => 'te\\1/\\2',
+       'dict_key' => '6-1080',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['catalyst-module-any-10000XENPAK'] = array
+(
+       'pattern' => '@^TenGigabitEthernet(\d+)/(\d+)$@',
+       'replacement' => 'te\\1/\\2',
+       'dict_key' => '5-1079',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['catalyst-module-sup-g1-1000SFP'] = array
+(
+       'pattern' => '@^Gi(\d+)/1$@',
+       'replacement' => 'gi\\1/1',
+       'dict_key' => '4-1077',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['catalyst-module-sup-g2-1000SFP'] = array
+(
+       'pattern' => '@^Gi(\d+)/2$@',
+       'replacement' => 'gi\\1/2',
+       'dict_key' => '4-1077',
+       'label' => '\\2',
+       'try_next_proc' => TRUE,
+);
+
+$iftable_processors['catalyst-module-sup-g2-1000T'] = array
+(
+       'pattern' => '@^Gi(\d+)/2 10/100/1000BaseT$@',
+       'replacement' => 'gi\\1/2',
+       'dict_key' => '1-24',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-100TX'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(\d+)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => 19,
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-1000T'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(\d+)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => 24,
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-CX4'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(\d+)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => '1-40',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-X2'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(\d+)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => '6-1080',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-1000SFP'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(\d+)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => '4-1077',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-10000SFP+'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(\d+)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => '9-1084',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['procurve-module-21-to-24-1000SFP'] = array
+(
+       'pattern' => '@^Port ([A-Z])?(21|22|23|24)$@',
+       'replacement' => '\\1\\2',
+       'dict_key' => '4-1077',
+       'label' => '\\2',
+       'try_next_proc' => FALSE,
+);
+
+global $known_devices;
+$known_devices = array // key is system OID w/o "enterprises" prefix
 (
        '9.1.217' => array
        (
@@ -1832,6 +1994,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'WS-C2924M-XL: 24 RJ-45/10-100TX',
                'processors' => array ('catalyst-chassis-any-100TX'),
        ),
+       '9.1.222' => array
+       (
+               'dict_key' => 269,
+               'text' => '7206VXR: 6 module bays',
+               'modular' => TRUE,
+       ),
        '9.1.246' => array
        (
                'dict_key' => 391,
@@ -1856,6 +2024,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'WS-C6506: modular device (INCOMPLETE!)',
                'processors' => array ('catalyst-chassis-any-1000T'),
        ),
+       '9.1.283' => array
+       (
+               'dict_key' => 148,
+               'text' => 'WS-C6509-E: 9 module bays',
+               'modular' => TRUE,
+       ),
        '9.1.323' => array
        (
                'dict_key' => 381,
@@ -1934,6 +2108,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'WS-C2950SX-24 24 RJ-45/10-100TX + 2 1000Base-SX',
                'processors' => array ('catalyst-chassis-uplinks-1000SX','catalyst-chassis-any-100TX'),
        ),
+       '9.1.503' => array
+       (
+               'dict_key' => 145,
+               'text' => 'WS-C4503: 3 module bays',
+               'modular' => TRUE,
+       ),
        '9.1.527' => array
        (
                'dict_key' => 210,
@@ -1982,6 +2162,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'Cisco 878 ISR: 4 RJ-45/10-100TX',
                'processors' => array ('catalyst-chassis-any-100TX'),
        ),
+       '9.1.577' => array
+       (
+               'dict_key' => 281,
+               'text' => 'Cisco 2821 ISR: 2 RJ-45/10-100-1000T(X) + 6 module bays',
+               'modular' => TRUE,
+       ),
        '9.1.614' => array
        (
                'dict_key' => 175,
@@ -2006,6 +2192,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'WS-C3560G-48TS: 48 RJ-45/10-100-1000T(X) + 4 SFP/1000',
                'processors' => array ('catalyst-chassis-49-to-52-1000SFP', 'catalyst-chassis-any-1000T'),
        ),
+       '9.1.619' => array
+       (
+               'dict_key' => 279,
+               'text' => 'Cisco 2801 ISR: 2 RJ-45/10-100TX + 4 module bays',
+               'modular' => TRUE,
+       ),
        '9.1.626' => array
        (
                'dict_key' => 147,
@@ -2515,6 +2707,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'J4900B: 24 RJ-45/10-100TX + 2 combo-gig',
                'processors' => array ('procurve-25-to-26-combo-1000SFP', 'procurve-25-to-26-1000T', 'procurve-chassis-100TX'),
        ),
+       '11.2.3.7.11.51' => array
+       (
+               'dict_key' => 890,
+               'text' => 'J8698A: 12 module bays',
+               'modular' => TRUE,
+       ),
        '11.2.3.7.11.53' => array
        (
                'dict_key' => 881,
@@ -3165,6 +3363,246 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
        ),
 );
 
+global $known_modules;
+$known_modules = array
+(
+       '2801_ISR_motherboard' => array
+       (
+               'pattern' => '@^C2801 Motherboard@',
+               'processors' => array ('cisco-router-chassis-any-100TX'),
+               'assign_to_origin' => TRUE,
+       ),
+       '2821_ISR_motherboard' => array
+       (
+               'pattern' => '@^c2821 Motherboard@',
+               'processors' => array ('cisco-router-chassis-any-1000T'),
+               'assign_to_origin' => TRUE,
+       ),
+       'J8702A' => array
+       (
+               'pattern' => '@^ProCurve J8702A .+ Module$@',
+               'dict_key' => 1628,
+               'text' => 'J8702A: 24 RJ-45/10-100-1000T(X) PoE',
+               'processors' => array ('procurve-module-1000T'),
+       ),
+       'J8705A' => array
+       (
+               'pattern' => '@^ProCurve J8705A .+ Module$@',
+               'dict_key' => 1629,
+               'text' => 'J8705A: 20 RJ-45/10-100-1000T(X) PoE + 4 SFP',
+               'processors' => array ('procurve-module-21-to-24-1000SFP', 'procurve-module-1000T'),
+       ),
+       'J8706A' => array
+       (
+               'pattern' => '@^ProCurve J8706A .+ Module$@',
+               'dict_key' => 1630,
+               'text' => 'J8706A: 24 SFP',
+               'processors' => array ('procurve-module-1000SFP'),
+       ),
+       'J8707A' => array
+       (
+               'pattern' => '@^ProCurve J8707A .+ Module$@',
+               'dict_key' => 1631,
+               'text' => 'J8707A: 4 X2/10000',
+               'processors' => array ('procurve-module-CX4'),
+       ),
+       'J8708A' => array
+       (
+               'pattern' => '@^ProCurve J8708A .+ Module$@',
+               'dict_key' => 1632,
+               'text' => 'J8708A: 4 CX4/10000',
+               'processors' => array ('procurve-module-X2'),
+       ),
+       'J8726A' => array
+       (
+               'pattern' => '@^ProCurve J8726A Management Module 5400zl$@',
+               'dict_key' => 1627,
+               'text' => 'J8726A: 1 console',
+       ),
+       'J9307A' => array
+       (
+               'pattern' => '@^ProCurve J9307A .+ Module$@',
+               'dict_key' => 1633,
+               'text' => 'J9307A: 24 RJ-45/10-100-1000T(X) PoE+',
+               'processors' => array ('procurve-module-1000T'),
+       ),
+       'J9308A' => array
+       (
+               'pattern' => '@^ProCurve J9308A .+ Module$@',
+               'dict_key' => 1634,
+               'text' => 'J9308A: 20 RJ-45/10-100-1000T(X) PoE+ + 4 SFP',
+               'processors' => array ('procurve-module-21-to-24-1000SFP', 'procurve-module-1000T'),
+       ),
+       'J8709A' => array
+       (
+               'pattern' => '@^ProCurve J8709A .+ Module$@',
+               'dict_key' => 1635,
+               'text' => 'J8709A: 4 SFP+',
+               'processors' => array ('procurve-module-10000SFP+'),
+       ),
+       'J9478A' => array
+       (
+               'pattern' => '@^ProCurve J9478A .+ Module$@',
+               'dict_key' => 1636,
+               'text' => 'J9478A: 24 RJ-45/10-100 PoE+',
+               'processors' => array ('procurve-module-100TX'),
+       ),
+       'NPE-G2' => array
+       (
+               'pattern' => '@^NPE-G2@',
+               'dict_key' => 2505,
+               'text' => 'NPE-G2: processing engine',
+               'processors' => array ('catalyst-chassis-1-to-3-combo-1000SFP', 'catalyst-chassis-any-1000T'),
+       ),
+       'WS-X4515' => array
+       (
+               'pattern' => '@^WS-X4515@',
+               'dict_key' => 2503,
+               'text' => 'WS-X4515: supervisor module',
+               'processors' => array ('catalyst-chassis-any-1000GBIC'),
+       ),
+       'WS-X6K-SUP2-2GE' => array
+       (
+               'pattern' => '@^WS-X6K-S(2|2U)-(P|MS)FC2@',
+               'dict_key' => 2502,
+               'text' => 'WS-X6K-SUP2-2GE: supervisor module',
+               'processors' => array ('catalyst-module-sup-g1-1000SFP', 'catalyst-module-sup-g2-1000SFP'),
+       ),
+       'WS-SUP720-3B' => array
+       (
+               'pattern' => '@^WS-SUP720-3B@',
+               'dict_key' => 1552,
+               'text' => 'WS-SUP720-3B: supervisor module',
+               'processors' => array ('catalyst-module-sup-g1-1000SFP', 'catalyst-module-sup-g2-1000SFP', 'catalyst-module-sup-g2-1000T'),
+       ),
+       'WS-XSUP720-3BXL' => array
+       (
+               'pattern' => '@^WS-XSUP720-3BXL@',
+               'dict_key' => 1553,
+               'text' => 'WS-XSUP720-3BXL: supervisor module',
+               'processors' => array ('catalyst-module-sup-g1-1000SFP', 'catalyst-module-sup-g2-1000SFP', 'catalyst-module-sup-g2-1000T'),
+       ),
+       'WS-X4548-GB-RJ45' => array
+       (
+               'pattern' => '@^WS-X4548-GB-RJ45@',
+               'dict_key' => 2504,
+               'text' => 'WS-X4548-GB-RJ45: 48 RJ-45/10-100-1000T(X)',
+               'processors' => array ('catalyst-chassis-any-1000T'),
+       ),
+       'WS-X6148-GE-TX' => array
+       (
+               'pattern' => '@^WS-X6148-GE-TX@',
+               'dict_key' => 1536,
+               'text' => 'WS-X6148-GE-TX: 48 RJ-45/10-100-1000T(X)',
+               'processors' => array ('catalyst-module-any-1000T'),
+       ),
+       'WS-X6148-GE-45AF' => array
+       (
+               'pattern' => '@^WS-X6148-GE-45AF@',
+               'dict_key' => 1537,
+               'text' => 'WS-X6148-GE-45AF: 48 RJ-45/10-100-1000T(X) PoE',
+               'processors' => array ('catalyst-module-any-1000T'),
+       ),
+       'WS-X6148A-GE-TX' => array
+       (
+               'pattern' => '@^WS-X6148A-GE-TX@',
+               'dict_key' => 1538,
+               'text' => 'WS-X6148A-GE-TX: 48 RJ-45/10-100-1000T(X)',
+               'processors' => array ('catalyst-module-any-1000T'),
+       ),
+       'WS-X6408A-GBIC' => array
+       (
+               'pattern' => '@^WS-X6408A-GBIC@',
+               'dict_key' => 1539,
+               'text' => 'WS-X6408A-GBIC: 8 GBIC/1000',
+               'processors' => array ('catalyst-module-any-1000GBIC'),
+       ),
+       'WS-X6416-GBIC' => array
+       (
+               'pattern' => '@^WS-X6416-GBIC@',
+               'dict_key' => 1540,
+               'text' => 'WS-X6416-GBIC: 16 GBIC/1000',
+               'processors' => array ('catalyst-module-any-1000GBIC'),
+       ),
+       'WS-X6516A-GBIC' => array
+       (
+               'pattern' => '@^WS-X6516A-GBIC@',
+               'dict_key' => 1541,
+               'text' => 'WS-X6516A-GBIC: 16 GBIC/1000',
+               'processors' => array ('catalyst-module-any-1000GBIC'),
+       ),
+       'WS-X6548-GE-TX' => array
+       (
+               'pattern' => '@^WS-X6548-GE-TX@',
+               'dict_key' => 1542,
+               'text' => 'WS-X6548-GE-TX: 48 RJ-45/10-100-1000T(X)',
+               'processors' => array ('catalyst-module-any-1000T'),
+       ),
+       'WS-X6548-GE-45AF' => array
+       (
+               'pattern' => '@^WS-X6548-GE-45AF@',
+               'dict_key' => 1543,
+               'text' => 'WS-X6548-GE-45AF: 48 RJ-45/10-100-1000T(X) PoE',
+               'processors' => array ('catalyst-module-any-1000T'),
+       ),
+       'WS-X6704-10GE' => array
+       (
+               'pattern' => '@^WS-X6704-10GE@',
+               'dict_key' => 1544,
+               'text' => 'WS-X6704-10GE: 4 XENPAK',
+               'processors' => array ('catalyst-module-any-10000XENPAK'),
+       ),
+       'WS-X6708-10G-3C' => array
+       (
+               'pattern' => '@^WS-X6708-10G-3C@',
+               'dict_key' => 1545,
+               'text' => 'WS-X6708-10G-3C: 8 X2',
+               'processors' => array ('catalyst-module-any-10000X2'),
+       ),
+       'WS-X6708-10G-3CXL' => array
+       (
+               'pattern' => '@^WS-X6708-10G-3CXL@',
+               'dict_key' => 1546,
+               'text' => 'WS-X6708-10G-3CXL: 8 X2',
+               'processors' => array ('catalyst-module-any-10000X2'),
+       ),
+       'WS-X6716-10GT-3C' => array
+       (
+               'pattern' => '@^WS-X6716-10GT-3C@',
+               'dict_key' => 1547,
+               'text' => 'WS-X6716-10GT-3C: 16 X2',
+               'processors' => array ('catalyst-module-any-10000X2'),
+       ),
+       'WS-X6716-10GT-3CXL' => array
+       (
+               'pattern' => '@^WS-X6716-10GT-3CXL@',
+               'dict_key' => 1548,
+               'text' => 'WS-X6716-10GT-3CXL: 16 X2',
+               'processors' => array ('catalyst-module-any-10000X2'),
+       ),
+       'WS-X6724-SFP' => array
+       (
+               'pattern' => '@^WS-X6724-SFP@',
+               'dict_key' => 1549,
+               'text' => 'WS-X6724-SFP: 24 SFP/1000',
+               'processors' => array ('catalyst-module-any-1000SFP'),
+       ),
+       'WS-X6748-SFP' => array
+       (
+               'pattern' => '@^WS-X6748-SFP@',
+               'dict_key' => 1551,
+               'text' => 'WS-X6748-SFP: 48 SFP/1000',
+               'processors' => array ('catalyst-module-any-1000SFP'),
+       ),
+       'WS-X6748-GE-TX' => array
+       (
+               'pattern' => '@^WS-X6748-GE-TX@',
+               'dict_key' => 1550,
+               'text' => 'WS-X6748-GE-TX: 48 RJ-45/10-100-1000T(X)',
+               'processors' => array ('catalyst-module-any-1000T'),
+       ),
+);
+
 global $swtype_pcre;
 $swtype_pcre = array
 (
@@ -3182,6 +3620,21 @@ $swtype_pcre = array
        '/^Dell Force10 OS\b.*\bApplication Software Version: 8(\.\d+){3}/' => 1594,
 );
 
+global $chapter_to_objtype_map;
+$chapter_to_objtype_map = array
+(
+       11 => 4,
+       12 => 8,
+       17 => 7,
+       18 => 5,
+       24 => 798,
+       25 => 965,
+       26 => 1055,
+       27 => 2,
+       30 => 1503,
+       38 => 1787,
+);
+
 function updateStickerForCell ($cell, $attr_id, $new_value)
 {
        if
@@ -3226,10 +3679,132 @@ function checkPIC ($port_type_id)
        }
 }
 
+// Recursive function handles sub-modules
+function addModules ($parent_id, $object_id, $module_key = NULL)
+{
+       $self = __FUNCTION__;
+       global $chapter_to_objtype_map, $known_modules, $iftable_processors, $snmp_data, $objectInfo;
+       $count = count ($snmp_data['entPhysicalContainedIn']);
+       $children = array_keys ($snmp_data['entPhysicalContainedIn'], $parent_id);
+       foreach ($children as $id)
+       {
+               // it should be added as a port
+               if (array_key_exists ($id, $snmp_data['entToIfMapping']))
+                       addModulePort ($id, $object_id, $module_key);
+
+               // it should be added as an object
+               foreach ($known_modules as $key => $attr)
+                       // the model string may be stored in entPhysicalDescr or entPhysicalModelName
+                       if (preg_match ($attr['pattern'], $snmp_data['entPhysicalDescr'][$id]) or preg_match ($attr['pattern'], $snmp_data['entPhysicalModelName'][$id]))
+                       {
+                               $module_key = $key;
+
+                               // sometimes ports are registered to a child object such as a motherboard
+                               // in such cases, we want them assigned to the origin object itself (example: Cisco 2801 ISR)
+                               if (isset ($known_modules[$module_key]['assign_to_origin']))
+                               {
+                                       addModulePort ($id, $object_id, $module_key);
+                                       break;
+                               }
+                               showSuccess ($known_modules[$module_key]['text']);
+
+                               // entPhysicalName contains the name of the slot in most cases.
+                               // But sometimes it contains a non-unique string, such as 'Linecard'.
+                               // If that is true, use the id instead, which can be assumed to be unique.
+                               $module_name_known = TRUE;
+                               $module_name = $snmp_data['entPhysicalName'][$id];
+                               if (strlen ($snmp_data['entPhysicalName'][$id]) > 3)
+                               {
+                                       $module_name_known = FALSE;
+                                       $module_name = $id;
+                               }
+
+                               // determine object type
+                               $dict_entry = getDictionaryEntry ($attr['dict_key']);
+                               if (! array_key_exists ($dict_entry['chapter_id'], $chapter_to_objtype_map))
+                               {
+                                       showError ('No dictionary entry, terminating');
+                                       break 2;
+                               }
+
+                               $objtype_id = $chapter_to_objtype_map[$dict_entry['chapter_id']];
+                               $child_object_id = commitAddObject ("${objectInfo['name']} - module ${module_name}", NULL, $objtype_id, NULL);
+                               $child_objectInfo = spotEntity ('object', $child_object_id);
+                               $child_objectInfo['attrs'] = getAttrValues ($child_object_id);
+                               updateStickerForCell ($child_objectInfo, 1, $snmp_data['entPhysicalSerialNum'][$id]); // OEM S/N 1
+                               updateStickerForCell ($child_objectInfo, 2, $attr['dict_key']); // HW type
+                               updateStickerForCell ($child_objectInfo, 5, $snmp_data['entPhysicalFirmwareRev'][$id]); // SW version
+                               if ($module_name_known)
+                                       updateStickerForCell ($child_objectInfo, 28, $module_name); // Slot number
+                               commitLinkEntities ('object', $object_id, 'object', $child_object_id);
+
+                               // add model-specific module details
+                               switch ($module_key)
+                               {
+                               case 'J8726A':
+                                       checkPIC ('1-681');
+                                       commitAddPort ($child_object_id, 'con0', '1-681', 'console', ''); // DB-9 RS-232 console
+                                       break;
+                               case 'NPE-G2':
+                                       checkPIC ('1-29');
+                                       commitAddPort ($child_object_id, 'con0', '1-29', 'console', ''); // RJ-45 RS-232 console
+                                       commitAddPort ($child_object_id, 'aux0', '1-29', 'auxillary', ''); // RJ-45 RS-232 aux port
+                                       checkPIC ('1-19');
+                                       commitAddPort ($child_object_id, 'mgmt', '1-19', '', ''); // 100Mb OOB mgmt
+                                       break;
+                               case 'WS-SUP720-3B':
+                               case 'WS-X6K-SUP2-2GE':
+                               case 'WS-XSUP720-3BXL':
+                                       checkPIC ('1-29');
+                                       commitAddPort ($child_object_id, 'con0', '1-29', 'console', ''); // RJ-45 RS-232 console
+                                       break;
+                               case 'WS-X4515':
+                                       checkPIC ('1-29');
+                                       commitAddPort ($child_object_id, 'con0', '1-29', 'console', ''); // RJ-45 RS-232 console
+                                       checkPIC ('1-19');
+                                       commitAddPort ($child_object_id, 'eobc', '1-19', 'mgmt', ''); // 100Mb OOB mgmt
+                                       break;
+                               }
+                               break;
+                       }
+
+               // if a new child object was just created, find children of that; otherwise, find children of the object passed as an argument
+               $search_object_id = isset ($child_object_id) ? $child_object_id : $object_id;
+               $self ($id, $search_object_id, $module_key);
+       }
+}
+
+function addModulePort ($ent_id, $object_id, $module_key)
+{
+       global $known_modules, $iftable_processors, $snmp_data;
+       $ifId = $snmp_data['entToIfMapping'][$ent_id];
+       foreach ($known_modules[$module_key]['processors'] as $processor_name)
+       {
+               $pattern = $iftable_processors[$processor_name]['pattern'];
+               $replacement = $iftable_processors[$processor_name]['replacement'];
+               $newname = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['replacement'], $snmp_data['entPhysicalName'][$ent_id], 1, $count);
+               if ($newname === NULL)
+               {
+                       showError ('PCRE pattern error, terminating');
+                       break;
+               }
+               if (!$count)
+                       continue; // try next processor on current port
+               $newlabel = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['label'], $snmp_data['entPhysicalName'][$ent_id], 1, $count);
+               checkPIC ($iftable_processors[$processor_name]['dict_key']);
+               // By default, the 'skip_macs' key of the known_switch_modules array doesn't exist.  If it does exist, it can be assumed that it's true.
+               $mac = (isset ($known_modules[$module_key]['skip_macs'])) ? NULL : $snmp_data['ifPhysAddress'][$ifId];
+               commitAddPort ($object_id, $newname, $iftable_processors[$processor_name]['dict_key'], $newlabel, $mac);
+               if (!$iftable_processors[$processor_name]['try_next_proc']) // done with this port
+                       break;
+       }
+}
+
 $msgcode['doSNMPmining']['ERR1'] = 161;
 $msgcode['doSNMPmining']['ERR2'] = 162;
 function doSNMPmining ($object_id, $snmpsetup)
 {
+       global $objectInfo;
        $objectInfo = spotEntity ('object', $object_id);
        $objectInfo['attrs'] = getAttrValues ($object_id);
        $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
@@ -3246,22 +3821,23 @@ function doSNMPmining ($object_id, $snmpsetup)
 
        switch ($objectInfo['objtype_id'])
        {
-       case 7:   // Router
-       case 8:   // Network switch
-       case 965: // Wireless
+       case 7:    // Router
+       case 8:    // Network switch
+       case 965:  // Wireless
+       case 1503: // Network chassis
                $device = new RTSNMPDevice ($endpoints[0], $snmpsetup);
-               return doSwitchSNMPmining ($objectInfo, $device);
+               return doGenericSNMPmining ($device);
        case 2:
                $device = new APCPowerSwitch ($endpoints[0], $snmpsetup);
-               return doPDUSNMPmining ($objectInfo, $device);
+               return doPDUSNMPmining ($device);
        }
 }
 
-$msgcode['doSwitchSNMPmining']['ERR3'] = 188;
-$msgcode['doSwitchSNMPmining']['ERR4'] = 189;
-function doSwitchSNMPmining ($objectInfo, $device)
+$msgcode['doGenericSNMPmining']['ERR3'] = 188;
+$msgcode['doGenericSNMPmining']['ERR4'] = 189;
+function doGenericSNMPmining ($device)
 {
-       global $known_switches, $iftable_processors;
+       global $objectInfo, $known_devices, $iftable_processors;
 
        if (FALSE === ($sysObjectID = $device->snmpget ('sysObjectID.0')))
        {
@@ -3269,7 +3845,7 @@ function doSwitchSNMPmining ($objectInfo, $device)
                return;
        }
        $sysObjectID = preg_replace ('/^.*(enterprises\.|joint-iso-ccitt\.)([\.[:digit:]]+)$/', '\\2', $sysObjectID);
-       if (!isset ($known_switches[$sysObjectID]))
+       if (!isset ($known_devices[$sysObjectID]))
        {
                showFuncMessage (__FUNCTION__, 'ERR4', array ($sysObjectID)); // unknown OID
                return;
@@ -3277,15 +3853,8 @@ function doSwitchSNMPmining ($objectInfo, $device)
        $sysName = substr ($device->snmpget ('sysName.0'), strlen ('STRING: '));
        $sysDescr = substr ($device->snmpget ('sysDescr.0'), strlen ('STRING: '));
        $sysDescr = str_replace (array ("\n", "\r"), " ", $sysDescr);  // Make it one line
-       $ifDescr_tablename = array_fetch ($known_switches[$sysObjectID], 'ifDescrOID', 'ifDescr');
-       showSuccess ($known_switches[$sysObjectID]['text']);
-       foreach (array_keys ($known_switches[$sysObjectID]['processors']) as $pkey)
-               if (!array_key_exists ($known_switches[$sysObjectID]['processors'][$pkey], $iftable_processors))
-               {
-                       showWarning ('processor "' . $known_switches[$sysObjectID]['processors'][$pkey] . '" not found');
-                       unset ($known_switches[$sysObjectID]['processors'][$pkey]);
-               }
-       updateStickerForCell ($objectInfo, 2, $known_switches[$sysObjectID]['dict_key']);
+       showSuccess ($known_devices[$sysObjectID]['text']);
+       updateStickerForCell ($objectInfo, 2, $known_devices[$sysObjectID]['dict_key']); // HW type
        updateStickerForCell ($objectInfo, 3, $sysName);
        detectSoftwareType ($objectInfo, $sysDescr);
        switch (1)
@@ -3308,11 +3877,25 @@ function doSwitchSNMPmining ($objectInfo, $device)
                $sysChassi = $device->snmpget ('1.3.6.1.4.1.9.3.6.3.0');
                if ($sysChassi !== FALSE or $sysChassi !== NULL)
                        updateStickerForCell ($objectInfo, 1, str_replace ('"', '', substr ($sysChassi, strlen ('STRING: '))));
-               checkPIC ('1-29');
-               commitAddPort ($objectInfo['id'], 'con0', '1-29', 'console', ''); // RJ-45 RS-232 console
+               // some models have the console port located on a module instead of the chassis
+               $no_console = array
+               (
+                       '9.1.283',
+                       '9.1.503'
+               );
+               if (! in_array ($sysObjectID, $no_console))
+               {
+                       checkPIC ('1-29');
+                       commitAddPort ($objectInfo['id'], 'con0', '1-29', 'console', ''); // RJ-45 RS-232 console
+               }
                if (preg_match ('/Cisco IOS Software, C2600/', $sysDescr))
                        commitAddPort ($objectInfo['id'], 'aux0', '1-29', 'auxillary', ''); // RJ-45 RS-232 aux port
-               if ($sysObjectID == '9.1.956')
+               $dual_ac = array
+               (
+                       '9.1.283',
+                       '9.1.956'
+               );
+               if (in_array ($sysObjectID, $dual_ac))
                {
                        // models with two AC inputs
                        checkPIC ('1-16');
@@ -3595,61 +4178,169 @@ function doSwitchSNMPmining ($objectInfo, $device)
        default: // Nortel...
                break;
        }
-       $ifInfo = array();
-       foreach ($device->snmpwalkoid ($ifDescr_tablename) as $oid => $value)
+       if (isset ($known_devices[$sysObjectID]['modular']))
        {
-               $randomindex = preg_replace ("/^.*${ifDescr_tablename}\.(.+)\$/", '\\1', $oid);
-               $value = trim (preg_replace ('/^[^:]+: (.+)$/', '\\1', $value), '"');
-               $ifInfo[$randomindex]['ifDescr'] = $value;
-       }
-       foreach ($device->snmpwalkoid ('ifPhysAddress') as $oid => $value)
-       {
-               $randomindex = preg_replace ("/^.*ifPhysAddress\.(.+)\$/", '\\1', $oid);
-               $value = trim ($value);
-               // NET-SNMP may return MAC addresses in one of two (?) formats depending on
-               // DISPLAY-HINT internal database. The best we can do about it is to accept both.
-               // Bug originally reported by Walery Wysotsky against openSUSE 11.0.
-               if (preg_match ('/^string: [0-9a-f]{1,2}(:[0-9a-f]{1,2}){5}/i', $value)) // STRING: x:yy:z:xx:y:zz
+               // this is a modular device
+               // TODO: put the entire mining operation inside a single transaction (not possible now because commitAddPort() LOCKs/UNLOCKs the Port table which auto-commits any open transactions)
+               global $snmp_data;
+               $snmp_data = array ();
+               foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.4') as $raw_key => $raw_value)
+               {
+                       $key = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.4.'));
+                       $value = substr ($raw_value, strlen ('INTEGER: '));
+                       $snmp_data['entPhysicalContainedIn'][$key] = $value;
+               }
+               foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.2') as $raw_key => $raw_value)
+               {
+                       $key = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.2.'));
+                       $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                       $snmp_data['entPhysicalDescr'][$key] = $value;
+               }
+               foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.7') as $raw_key => $raw_value)
+               {
+                       $key = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.7.'));
+                       $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                       $snmp_data['entPhysicalName'][$key] = $value;
+               }
+               foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.13') as $raw_key => $raw_value)
+               {
+                       $key = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.13.'));
+                       $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                       $snmp_data['entPhysicalModelName'][$key] = $value;
+               }
+               foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.11') as $raw_key => $raw_value)
+               {
+                       $key = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.11.'));
+                       $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                       $snmp_data['entPhysicalSerialNum'][$key] = $value;
+               }
+               updateStickerForCell ($objectInfo, 1, $snmp_data['entPhysicalSerialNum'][1]); // OEM S/N 1, safe to assume that the chassis is the first array element
+               foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.9') as $raw_key => $raw_value)
                {
-                       list ($dummy, $value) = explode (' ', $value);
-                       $addrbytes = explode (':', $value);
-                       foreach ($addrbytes as $bidx => $bytestr)
-                               if (strlen ($bytestr) == 1)
-                                       $addrbytes[$bidx] = '0' . $bytestr;
+                       $key = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.9.'));
+                       $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                       $snmp_data['entPhysicalFirmwareRev'][$key] = $value;
                }
-               elseif (preg_match ('/^hex-string:( [0-9a-f]{2}){6}/i', $value)) // Hex-STRING: xx yy zz xx yy zz
-                       $addrbytes = explode (' ', substr ($value, -17));
-               elseif (preg_match ('/22[0-9a-f]{12}22$/', bin2hex ($value))) // STRING: "??????"
-                       $addrbytes = array (substr (bin2hex ($value), -14, 12));
+               foreach ($device->snmpwalkoid ('ifPhysAddress') as $raw_key => $raw_value)
+               {
+                       $key = substr ($raw_key, strlen ('IF-MIB::ifPhysAddress.'));
+                       $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                       if (preg_match ('/^[0-9a-f]{1,2}(:[0-9a-f]{1,2}){5}/i', $value)) // x:yy:z:xx:y:zz
+                       {
+                               $addrbytes = explode (':', $value);
+                               foreach ($addrbytes as $bidx => $bytestr)
+                                       if (strlen ($bytestr) == 1)
+                                               $addrbytes[$bidx] = '0' . $bytestr;
+                       }
+                       elseif (preg_match ('/^[0-9a-f]{1,2}( [0-9a-f]{1,2}){5}/i', $value)) // xx yy zz xx yy zz
+                               $addrbytes = explode (' ', substr ($value, -17));
+                       elseif (preg_match ('/22[0-9a-f]{12}22$/', bin2hex ($value))) // "??????"
+                               $addrbytes = array (substr (bin2hex ($value), -14, 12));
+                       else
+                               break; // martian format
+                       // if this port's MAC is already assigned to another port, mark it as an empty string instead of causing a conflict
+                       $mac = implode ('', $addrbytes);
+                       $snmp_data['ifPhysAddress'][$key] = (array_key_exists ('ifPhysAddress', $snmp_data) and in_array ($mac, $snmp_data['ifPhysAddress'])) ? '' : $mac;
+               }
+               // map entPhysicalIndex to ifIndex for MAC addresses association
+               // first try to use entAliasMappingIdentifier (some devices don't support it)
+               if ($entToIfMapping_raw = @$device->snmpwalkoid ('mib-2.47.1.3.2.1.2'))
+                       foreach ($entToIfMapping_raw as $key => $value)
+                       {
+                               // find the entID and map it to the ifID
+                               $e_array = explode ('.', $key);
+                               $e_index = count ($e_array) - 2;
+                               $eid = $e_array[$e_index];
+                               $iid = substr ($value, strrpos ($value, '.') + 1);
+                               $snmp_data['entToIfMapping'][$eid] = $iid; 
+                       }
                else
-                       continue; // martian format
-               $ifInfo[$randomindex]['ifPhysAddress'] = implode ('', $addrbytes);
+               {
+                       // entAliasMappingIdentifier isn't available, use entPhysicalName
+                       $ifName = array ();
+                       foreach ($device->snmpwalkoid ('ifName') as $raw_key => $raw_value)
+                       {
+                               $key = substr ($raw_key, strlen ('IF-MIB::ifName.'));
+                               $value = str_ireplace (array ('STRING: ', '"'), '', $raw_value);
+                               $ifName[$key] = $value;
+                       }
+                       // query entPhysicalClass and note which entries which are ports
+                       foreach ($device->snmpwalkoid ('mib-2.47.1.1.1.1.5') as $raw_key => $raw_value)
+                               if (substr ($raw_value, strlen ('INTEGER: ')) == 10)
+                                       $snmp_data['entPhysicalPorts'][] = substr ($raw_key, strlen ('SNMPv2-SMI::mib-2.47.1.1.1.1.5.'));
+                       // do the mapping, but only for ports
+                       foreach ($snmp_data['entPhysicalName'] as $key => $value)
+                               if (in_array ($key, $snmp_data['entPhysicalPorts']))
+                                       $snmp_data['entToIfMapping'][$key] = array_search ($value, $ifName);
+               }
+               addModules (1, $objectInfo['id']);
        }
-       // process each interface only once regardless of how many processors we have to run
-       foreach ($ifInfo as $iface)
-               foreach ($known_switches[$sysObjectID]['processors'] as $processor_name)
+       else
+       {
+               // this is not a modular device
+               $ifDescr_tablename = (isset ($known_devices[$sysObjectID]['ifDescrOID'])) ? $known_devices[$sysObjectID]['ifDescrOID'] : 'ifDescr';
+               foreach (array_keys ($known_devices[$sysObjectID]['processors']) as $pkey)
+                       if (!array_key_exists ($known_devices[$sysObjectID]['processors'][$pkey], $iftable_processors))
+                       {
+                               showWarning ('processor "' . $known_devices[$sysObjectID]['processors'][$pkey] . '" not found');
+                               unset ($known_devices[$sysObjectID]['processors'][$pkey]);
+                       }
+               $ifInfo = array ();
+               foreach ($device->snmpwalkoid ($ifDescr_tablename) as $oid => $value)
+               {
+                       $randomindex = preg_replace ("/^.*${ifDescr_tablename}\.(.+)\$/", '\\1', $oid);
+                       $value = trim (preg_replace ('/^[^:]+: (.+)$/', '\\1', $value), '"');
+                       $ifInfo[$randomindex]['ifDescr'] = $value;
+               }
+               foreach ($device->snmpwalkoid ('ifPhysAddress') as $oid => $value)
                {
-                       $newname = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['replacement'], $iface['ifDescr'], 1, $count);
-                       if ($newname === NULL)
+                       $randomindex = preg_replace ("/^.*ifPhysAddress\.(.+)\$/", '\\1', $oid);
+                       $value = trim ($value);
+                       // NET-SNMP may return MAC addresses in one of two (?) formats depending on
+                       // DISPLAY-HINT internal database. The best we can do about it is to accept both.
+                       // Bug originally reported by Walery Wysotsky against openSUSE 11.0.
+                       if (preg_match ('/^string: [0-9a-f]{1,2}(:[0-9a-f]{1,2}){5}/i', $value)) // STRING: x:yy:z:xx:y:zz
                        {
-                               showError ('PCRE pattern error, terminating');
-                               break 2;
+                               list ($dummy, $value) = explode (' ', $value);
+                               $addrbytes = explode (':', $value);
+                               foreach ($addrbytes as $bidx => $bytestr)
+                                       if (strlen ($bytestr) == 1)
+                                               $addrbytes[$bidx] = '0' . $bytestr;
                        }
-                       if (!$count)
-                               continue; // try next processor on current port
-                       $newlabel = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['label'], $iface['ifDescr'], 1, $count);
-                       checkPIC ($iftable_processors[$processor_name]['dict_key']);
-                       commitAddPort ($objectInfo['id'], $newname, $iftable_processors[$processor_name]['dict_key'], $newlabel, $iface['ifPhysAddress']);
-                       if (!$iftable_processors[$processor_name]['try_next_proc']) // done with this port
-                               continue 2;
+                       elseif (preg_match ('/^hex-string:( [0-9a-f]{2}){6}/i', $value)) // Hex-STRING: xx yy zz xx yy zz
+                               $addrbytes = explode (' ', substr ($value, -17));
+                       elseif (preg_match ('/22[0-9a-f]{12}22$/', bin2hex ($value))) // STRING: "??????"
+                               $addrbytes = array (substr (bin2hex ($value), -14, 12));
+                       else
+                               continue; // martian format
+                       $ifInfo[$randomindex]['ifPhysAddress'] = implode ('', $addrbytes);
                }
+               // process each interface only once regardless of how many processors we have to run
+               foreach ($ifInfo as $iface)
+                       foreach ($known_devices[$sysObjectID]['processors'] as $processor_name)
+                       {
+                               $newname = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['replacement'], $iface['ifDescr'], 1, $count);
+                               if ($newname === NULL)
+                               {
+                                       showError ('PCRE pattern error, terminating');
+                                       break 2;
+                               }
+                               if (!$count)
+                                       continue; // try next processor on current port
+                               $newlabel = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['label'], $iface['ifDescr'], 1, $count);
+                               checkPIC ($iftable_processors[$processor_name]['dict_key']);
+                               commitAddPort ($objectInfo['id'], $newname, $iftable_processors[$processor_name]['dict_key'], $newlabel, $iface['ifPhysAddress']);
+                               if (!$iftable_processors[$processor_name]['try_next_proc']) // done with this port
+                                       continue 2;
+                       }
+       }
        // No failure up to this point, thus leave current tab for the "Ports" one.
        return buildRedirectURL (NULL, 'ports');
 }
 
-function doPDUSNMPmining ($objectInfo, $switch)
+function doPDUSNMPmining ($switch)
 {
-       global $known_APC_SKUs;
+       global $objectInfo, $known_APC_SKUs;
        if (FALSE !== ($dict_key = array_search ($switch->getHWModel(), $known_APC_SKUs)))
                updateStickerForCell ($objectInfo, 2, $dict_key);
        updateStickerForCell ($objectInfo, 1, $switch->getHWSerial());
@@ -3859,109 +4550,6 @@ class APCPowerSwitch extends RTSNMPDevice
        }
 }
 
-// Take address in the form XX:XX:XX:XX:XX:XX and return the next
-// address in the same form.
-function nextMACAddress ($addr)
-{
-       if ($addr == '')
-               return '';
-       $bytes = array();
-       foreach (explode (':', $addr) as $hex)
-               $bytes[] = hexdec ($hex);
-       for ($i = 5; $i >= 0; $i--)
-       {
-               $bytes[$i] += 1;
-               if ($bytes[$i] <= 255) // FF
-                       break; // no roll over
-               $bytes[$i] = 0;
-       }
-       foreach (array_keys ($bytes) as $key)
-               $bytes[$key] = sprintf ('%02X', $bytes[$key]);
-       return implode (':', $bytes);
-}
-
-function generatePortsForCatModule ($object_id, $slotno = 1, $mtype = 'X6748', $mac_address = '')
-{
-       global $dbxlink;
-       $mac_address = l2addressFromDatabase (l2addressForDatabase ($mac_address));
-       switch ($mtype)
-       {
-       case 'WS-X6748-GE-TX':
-               $dbxlink->beginTransaction();
-               for ($i = 1; $i <= 48; $i++)
-               {
-                       commitAddPort ($object_id, "gi${slotno}/${i}", '1-24', "slot ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               $dbxlink->commit();
-               break;
-       case 'WS-X6708-10GE':
-               for ($i = 1; $i <= 8; $i++)
-               {
-                       commitAddPort ($object_id, "te${slotno}/${i}", '6-1080', "slot ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               break;
-       case 'WS-X6704-10GE':
-               for ($i = 1; $i <= 4; $i++)
-               {
-                       commitAddPort ($object_id, "te${slotno}/${i}", '5-1079', "slot ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               break;
-       case 'VS-S720-10G':
-               commitAddPort ($object_id, "gi${slotno}/1", '4-1077', "slot ${slotno} port 1", $mac_address);
-               $mac_address = nextMACAddress ($mac_address);
-               commitAddPort ($object_id, "gi${slotno}/2", '4-1077', "slot ${slotno} port 2", $mac_address);
-               $mac_address = nextMACAddress ($mac_address);
-               commitAddPort ($object_id, "gi${slotno}/3", '1-24',   "slot ${slotno} port 3", $mac_address);
-               $mac_address = nextMACAddress ($mac_address);
-               commitAddPort ($object_id, "te${slotno}/4", '6-1080', "slot ${slotno} port 4", $mac_address);
-               $mac_address = nextMACAddress ($mac_address);
-               commitAddPort ($object_id, "te${slotno}/5", '6-1080', "slot ${slotno} port 5", $mac_address);
-               break;
-       case '3750G-24TS':
-               // MAC address of 1st port is the next one after switch's address
-               $mac_address = nextMACAddress ($mac_address);
-               for ($i = 1; $i <= 24; $i++)
-               {
-                       commitAddPort ($object_id, "gi${slotno}/0/${i}", '1-24', "unit ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               for ($i = 25; $i <= 28; $i++)
-               {
-                       commitAddPort ($object_id, "gi${slotno}/0/${i}", '4-1077', "unit ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               break;
-       case '3750G-24T':
-               $mac_address = nextMACAddress ($mac_address);
-               for ($i = 1; $i <= 24; $i++)
-               {
-                       commitAddPort ($object_id, "gi${slotno}/0/${i}", '1-24', "unit ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               break;
-       case '3750G-16TD':
-               $mac_address = nextMACAddress ($mac_address);
-               for ($i = 1; $i <= 16; $i++)
-               {
-                       commitAddPort ($object_id, "gi${slotno}/0/${i}", '1-24', "unit ${slotno} port ${i}", $mac_address);
-                       $mac_address = nextMACAddress ($mac_address);
-               }
-               commitAddPort ($object_id, "te${slotno}/0/1", '5-1079', "unit ${slotno} port ${i}", $mac_address);
-               break;
-       case 'LE02G48TA':
-               for ($i = 0; $i <= 47; $i++)
-                       commitAddPort ($object_id, "gi${slotno}/0/${i}", '1-24', "slot ${slotno} port ${i}", $mac_address);
-               break;
-       case 'LE02X12SA':
-               for ($i = 0; $i <= 11; $i++)
-                       commitAddPort ($object_id, "gi${slotno}/0/${i}", '9-1084', "slot ${slotno} port ${i}", $mac_address);
-               break;
-       }
-}
-
 function detectSoftwareType ($objectInfo, $sysDescr)
 {
        global $swtype_pcre;
index 4df2085..0fff4c0 100644 (file)
@@ -78,9 +78,10 @@ function trigger_snmpportfinder ()
        $object = spotEntity ('object', getBypassValue());
        switch ($object['objtype_id'])
        {
-       case 7:   // any router
-       case 8:   // or switch
-       case 965: // or wireless device would suffice
+       case 7:    // any router
+       case 8:    // or switch
+       case 965:  // or wireless device
+       case 1503: // or network chassis would suffice
                return $object['nports'] ? '' : 'attn';
        case 2: // but only selected PDUs
                if ($object['nports'])
@@ -113,7 +114,7 @@ function trigger_ip ()
 
 function trigger_natv4 ()
 {
-       if (!count (getObjectIPv4AllocationList (getBypassValue())))
+       if (!count (getObjectIPvNAllocationList (getBypassValue(), 4)))
                return '';
        return considerConfiguredConstraint (spotEntity ('object', getBypassValue()), 'IPV4NAT_LISTSRC') ? 'std' : '';
 }
index 46be172..377eab8 100644 (file)
@@ -1820,6 +1820,9 @@ CREATE TABLE `PatchCableOIFCompat` (
                        $query[] = "UPDATE Config SET varvalue = '0.20.9' WHERE varname = 'DB_VERSION'";
                        break;
                case '0.21.0':
+                       // some HW types were moved from the 'Network switch' chapter to the 'Network chassis' chapter
+                       // change the type of affected objects to 'Network chassis'
+                       $query[] = "UPDATE `Object` SET objtype_id = 1503 WHERE id IN (SELECT object_id FROM `AttributeValue` WHERE attr_id = 2 and uint_value IN (145,146,372,373,374,375))";
                        $query[] = "UPDATE Config SET varvalue = '0.21.0' WHERE varname = 'DB_VERSION'";
                        break;
                case 'dictionary':