r3636 Initial, basic implementation of CDP support:
authorDenis Ovsienko <infrastation@yandex.ru>
Wed, 19 May 2010 21:52:08 +0000 (21:52 +0000)
committerDenis Ovsienko <infrastation@yandex.ru>
Wed, 19 May 2010 21:52:08 +0000 (21:52 +0000)
deviceconfig: add handling of "getcdpstatus" command
navigation: add new tab records
getPortID(): rewrite into getPortIDs() to return all records found
addMultiPorts(): update respectively
searchByMgmtHostname(): new helper function
ios12ShortenIfName(): new helper function
ios12ScanTopLevel(): employ above
ios12ReadCDPStatus(): new function, part of CDP status reader FSM
ios12ScanCDPTopLevel(): idem
ios12ScanCDPEntry(): idem
formatPortIIFOIF(): new helper function
renderRackObject(): make use of above
renderPortsForObject(): idem
renderObject8021QPorts(): idem
gwRetrieveDeviceConfig(): accept command code
getRunning8021QConfig(): update respectively
getRunningCDPStatus(): new function
trigger_LiveCDP(): new function
renderLiveCDP(): new function

gateways/deviceconfig/ios12.connector
gateways/deviceconfig/main
inc/database.php
inc/functions.php
inc/gateways.php
inc/interface.php
inc/navigation.php
inc/ophandlers.php
inc/triggers.php

index 0646797..0aa7d63 100755 (executable)
@@ -51,6 +51,10 @@ retrieve)
        printf 'term len 0\nshow run\n! END OF CONFIG\nshow vlan brief\n! END OF VLAN LIST\n' >> "$SESSION"
        outfile="$WORKFILE"
        ;;
+getcdpstatus)
+       printf 'term len 0\nshow cdp neighbors detail\n' >> "$SESSION"
+       outfile="$WORKFILE"
+       ;;
 deploy)
        cat "$WORKFILE" >> "$SESSION"
        outfile=/dev/null
index 7f2b403..257eede 100755 (executable)
@@ -56,6 +56,10 @@ do_work()
                echo 'ERR!too few arguments to connect'
                return
        fi
+       if [ "$command" = "getcdpstatus" and "$handler" != "ios12" ]; then
+               echo 'ERR!getcdpstatus only works for ios12'
+               return
+       fi
        [ -x "$MYDIR/$handler.connector" ] || {
                echo "ERR!Handler '$handler' is not available"
                exit 1
@@ -73,7 +77,7 @@ do_work()
 
 while read cmd args; do
        case $cmd in
-               retrieve|deploy)
+               retrieve|deploy|getcdpstatus)
                        do_work $cmd $args
                        ;;
                *)
index 9cfd1a9..3a74ba5 100644 (file)
@@ -1674,17 +1674,24 @@ function getObjectNATSearchResults ($what)
 }
 
 // This function returns either port ID or NULL for specified arguments.
-// This function returns either port ID or NULL for specified arguments.
-function getPortID ($object_id, $port_name)
+function getPortIDs ($object_id, $port_name)
 {
-       $query = "select id from Port where object_id=${object_id} and name='${port_name}' limit 2";
-       $result = useSelectBlade ($query);
+       $ret = array();
+       $result = usePreparedSelectBlade ('SELECT id FROM Port WHERE object_id = ? AND name = ?', array ($object_id, $port_name));
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               $ret[] = $row['id'];
+       return $ret;
+}
+
+// Search in "FQDN" attribute only, and return object ID, when there is exactly
+// one result found (and NULL in any other case).
+function searchByMgmtHostname ($string)
+{
+       $result = usePreparedSelectBlade ('SELECT object_id FROM AttributeValue WHERE attr_id = 3 AND string_value = ? LIMIT 2', array ($string));
        $rows = $result->fetchAll (PDO::FETCH_NUM);
        if (count ($rows) != 1)
                return NULL;
-       $ret = $rows[0][0];
-       $result->closeCursor();
-       return $ret;
+       return $rows[0][0];
 }
 
 function commitCreateUserAccount ($username, $realname, $password)
index 7af6198..f7cc555 100644 (file)
@@ -2379,19 +2379,24 @@ function ios12ReadVLANConfig ($input)
        return $ret;
 }
 
+// map interface name
+function ios12ShortenIfName ($ifname)
+{
+       $ifname = preg_replace ('@^Ethernet(.+)$@', 'et\\1', $ifname);
+       $ifname = preg_replace ('@^FastEthernet(.+)$@', 'fa\\1', $ifname);
+       $ifname = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $ifname);
+       $ifname = preg_replace ('@^TenGigabitEthernet(.+)$@', 'te\\1', $ifname);
+       $ifname = preg_replace ('@^Port-channel(.+)$@', 'po\\1', $ifname);
+       return $ifname;
+}
+
 function ios12ScanTopLevel (&$work, $line)
 {
        $matches = array();
        switch (TRUE)
        {
        case (preg_match ('@^interface ((Ethernet|FastEthernet|GigabitEthernet|TenGigabitEthernet|Port-channel)[[:digit:]]+(/[[:digit:]]+)*)$@', $line, $matches)):
-               // map interface name
-               $matches[1] = preg_replace ('@^Ethernet(.+)$@', 'et\\1', $matches[1]);
-               $matches[1] = preg_replace ('@^FastEthernet(.+)$@', 'fa\\1', $matches[1]);
-               $matches[1] = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $matches[1]);
-               $matches[1] = preg_replace ('@^TenGigabitEthernet(.+)$@', 'te\\1', $matches[1]);
-               $matches[1] = preg_replace ('@^Port-channel(.+)$@', 'po\\1', $matches[1]);
-               $work['current'] = array ('port_name' => $matches[1]);
+               $work['current'] = array ('port_name' => ios12ShortenIfName ($matches[1]));
                return 'ios12PickSwitchportCommand'; // switch to interface block reading
        case (preg_match ('/^VLAN Name                             Status    Ports$/', $line, $matches)):
                return 'ios12PickVLANCommand';
@@ -3084,6 +3089,61 @@ function vrp53TranslatePushQueue ($queue)
        return $ret;
 }
 
+// Read provided output of "show cdp neighbors detail" command and
+// return a list of records with (translated) local port name,
+// remote device name and (translated) remote port name.
+function ios12ReadCDPStatus ($input)
+{
+       $ret = array();
+       $procfunc = 'ios12ScanCDPTopLevel';
+       foreach (explode ("\n", $input) as $line)
+               $procfunc = $procfunc ($ret, $line);
+       unset ($ret['current']);
+       return $ret;
+}
+
+function ios12ScanCDPTopLevel (&$work, $line)
+{
+       $matches = array();
+       switch (TRUE)
+       {
+       case preg_match ('/^Device ID: (.+)$/', $line, $matches):
+               $work['current'] = array ('remote_device' => $matches[1]);
+               return 'ios12ScanCDPEntry';
+       default:
+               return __FUNCTION__; // continue scan
+       }
+}
+
+function ios12ScanCDPEntry (&$work, $line)
+{
+       if (preg_match ('/^-+$/', $line))
+       {
+               if
+               (
+                       array_key_exists ('local_port', $work['current']) and
+                       array_key_exists ('remote_port', $work['current'])
+               )
+                       $work[$work['current']['local_port']] = array
+                       (
+                               'device' => $work['current']['remote_device'],
+                               'port' => $work['current']['remote_port'],
+                       );
+               unset ($work['current']);
+               return 'ios12ScanCDPTopLevel';
+       }
+       $matches = array();
+       switch (TRUE)
+       {
+       case preg_match ('/^Interface: (.+),  Port ID \(outgoing port\): (.+)$/', $line, $matches):
+               $work['current']['local_port'] = ios12ShortenIfName ($matches[1]);
+               $work['current']['remote_port'] = ios12ShortenIfName ($matches[2]);
+               break;
+       default:
+       }
+       return __FUNCTION__;
+}
+
 // Return TRUE, if every value of A1 is present in A2 and vice versa,
 // regardless of each array's sort order and indexing.
 function array_values_same ($a1, $a2)
@@ -3969,4 +4029,13 @@ function authorize8021QChangeRequests ($before, $changes)
        return $ret;
 }
 
+function formatPortIIFOIF ($port)
+{
+       $ret = '';
+       if ($port['iif_id'] != 1)
+               $ret .= $port['iif_name'] . '/';
+       $ret .= $port['oif_name'];
+       return $ret;
+}
+
 ?>
index 1884540..fc9a7f8 100644 (file)
@@ -322,7 +322,7 @@ function getRunning8021QConfig ($object_id)
                'vrp53' => 'vrp53ReadVLANConfig',
                'nxos4' => 'nxos4Read8021QConfig',
        );
-       $ret = $reader[$breed] (dos2unix (gwRetrieveDeviceConfig ($object_id, $breed)));
+       $ret = $reader[$breed] (dos2unix (gwRetrieveDeviceConfig ($object_id, $breed, 'retrieve')));
        // Once there is no default VLAN in the parsed data, it means
        // something else was parsed instead of config text.
        if (!in_array (VLAN_DFL_ID, $ret['vlanlist']))
@@ -330,6 +330,13 @@ function getRunning8021QConfig ($object_id)
        return $ret;
 }
 
+function getRunningCDPStatus ($object_id)
+{
+       if ('' == $breed = detectDeviceBreed ($object_id))
+               throw new RuntimeException ('cannot pick handler for this device');
+       return ios12ReadCDPStatus (dos2unix (gwRetrieveDeviceConfig ($object_id, $breed, 'getcdpstatus')));
+}
+
 function setDevice8021QConfig ($object_id, $pseudocode)
 {
        if ('' == $breed = detectDeviceBreed ($object_id))
@@ -344,7 +351,7 @@ function setDevice8021QConfig ($object_id, $pseudocode)
        gwDeployDeviceConfig ($object_id, $breed, unix2dos ($xlator[$breed] ($pseudocode)));
 }
 
-function gwRetrieveDeviceConfig ($object_id, $breed)
+function gwRetrieveDeviceConfig ($object_id, $breed, $command = 'retrieve')
 {
        $objectInfo = spotEntity ('object', $object_id);
        $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
@@ -357,7 +364,7 @@ function gwRetrieveDeviceConfig ($object_id, $breed)
        $outputlines = queryGateway
        (
                'deviceconfig',
-               array ("retrieve ${endpoint} ${breed} ${tmpfilename}")
+               array ("${command} ${endpoint} ${breed} ${tmpfilename}")
        );
        $configtext = file_get_contents ($tmpfilename);
        unlink ($tmpfilename);
index a78c470..000bcb1 100644 (file)
@@ -986,9 +986,7 @@ function renderRackObject ($object_id)
                        if ($hl_port_id == $port['id'])
                                echo ' class=port_highlight';
                        echo "><td class=tdleft>${port['name']}</td><td class=tdleft>${port['label']}</td><td class=tdleft>";
-                       if ($port['iif_id'] != 1)
-                               echo $port['iif_name'] . '/';
-                       echo $port['oif_name'] . "</td><td class=tdleft><tt>${port['l2address']}</tt></td>";
+                       echo formatPortIIFOIF ($port) . "</td><td class=tdleft><tt>${port['l2address']}</tt></td>";
                        if ($port['remote_object_id'])
                        {
                                $remote_object = spotEntity ('object', $port['remote_object_id']);
@@ -1270,9 +1268,7 @@ function renderPortsForObject ($object_id)
                else
                {
                        echo "<input type=hidden name=port_type_id value='${port['oif_id']}'><td class=tdleft>";
-                       if ($port['iif_id'] != 1)
-                               echo $port['iif_name'] . '/';
-                       echo "${port['oif_name']}</td>\n";
+                       echo formatPortIIFOIF ($port) . '</td>';
                }
                // 18 is enough to fit 6-byte MAC address in its longest form,
                // while 24 should be Ok for WWN
@@ -6876,7 +6872,7 @@ function renderObject8021QPorts ($object_id)
        foreach ($object['ports'] as $port)
                if (mb_strlen ($port['name']) and array_key_exists ($port['name'], $desired_config))
                {
-                       $socket = array ('interface' => ($port['iif_id'] == 1 ? '' : $port['iif_name'] . '/') . $port['oif_name']);
+                       $socket = array ('interface' => formatPortIIFOIF ($port));
                        if ($port['remote_object_id'])
                        {
                                $remote_object = spotEntity ('object', $port['remote_object_id']);
@@ -7736,4 +7732,68 @@ function renderDeployQueue ($dqcode)
        echo '</table>';
 }
 
+function renderLiveCDP ($object_id)
+{
+       $CDP_status = getRunningCDPStatus ($object_id);
+       uksort ($CDP_status, 'sortTokenize');
+       $mydevice = spotEntity ('object', $object_id);
+       amplifyCell ($mydevice);
+       // reindex by port name
+       $myports = array();
+       foreach ($mydevice['ports'] as $port)
+               if (mb_strlen ($port['name']))
+                       $myports[$port['name']][] = $port;
+       unset ($mydevice);
+       printOpFormIntro ('importCDPData');
+       echo '<br><table cellspacing=0 cellpadding=5 align=center class=widetable>';
+       echo '<tr><th colspan=2>local port</th><th>remote device</th><th colspan=2>remote port</th><th>&nbsp;</th></tr>';
+       $inputno = 0;
+       foreach ($CDP_status as $local_port => $remote)
+       {
+               if
+               (
+                       !array_key_exists ($local_port, $myports) or
+                       NULL === $remote_id = searchByMgmtHostname ($remote['device']) or
+                       !count ($remote_port_ids = getPortIDs ($remote_id, $remote['port']))
+               )
+               {
+                       echo "<tr class=trerror><td>${local_port}</td><td>&nbsp;</td>";
+                       echo "<td>${remote['device']}</td>";
+                       echo "<td>${remote['port']}</td><td>&nbsp;</td>";
+                       echo "<td>&nbsp;</td></tr>";
+                       continue;
+               }
+               // Link already installed?
+               foreach ($myports[$local_port] as $socket)
+                       if (in_array ($socket['remote_id'], $remote_port_ids))
+                       {
+                               echo "<tr class=trok><td>${local_port}</td><td>";
+                               echo formatPortIIFOIF ($socket) . "</td>";
+                               echo "<td>${remote['device']}</td>";
+                               echo "<td>${remote['port']}</td><td>";
+                               echo formatPortIIFOIF (getPortInfo ($socket['remote_id'])) . "</td>";
+                               echo "<td>&nbsp;</td></tr>";
+                               continue 2;
+                       }
+               $local_options = $remote_options = array();
+               foreach ($myports[$local_port] as $port)
+                       $local_options[$port['id']] = formatPortIIFOIF ($port);
+               foreach ($remote_port_ids as $remote_port_id)
+                       $remote_options[$remote_port_id] = formatPortIIFOIF (getPortInfo ($remote_port_id));
+               echo "<tr class=trwarning><td>${local_port}</td><td>";
+               printSelect ($local_options, array ('name' => "pid2_${inputno}"));
+               echo "</td><td>${remote['device']}</td>";
+               echo "<td>${remote['port']}</td><td>";
+               printSelect ($remote_options, array ('name' => "pid2_${inputno}"));
+               echo "</td><td><input type=checkbox name=do_${inputno}></td></tr>";
+               $inputno++;
+       }
+       if ($inputno)
+       {
+               echo "<input type=hidden name=nports value=${inputno}>";
+               echo '<tr><td colspan=6 align=center>' . getImageHREF ('CREATE', 'import selected', TRUE) . '</td></tr>';
+       }
+       echo '</table></form>';
+}
+
 ?>
index f583bcb..7799b60 100644 (file)
@@ -84,6 +84,7 @@ $tab['object']['ports'] = 'Ports';
 $tab['object']['ipv4'] = 'IPv4';
 $tab['object']['nat4'] = 'NATv4';
 $tab['object']['livevlans'] = 'Live VLANs';
+$tab['object']['livecdp'] = '[Live CDP]';
 $tab['object']['snmpportfinder'] = 'SNMP sync';
 $tab['object']['editrspvs'] = 'RS pools';
 $tab['object']['lvsconfig'] = 'keepalived.conf';
@@ -100,6 +101,7 @@ $tabhandler['object']['ports'] = 'renderPortsForObject';
 $tabhandler['object']['ipv4'] = 'renderIPv4ForObject';
 $tabhandler['object']['nat4'] = 'renderNATv4ForObject';
 $tabhandler['object']['livevlans'] = 'renderVLANMembership';
+$tabhandler['object']['livecdp'] = 'renderLiveCDP';
 $tabhandler['object']['snmpportfinder'] = 'renderSNMPPortFinder';
 $tabhandler['object']['lvsconfig'] = 'renderLVSConfig';
 $tabhandler['object']['autoports'] = 'renderAutoPortsForm';
@@ -113,6 +115,7 @@ $trigger['object']['rackspace'] = 'trigger_rackspace';
 $trigger['object']['ipv4'] = 'trigger_ipv4';
 $trigger['object']['nat4'] = 'trigger_natv4';
 $trigger['object']['livevlans'] = 'trigger_livevlans';
+$trigger['object']['livecdp'] = 'trigger_LiveCDP';
 $trigger['object']['snmpportfinder'] = 'trigger_snmpportfinder';
 $trigger['object']['editrspvs'] = 'trigger_isloadbalancer';
 $trigger['object']['lvsconfig'] = 'trigger_isloadbalancer';
@@ -139,6 +142,7 @@ $ophandler['object']['nat4']['addNATv4Rule'] = 'addPortForwarding';
 $ophandler['object']['nat4']['delNATv4Rule'] = 'delPortForwarding';
 $ophandler['object']['nat4']['updNATv4Rule'] = 'updPortForwarding';
 $ophandler['object']['livevlans']['setPortVLAN'] = 'setPortVLAN';
+$ophandler['object']['livecdp']['importCDPData'] = 'importCDPData';
 $ophandler['object']['autoports']['generate'] = 'generateAutoPorts';
 $ophandler['object']['tags']['saveTags'] = 'saveEntityTags';
 $ophandler['object']['files']['addFile'] = 'addFileToEntity';
index dbe75c2..60ee1d9 100644 (file)
@@ -333,8 +333,8 @@ http://www.cisco.com/en/US/products/hw/routers/ps274/products_tech_note09186a008
        $added_count = $updated_count = $error_count = 0;
        foreach ($ports as $port)
        {
-               $port_id = getPortID ($object_id, $port['name']);
-               if ($port_id === NULL)
+               $port_ids = getPortIDs ($object_id, $port['name']);
+               if (!count ($port_ids))
                {
                        $result = commitAddPort ($object_id, $port['name'], $port_type, $port['label'], $port['l2address']);
                        if ($result == '')
@@ -342,9 +342,9 @@ http://www.cisco.com/en/US/products/hw/routers/ps274/products_tech_note09186a008
                        else
                                $error_count++;
                }
-               else
+               elseif (count ($port_ids) == 1) // update only single-socket ports
                {
-                       $result = commitUpdatePort ($object_id, $port_id, $port['name'], $port_type, $port['label'], $port['l2address']);
+                       $result = commitUpdatePort ($object_id, $port_ids[0], $port['name'], $port_type, $port['label'], $port['l2address']);
                        if ($result == '')
                                $updated_count++;
                        else
index 7dae1bb..165c4b3 100644 (file)
@@ -223,4 +223,9 @@ function trigger_object_8021qsync ()
        return $vswitch['out_of_sync'] == 'yes' ? 'attn' : 'std';
 }
 
+function trigger_LiveCDP ()
+{
+       return 'ios12' == detectDeviceBreed ($_REQUEST['object_id']) ? 'std' : '';
+}
+
 ?>