r4352 LiveCDP, LiveLLDP, etc: ability to install tranceivers into ports in-place...
authorAlexey Andriyanov <alan@al-an.info>
Thu, 17 Mar 2011 13:35:51 +0000 (13:35 +0000)
committerAlexey Andriyanov <alan@al-an.info>
Thu, 17 Mar 2011 13:35:51 +0000 (13:35 +0000)
modified functions:
importDPData: does the tranceiver installation by calling commitUpdatePortOIF. Uses DB transactions
renderDiscoveredNeighbors: calculates variant list and presents it to the user. Toggle checkbox added
searchByMgmtHostname: now it matches not only full FQDN, but also its part, allowing to not having object names like hostnames. Uses new DB indexes
usePreparedUpdateBlade: function prototype ne wis identical to siblings: it is returning the updated rows count, or FALSE
getSelect: new param $treat_single_special. If it is true, and the option list contains single value, hidden input returned instead of select

new functions:
getPortTypeUsageStatistics: fetches the links count grouped by port type
formatIfTypeVariants: formats variant list into the HTML selecbox
isTranceiverEmpty: new func with self-explaining name
commitUpdatePortOIF: DB function to change port type

new indices in table AttributeValue used to search objects by attribute values

ChangeLog
wwwroot/inc/database.php
wwwroot/inc/interface-lib.php
wwwroot/inc/interface.php
wwwroot/inc/ophandlers.php
wwwroot/inc/upgrade.php

index 52b521a53209bce02e60edc4d4a45f5a2e6b6b8f..92153b4b0623c61f29845c1b3d90f0888b1b0877 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,7 @@
        update: new object types: power supply chassis, power supply (#409)
        new feature: Cisco NX-OS v4, v5 LLDP gateway
        bugfix: VRP linkstatus gateway now properly handles port-channels
+       update: LiveCDP, LiveLLDP, etc: ability to install tranceivers into ports in-place, while linking the ports
 0.19.1 2011-02-22
        update: UI: rack lists are now reduced by common tags with object on Rackspace tab (FILTER_RACKLIST_BY_TAGS config var)
        update: 802.1q: If switch has IP interface in some VLAN linked to IP subnet, this VLAN is not pruned from switch's uplink
index 8216450be0df211c8c0c0e5dbd1f72fc7eec80e6..9be3992f404223eb027e86ec7133a4ab9e93f80c 100644 (file)
@@ -1139,6 +1139,16 @@ function commitUpdatePort ($object_id, $port_id, $port_name, $port_type_id, $por
        $dbxlink->exec ('UNLOCK TABLES');
 }
 
+function commitUpdatePortOIF ($port_id, $port_type_id)
+{
+       return usePreparedUpdateBlade
+       (
+               'Port',
+               array ('type' => $port_type_id),
+               array ('id' => $port_id)
+       );
+}
+
 function getAllIPv4Allocations ()
 {
        $result = usePreparedSelectBlade
@@ -2122,6 +2132,13 @@ 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 $rows[0][0];
+       unset ($result);
+
+       // second attempt: search for FQDN part, separated by dot.
+       $result = usePreparedSelectBlade ('SELECT object_id FROM AttributeValue WHERE attr_id = 3 AND string_value LIKE ? LIMIT 2', array ("$string.%"));
+       $rows = $result->fetchAll (PDO::FETCH_NUM);
        if (count ($rows) != 1)
                return NULL;
        return $rows[0][0];
@@ -2690,7 +2707,9 @@ function usePreparedUpdateBlade ($tablename, $set_columns, $where_columns, $conj
        try
        {
                $prepared = $dbxlink->prepare ($query);
-               $prepared->execute (array_merge (array_values ($set_columns), array_values ($where_columns)));
+               if (! $prepared->execute (array_merge (array_values ($set_columns), array_values ($where_columns))))
+                       return FALSE;
+               return $prepared->rowCount();
        }
        catch (PDOException $e)
        {
@@ -3745,6 +3764,19 @@ function getExistingPortTypeOptions ($port_id)
        return $ret;
 }
 
+function getPortTypeUsageStatistics()
+{
+       $result = usePreparedSelectBlade
+       (
+               'SELECT p.type, COUNT(p.id) AS count FROM Port p INNER JOIN Link l '.
+               'ON (p.id = l.porta or p.id = l.portb) WHERE p.type <> 0 GROUP BY type'
+       );
+       $ret = array();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               $ret[$row['type']] = $row['count'];
+       return $ret;
+}
+
 function getPortIIFOptions()
 {
        $ret = array();
index 065f5d1eb22bea6faa955dbc4cfe21de9e262671..71f0f9c7a37eaf41b1c1faa0d1150dfcfe4ee20a 100644 (file)
@@ -264,7 +264,7 @@ function printSelect ($optionList, $select_attrs = array(), $selected_id = NULL)
 }
 
 // Input array keys are OPTION VALUEs and input array values are OPTION text.
-function getSelect ($optionList, $select_attrs = array(), $selected_id = NULL)
+function getSelect ($optionList, $select_attrs = array(), $selected_id = NULL, $treat_single_special = TRUE)
 {
        $ret = '';
        if (!array_key_exists ('name', $select_attrs))
@@ -272,7 +272,7 @@ function getSelect ($optionList, $select_attrs = array(), $selected_id = NULL)
        // handle two corner cases in a specific way
        if (count ($optionList) == 0)
                return '(none)';
-       if (count ($optionList) == 1)
+       if (count ($optionList) == 1 && $treat_single_special)
        {
                foreach ($optionList as $key => $value) { break; }
                return "<input type=hidden name=${select_attrs['name']} id=${select_attrs['name']} value=${key}>" . $value;
index e0f959ea777adca0e3be08b000e4b617666ce58c..b650a0445ef70d32eed1da985ea332a72adc4fdf 100644 (file)
@@ -8686,6 +8686,8 @@ function renderDeployQueue()
 function renderDiscoveredNeighbors ($object_id)
 {
        global $tabno;
+       static $POIFC;
+       
        $opcode_by_tabno = array
        (
                'livecdp' => 'getcdpstatus',
@@ -8722,7 +8724,7 @@ function renderDiscoveredNeighbors ($object_id)
        switchportInfoJS($object_id); // load JS code to make portnames interactive
        printOpFormIntro ('importDPData');
        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>';
+       echo '<tr><th colspan=2>local port</th><th></th><th>remote device</th><th colspan=2>remote port</th><th><input type="checkbox" id="cb-toggle"></th></tr>';
        $inputno = 0;
        foreach ($neighbors as $local_port => $remote_list)
        {
@@ -8735,6 +8737,7 @@ function renderDiscoveredNeighbors ($object_id)
                        $link_matches = FALSE;
                        $portinfo_local = NULL;
                        $portinfo_remote = NULL;
+                       $variants = array();
 
                        do { // once-cyle fake loop used only to break out of it
                                if (! empty($local_ports))
@@ -8815,15 +8818,34 @@ function renderDiscoveredNeighbors ($object_id)
                                        }
 
                                // no links found on both sides, search for a compatible port pair
-                               $POIFC = getPortOIFCompat();
-                               foreach ($local_ports as $portinfo_local)
-                                       foreach ($remote_ports as $portinfo_remote)
-                                               foreach ($POIFC as $item)
-                                                       if ($item['type1'] == $portinfo_local['oif_id'] and $item['type2'] == $portinfo_remote['oif_id'])
-                                                               break 4;
-
-                               // no compatible ports found
-                               $error_message = "Incompatible port types";
+                               $left_types = array();
+                               foreach ($local_ports as $portinfo)
+                                       if (! isTranceiverEmpty ($portinfo))
+                                               $left_types[$portinfo['oif_id']] = array ('id' => $portinfo['oif_id'], 'name' => $portinfo['oif_name'], 'portinfo' => $portinfo);
+                                       else
+                                               foreach (getExistingPortTypeOptions ($portinfo['id']) as $oif_id => $oif_name)
+                                                       $left_types[$oif_id] = array ('id' => $oif_id, 'name' => $oif_name, 'portinfo' => $portinfo);
+                               $right_types = array();
+                               foreach ($remote_ports as $portinfo)
+                                       if (! isTranceiverEmpty ($portinfo))
+                                               $right_types[$portinfo['oif_id']] = array ('id' => $portinfo['oif_id'], 'name' => $portinfo['oif_name'], 'portinfo' => $portinfo);
+                                       else
+                                               foreach (getExistingPortTypeOptions ($portinfo['id']) as $oif_id => $oif_name)
+                                                       $right_types[$oif_id] = array ('id' => $oif_id, 'name' => $oif_name, 'portinfo' => $portinfo);
+                               if (! isset ($POIFC))
+                               {
+                                       $POIFC = array();
+                                       foreach (getPortOIFCompat() as $item)
+                                       {
+                                               $POIFC[$item['type1']][$item['type2']] = TRUE;
+                                               $POIFC[$item['type2']][$item['type1']] = TRUE;
+                                       }
+                               }
+                               foreach ($left_types as $left_id => $left) foreach ($right_types as $right_id => $right)
+                                       if (isset ($POIFC[$left_id][$right_id]))
+                                               $variants["${left_id}:${right_id}"] = array ('left' => $left, 'right' => $right);
+                               if (! count ($variants)) // no compatible ports found
+                                       $error_message = "Incompatible port types";
                        } while (FALSE); // do {
 
                        $tr_class = $link_matches ? 'trok' : (isset ($error_message) ? 'trerror' : 'trwarning');
@@ -8843,18 +8865,15 @@ function renderDiscoveredNeighbors ($object_id)
                                        '</td>';
                                $initial_row = FALSE;
                        }
-                       echo "<td>" . ($portinfo_local ?  formatPortIIFOIF($portinfo_local) : '&nbsp') . "</td>";
+                       echo "<td>" . ($portinfo_local ?  formatPortIIFOIF ($portinfo_local) : '&nbsp') . "</td>";
+                       echo "<td>" . formatIfTypeVariants ($variants, "ports_${inputno}") . "</td>";
                        echo "<td>${dp_neighbor['device']}</td>";
                        echo "<td>" . ($portinfo_remote ? formatPortLink ($dp_remote_object_id, NULL, $portinfo_remote['id'], $portinfo_remote['name']) : $dp_neighbor['port'] ) . "</td>";
-                       echo "<td>" . ($portinfo_remote ? formatPortIIFOIF($portinfo_remote) : '&nbsp') . "</td>";
+                       echo "<td>" . ($portinfo_remote ?  formatPortIIFOIF ($portinfo_remote) : '&nbsp') . "</td>";
                        echo "<td>";
-                       if (isset ($error_message) || $link_matches)
-                               echo '&nbsp;';
-                       else
+                       if (! empty ($variants))
                        {
-                               echo "<input type=hidden name=\"pid1_${inputno}\" value=\"{$portinfo_local['id']}\">";
-                               echo "<input type=hidden name=\"pid2_${inputno}\" value=\"{$portinfo_remote['id']}\">";
-                               echo "<input type=checkbox name=do_${inputno}>";
+                               echo "<input type=checkbox name=do_${inputno} class='cb-makelink'>";
                                $inputno++;
                        }
                        echo "</td>";
@@ -8870,6 +8889,91 @@ function renderDiscoveredNeighbors ($object_id)
                echo '<tr><td colspan=6 align=center>' . getImageHREF ('CREATE', 'import selected', TRUE) . '</td></tr>';
        }
        echo '</table></form>';
+
+       addJS (<<<END
+$(document).ready(function () {
+       $('#cb-toggle').click(function (event) {
+               var list = $('.cb-makelink');
+               for (var i in list) {
+                       var cb = list[i];
+                       cb.checked = event.target.checked;
+               }
+       });
+});
+END
+               , TRUE
+       );
+}
+
+function formatIfTypeVariants ($variants, $select_name)
+{
+       if (empty ($variants))
+               return;
+       static $tranceivers_hint_shown = FALSE;
+       static $oif_usage_stat = NULL;
+       $select = array();
+       $creting_tranceivers = FALSE;
+       $most_used_count = 0;
+       $selected_key = NULL;
+
+       if (! isset ($oif_usage_stat))
+               $oif_usage_stat = getPortTypeUsageStatistics();
+
+       foreach ($variants as $item)
+       {
+               $params = array
+               (
+                       'a_id' => $item['left']['portinfo']['id'],
+                       'b_id' => $item['right']['portinfo']['id'],
+               );
+               $text = '';
+               if ($item['left']['id'] == $item['right']['id'])
+                       $text = $item['left']['name'];
+               else
+                       $text = $item['left']['name'] . ' | ' . $item['right']['name'];
+               
+               $popularity_count = 0;
+               if (isTranceiverEmpty ($item['left']['portinfo']))
+               {
+                       $creting_tranceivers = TRUE;
+                       $text = '← ' . $text;
+                       $params['a_oif'] = $item['left']['id'];
+                       if (isset ($oif_usage_stat[$item['left']['id']]))
+                               $popularity_count += $oif_usage_stat[$item['left']['id']];
+               }
+               if (isTranceiverEmpty ($item['right']['portinfo']))
+               {
+                       $creting_tranceivers = TRUE;
+                       $text = $text . ' →';
+                       $params['b_oif'] = $item['right']['id'];
+                       if (isset ($oif_usage_stat[$item['right']['id']]))
+                               $popularity_count += $oif_usage_stat[$item['right']['id']];
+               }
+               // a_id:id,a_oif:id,b_id:id,b_oif:id
+               $key = '';
+               foreach ($params as $i => $j)
+                       $key .= ",$i:$j";
+               $key = trim($key, ",");
+               $select[$key] = $text;
+               if ($popularity_count > $most_used_count)
+               {
+                       $most_used_count = $popularity_count;
+                       $selected_key = $key;
+               }
+       }
+
+       if ($creting_tranceivers and ! $tranceivers_hint_shown)
+       {
+               $tranceivers_hint_shown = TRUE;
+               showNotice ('The arrow (← or →) means to create a tranceiver in the suitable port');
+       }
+
+       return getSelect ($select, array('name' => $select_name), $selected_key, !$creting_tranceivers);
+}
+
+function isTranceiverEmpty ($portinfo)
+{
+       return (0 === strpos ($portinfo['oif_name'], 'empty '));
 }
 
 function formatAttributeValue ($record)
index 6be35defbcd56669de7b918de1af7d1df3d45cb9..82516bbf0dcc0e1bef1bc44bedd93388811bc049 100644 (file)
@@ -2695,17 +2695,28 @@ function updVSTRule()
 $msgcode['importDPData']['OK'] = 44;
 function importDPData()
 {
-       global $sic;
+       global $sic, $dbxlink;
        assertUIntArg ('nports');
        $nignored = $ndone = 0;
        $POIFC = getPortOIFCompat();
        for ($i = 0; $i < $sic['nports']; $i++)
                if (array_key_exists ("do_${i}", $sic))
                {
-                       assertUIntArg ("pid1_${i}");
-                       assertUIntArg ("pid2_${i}");
-                       $porta = getPortInfo ($_REQUEST["pid1_${i}"]);
-                       $portb = getPortInfo ($_REQUEST["pid2_${i}"]);
+                       $params = array();
+                       assertStringArg ("ports_${i}");
+                       foreach (explode (',', $_REQUEST["ports_${i}"]) as $item)
+                       {
+                               $pair = explode (':', $item);
+                               if (count ($pair) != 2)
+                                       continue;
+                               $params[$pair[0]] = $pair[1];
+                       }
+                       if (! isset ($params['a_id']) || ! isset ($params['b_id']) ||
+                               ! intval ($params['a_id']) || ! intval ($params['b_id']))
+                               throw new InvalidArgException ("ports_${i}", $_REQUEST["ports_${i}"], "can not unpack port ids");
+                       
+                       $porta = getPortInfo ($params['a_id']);
+                       $portb = getPortInfo ($params['b_id']);
                        if
                        (
                                $porta['linked'] or
@@ -2716,14 +2727,42 @@ function importDPData()
                                $nignored++;
                                continue;
                        }
+                       $oif_a = intval (@$params['a_oif']); // these parameters are optional
+                       $oif_b = intval (@$params['b_oif']);
+                       
+                       $dbxlink->beginTransaction();
+                       if ($oif_a || $oif_b)
+                               try
+                               {
+                                       if ($oif_a)
+                                               if (commitUpdatePortOIF ($params['a_id'], $oif_a))
+                                                       $porta['oif_id'] = $oif_a;
+                                       if ($oif_b)
+                                               if (commitUpdatePortOIF ($params['b_id'], $oif_b))
+                                                       $portb['oif_id'] = $oif_b;
+                               }
+                               catch (RTDatabaseError $e)
+                               {
+                                       $dbxlink->rollBack();
+                                       $nignored++;
+                                       continue;
+                               }
+
                        foreach ($POIFC as $item)
                                if ($item['type1'] == $porta['oif_id'] and $item['type2'] == $portb['oif_id'])
                                {
-                                       linkPorts ($_REQUEST["pid1_${i}"], $_REQUEST["pid2_${i}"]);
-                                       $ndone++;
-                                       continue 2; // next port
+                                       $ret = linkPorts ($params['a_id'], $params['b_id']);
+                                       if (empty ($ret))
+                                       {
+                                               $ndone++;
+                                               $dbxlink->commit();
+                                               continue 2; //next port
+                                       }
+                                       else
+                                               $nignored++;
+                                       break;
                                }
-                       $nignored++;
+                       $dbxlink->rollback();
                }
        return buildRedirectURL (__FUNCTION__, 'OK', array ($nignored, $ndone));
 }
index f7b406fc67330787e2dbc12d728ce1b0ed8b2c50..e59490f0b658026f9220175f9ef5b5ff117ee572 100644 (file)
@@ -1026,6 +1026,8 @@ CREATE TABLE `EntityLink` (
                        $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (1399,1399)";
                        $query[] = "INSERT INTO `PortInterfaceCompat` (`iif_id`, `oif_id`) VALUES (1,1399)";
                        $query[] = "UPDATE Config SET varvalue = CONCAT(varvalue, ' or {\$typeid_1397}') WHERE varname = 'IPV4OBJ_LISTSRC'";
+                       $query[] = "ALTER TABLE AttributeValue ADD KEY `attr_id-uint_value` (`attr_id`,`uint_value`)";
+                       $query[] = "ALTER TABLE AttributeValue ADD KEY `attr_id-string_value` (`attr_id`,`string_value`(12))";
                        $query[] = "UPDATE Config SET varvalue = '0.19.2' WHERE varname = 'DB_VERSION'";
                        break;
                default: