r4006 802.1Q: Recalculate downlinks/uplinks button. Used in cases when due to some...
authorAlexey Andriyanov <alan@al-an.info>
Fri, 19 Nov 2010 16:57:16 +0000 (16:57 +0000)
committerAlexey Andriyanov <alan@al-an.info>
Fri, 19 Nov 2010 16:57:16 +0000 (16:57 +0000)
(order change, link add) port vlan configs are not consistent.

inc/interface.php:
 renderObject8021QPorts: new button added under port list

inc/functions.php:
 recalc8021QPorts: new function to recalculate uplinks and downlinks on the specified switch,
                   and also its corresponding remote ports. Can be useful in script mode.
 produceDownlinkPort: new function to duplicate remote side uplink port config as local side downlink. Used by recalc8021QPorts.
 queueChangesToSwitch: updates D-config and mutex_rev if new port config differs from the old one.

ChangeLog
inc/functions.php
inc/interface.php
inc/navigation.php
inc/ophandlers.php
pi.css
pix/tango-view-refresh-32x32.png [new file with mode: 0644]

index bca18ce..5e083f6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,7 @@
        update: SNMP data for FastIron LS (#357)
        update: upon deleting 802.1Q order record focus SELECTs on the deleted values
        bugfix: SQL error issue on some versions of PHP introduced in 0.18.5
+       new feature: 802.1Q: ability to recalculate the switch uplinks and downlinks by pressing a button on 8021q ports page
 0.18.5 2010-10-25
        bugfix: attribute map editor was broken (#353)
        bugfix: speed up IPv4 VLAN selector (by Boris Lytochkin)
index 200c61c..7400869 100644 (file)
@@ -3512,6 +3512,162 @@ function initiateUplinksReverb ($object_id, $uplink_ports)
                saveDownlinksReverb ($remote_object_id, $remote_ports);
 }
 
+// checks if the desired config of all uplink/downlink ports of that switch, and
+// his neighbors, equals to the recalculated config. If not, and $check_only is FALSE,
+// sets the recalculated configs as desired and puts switches into out-of-sync state.
+// Returns an array with object_id as key and portname subkey
+function recalc8021QPorts ($switch_id, $check_only = FALSE)
+{
+       function find_connected_portinfo ($ports, $name)
+       {
+               foreach ($ports as $portinfo)
+                       if ($portinfo['name'] == $name and $portinfo['remote_object_id'] != '' and $portinfo['remote_name'] != '')
+                               return $portinfo;
+       }
+
+       $ret = array
+       (
+               'switches' => 0,
+               'ports' => 0,
+       );
+       global $dbxlink;
+
+       $object = spotEntity ('object', $switch_id);
+       amplifyCell ($object);
+       $vlan_config = getStored8021QConfig ($switch_id, 'desired');
+       $vswitch = getVLANSwitchInfo ($switch_id);
+       if (! $vswitch) {
+               return $ret;
+       }
+       $domain_vlanlist = getDomainVLANs ($vswitch['domain_id']);
+       $order = apply8021QOrder ($vswitch['template_id'], $vlan_config);
+       $before = $order;
+
+       $dbxlink->beginTransaction();
+       // calculate remote uplinks and copy them to local downlinks
+       foreach ($order as $pn => &$local_port_order)
+       {
+               if ($local_port_order['vst_role'] != 'downlink')
+                       continue;
+
+               // if there is a link with remote side type 'uplink', use its vlan mask
+               if ($portinfo = find_connected_portinfo ($object['ports'], $pn))
+               {
+                       $remote_pn = $portinfo['remote_name'];
+                       $remote_vlan_config = getStored8021QConfig ($portinfo['remote_object_id'], 'desired');
+                       $remote_vswitch = getVLANSwitchInfo ($portinfo['remote_object_id']);
+                       if (! $remote_vswitch)
+                               continue;
+                       $remote_domain_vlanlist = getDomainVLANs ($remote_vswitch['domain_id']);
+                       $remote_order = apply8021QOrder ($remote_vswitch['template_id'], $remote_vlan_config);
+                       $remote_before = $remote_order;
+                       if ($remote_order[$remote_pn]['vst_role'] == 'uplink')
+                       {
+                               $remote_uplinks = filter8021QChangeRequests ($remote_domain_vlanlist, $remote_before, produceUplinkPorts ($remote_domain_vlanlist, $remote_order));
+                               $remote_port_order = $remote_uplinks[$remote_pn];
+                               $new_order = produceDownlinkPort ($domain_vlanlist, $pn, array ($pn => $local_port_order), $remote_port_order);
+                               $local_port_order = $new_order[$pn]; // this updates $order
+
+                               // queue changes in D-config of remote switch
+                               if ($changed = queueChangesToSwitch ($portinfo['remote_object_id'], array ($remote_pn => $remote_port_order), $remote_before, $check_only))
+                               {
+                                       $ret['switches'] ++;
+                                       $ret['ports'] += $changed;
+                               }
+                       }
+               }
+       }
+
+       // calculate local uplinks, store changes in $order
+       foreach (filter8021QChangeRequests ($domain_vlanlist, $before, produceUplinkPorts ($domain_vlanlist, $order)) as $pn => $portorder)
+               $order[$pn] = $portorder;
+       // queue changes in D-config of local switch
+       if ($changed = queueChangesToSwitch ($switch_id, $order, $before, $check_only))
+       {
+               $ret['switches'] ++;
+               $ret['ports'] += $changed;
+       }
+
+       // calculate the remote side of local uplinks
+       foreach ($order as $pn => &$local_port_order)
+       {
+               if ($local_port_order['vst_role'] != 'uplink')
+                       continue;
+
+               // if there is a link with remote side type 'downlink', replace its vlan mask
+               if ($portinfo = find_connected_portinfo ($object['ports'], $pn))
+               {
+                       $remote_pn = $portinfo['remote_name'];
+                       $remote_vlan_config = getStored8021QConfig ($portinfo['remote_object_id'], 'desired');
+                       $remote_vswitch = getVLANSwitchInfo ($portinfo['remote_object_id']);
+                       if (! $remote_vswitch)
+                               continue;
+                       $remote_domain_vlanlist = getDomainVLANs ($remote_vswitch['domain_id']);
+                       $remote_order = apply8021QOrder ($remote_vswitch['template_id'], $remote_vlan_config);
+                       $remote_before = $remote_order;
+                       if ($remote_order[$remote_pn]['vst_role'] == 'downlink')
+                       {
+                               $new_order = produceDownlinkPort ($remote_domain_vlanlist, $remote_pn, $remote_order, $local_port_order);
+                               // queue changes in D-config of remote switch
+                               if ($changed = queueChangesToSwitch ($portinfo['remote_object_id'], $new_order, $remote_before, $check_only))
+                               {
+                                       $ret['switches'] ++;
+                                       $ret['ports'] += $changed;
+                               }
+                       }
+               }
+       }
+       $dbxlink->commit();
+       return $ret;
+}
+
+// This function takes 802.1q order and the order of corresponding remote uplink port.
+// It returns assotiative array with single row. Key = $portname, value - produced port
+// order based on $order, and having vlan list replaced based on $uplink_order, but filtered.
+function produceDownlinkPort ($domain_vlanlist, $portname, $order, $uplink_order)
+{
+       $new_order = array ($portname => $order[$portname]);
+       $new_order[$portname]['mode'] = 'trunk';
+       $new_order[$portname]['allowed'] = array();
+       $new_order[$portname]['native'] = 0;
+       foreach ($uplink_order['allowed'] as $vlan_id)
+       {
+               if (matchVLANFilter ($vlan_id, $new_order[$portname]['wrt_vlans']))
+               $new_order[$portname]['allowed'][] = $vlan_id;  
+       }
+       return filter8021QChangeRequests ($domain_vlanlist, $remote_before, $new_order);
+}
+
+// does upd8021QPort on any port from $order array which is not equal to the corresponding $before port.
+// returns changed port count.
+// If $check_only is TRUE, return port count that could be changed unless $check_only, does nothing to DB.
+function queueChangesToSwitch ($switch_id, $order, $before, $check_only = FALSE)
+{
+       global $script_mode;
+       $ret = array();
+       $nsaved = 0;
+       foreach ($order as $portname => $portorder)
+               if (! same8021QConfigs ($portorder, $before[$portname]))
+               {
+                       if ($script_mode)
+                       {
+                               $object = spotEntity ('object', $switch_id);
+                               print $object['name'] . " $portname: " . serializeVLANPack ($before[$portname]) . ' -> ' . serializeVLANPack ($portorder) . "\n";
+                               $nsaved++;
+                       }
+                       if (! $check_only)
+                               $nsaved += upd8021QPort ('desired', $switch_id, $portname, $portorder);
+               }
+       
+       if (! $check_only && $nsaved)
+               usePreparedExecuteBlade
+               (
+                       'UPDATE VLANSwitch SET mutex_rev=mutex_rev+1, last_change=NOW(), out_of_sync="yes" WHERE object_id=?',
+                       array ($switch_id)
+               );
+       return $nsaved;
+}
+
 function detectVLANSwitchQueue ($vswitch)
 {
        if ($vswitch['out_of_sync'] == 'no')
index 4306d2e..588a765 100644 (file)
@@ -175,6 +175,9 @@ $image['COMMIT']['height'] = 32;
 $image['COMMIT gray']['path'] = 'pix/tango-go-prev-next-gray-32x32.png';
 $image['COMMIT gray']['width'] = 32;
 $image['COMMIT gray']['height'] = 32;
+$image['RECALC']['path'] = 'pix/tango-view-refresh-32x32.png';
+$image['RECALC']['width'] = 32;
+$image['RECALC']['height'] = 32;
 $image['clear']['path'] = 'pix/tango-edit-clear.png';
 $image['clear']['width'] = 16;
 $image['clear']['height'] = 16;
@@ -1745,6 +1748,7 @@ function showMessageOrError ()
                                84 => array ('code' => 'success', 'format' => 'IPv6 prefix successfully added'),
                                85 => array ('code' => 'success', 'format' => 'IPv6 prefix deleted'),
                                86 => array ('code' => 'success', 'format' => 'IPv6 prefix updated'),
+                               87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d ports changed on %d switches'),
 // records 100~199 with fatal error messages
                                100 => array ('code' => 'error', 'format' => '%s'),
                                101 => array ('code' => 'error', 'format' => 'Port name cannot be empty'),
@@ -7909,10 +7913,16 @@ function renderObject8021QPorts ($object_id)
                        }
        }
        if ($req_port_name == '' and $nports)
-               echo "<input type=hidden name=nports value=${nports}>" .
-                       '<tr><td colspan=5 class=tdcenter>' .
-                       getImageHREF ('SAVE', 'save configuration', TRUE) .
-                       '</td></tr></form>';
+       {
+               echo "<input type=hidden name=nports value=${nports}>";
+               echo '<tr><td colspan=5 class=tdcenter><ul class="btns-8021q-sync">';
+               echo '<li>' . getImageHREF ('SAVE', 'save configuration', TRUE, 100) . '</form></li>';
+
+               echo '<li>';
+               printOpFormIntro ('exec8021QRecalc');
+               echo getImageHREF ('RECALC', 'Recalculate uplinks and downlinks', TRUE, 101) .
+                       '</form></li></ul></td></tr>';
+       }
        echo '</table>';
        if ($req_port_name == '');
                echo '</form>';
index 0525a15..ba0a0da 100644 (file)
@@ -177,6 +177,7 @@ $ophandler['object']['snmpportfinder']['querySNMPData'] = 'querySNMPData';
 $ophandler['object']['8021qorder']['add'] = 'add8021QOrder';
 $ophandler['object']['8021qorder']['del'] = 'del8021QOrder';
 $ophandler['object']['8021qports']['save8021QConfig'] = 'save8021QPorts';
+$ophandler['object']['8021qports']['exec8021QRecalc'] = 'process8021QRecalcRequest';
 $ophandler['object']['8021qsync']['exec8021QPull'] = 'process8021QSyncRequest';
 $ophandler['object']['8021qsync']['exec8021QPush'] = 'process8021QSyncRequest';
 $ophandler['object']['8021qsync']['resolve8021QConflicts'] = 'resolve8021QConflicts';
index 746aeb8..c301aa8 100644 (file)
@@ -2531,6 +2531,19 @@ function process8021QSyncRequest ()
        return buildRedirectURL (__FUNCTION__, 'OK', array ($done));
 }
 
+$msgcode['process8021QRecalcRequest']['CHANGED'] = 87;
+$msgcode['process8021QRecalcRequest']['NO_CHANGES'] = 300;
+function process8021QRecalcRequest ()
+{
+       assertUIntArg ('object_id');
+       global $sic;
+       $counters = recalc8021QPorts ($sic['object_id']);
+       if ($counters['ports'])
+               return buildRedirectURL (__FUNCTION__, 'CHANGED', array ($counters['ports'], $counters['switches']));
+       else
+               return buildRedirectURL (__FUNCTION__, 'NO_CHANGES', array ('No changes were made'));
+}
+
 $msgcode['resolve8021QConflicts']['OK'] = 63;
 $msgcode['resolve8021QConflicts']['ERR1'] = 179;
 $msgcode['resolve8021QConflicts']['ERR2'] = 109;
diff --git a/pi.css b/pi.css
index dee4bb9..5d17425 100644 (file)
--- a/pi.css
+++ b/pi.css
@@ -528,6 +528,15 @@ a.toggleTreeMode {
 }
 
 .net-usage {
-               float: right;
-               margin-left: 15px;
+       float: right;
+       margin-left: 15px;
+}
+
+.btns-8021q-sync li {
+       display: inline;
+       list-style-type: none;
+       padding-right: 40px;
+}
+.btns-8021q-sync li form {
+       display: inline;
 }
diff --git a/pix/tango-view-refresh-32x32.png b/pix/tango-view-refresh-32x32.png
new file mode 100644 (file)
index 0000000..606ea9e
Binary files /dev/null and b/pix/tango-view-refresh-32x32.png differ