View Issue Details

IDProjectCategoryView StatusLast Update
182RackTablesdefaultpublic2010-11-29 13:23
Reporteruser105Assigned Toinfrastation  
PriorityhighSeveritymajorReproducibilityN/A
Status closedResolutionfixed 
Target Version0.18.0Fixed in Version0.18.0 
Summary182: (not live) VLAN support
DescriptionOriginally suggested by Justin Ellison:

Hi all,

I found support for "Live VLANs", but I want "Dead VLANs" - I don't
want to have the webapp configure my switch for me, I want my webapp
to *document* how the switch is configured. Live VLAN support also
uses Telnet which is not an option for me.

So, I patched in VLAN support. Nothing fancy, just added a column to
Port, added a trigger, and modified some functions here and there. I
even modified upgrade.php and tested that, it works fine. To test
yourselves, apply this patch to trunk, run upgrade.php (or add the
column vlan_id to table port and add the word "Generic Managed Switch
(VLAN Support)" to your network switch dictionary).

No data conversion is needed, any port without a VLAN specified is
VLAN ID 1 by default, which is pretty much what the spec says. I
create a table VLAN to store comments about VLAN's, but got lazy and
didn't implement the interface into it.

To use it after patching, create a network switch object with a
hardware type of "Generic Managed Switch (VLAN Support)", and click on
the Ports tab.

Not for sure if this is the place to post a patch, but here goes. I
would love to see this feature in trunk - any feedback is much
appreciated.

Justin

---

Denis,

See attached patch. It's a patch against trunk that includes the
patch above (with a bugfix), and VLAN support as you outlined. It
works, but it's not pretty (yet).

All the hooks are there, and it's functional, but I'm getting pressure
to move onto other projects, so I have to stop now. Here's what my
code is lacking:

1) Error messaging and trapping
2) Interface for modifying VLAN comments
3) Their is support in the functions for VTP Domains, but I didn't
implement it yet in the form data.

I'm sure it would take you only an hour or so to fix it up to production ready.

Thanks so much for the project, hopefully my work can be beneficial to others!

Justin
TagsNo tags attached.

Activities

2010-02-08 16:29

 

justin-racktables-vlans.patch (36,412 bytes)   
Index: /home/justintime/workspace/RackTables/install/init-structure.sql
===================================================================
--- /home/justintime/workspace/RackTables/install/init-structure.sql	(revision 2235)
+++ /home/justintime/workspace/RackTables/install/init-structure.sql	(working copy)
@@ -10,7 +10,7 @@
 
 CREATE TABLE `Attribute` (
   `attr_id` int(10) unsigned NOT NULL auto_increment,
-  `attr_type` enum('string','uint','float','dict') default NULL,
+  `attr_type` enum('string','uint','float','bool','dict') default NULL,
   `attr_name` char(64) default NULL,
   PRIMARY KEY  (`attr_id`),
   UNIQUE KEY `attr_name` (`attr_name`)
@@ -276,3 +276,22 @@
   PRIMARY KEY  (`user_id`),
   UNIQUE KEY `user_name` (`user_name`)
 ) ENGINE=MyISAM AUTO_INCREMENT=10000;
+
+CREATE TABLE `Vlan` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `vlanid` int(10) unsigned NOT NULL,
+  `vtp_domain` char(255) default NULL,
+  `comment` text,
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM;
+
+CREATE TABLE `PortVlan` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `port_id` int(10) unsigned NOT NULL,
+  `vlan_id` int(10) unsigned NOT NULL,
+  `tagged` tinyint(3) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `port_index` (`port_id`),
+  KEY `vlan_index` (`vlan_id`),
+  KEY `tagged_index` (`tagged`)
+) ENGINE=MyISAM;
\ No newline at end of file
Index: /home/justintime/workspace/RackTables/install/init-dictbase.sql
===================================================================
--- /home/justintime/workspace/RackTables/install/init-dictbase.sql	(revision 2230)
+++ /home/justintime/workspace/RackTables/install/init-dictbase.sql	(working copy)
@@ -16,6 +16,9 @@
 INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (22,'string','HW warranty expiration');
 INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (24,'string','SW warranty expiration');
 INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (25,'string','UUID');
+INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (26,'bool','VLAN Enabled');
+INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (27,'bool','VTP Enabled');
+INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (28,'bool','VTP Domain Name');
 
 INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (4,1,0);
 INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (4,2,11);
@@ -76,6 +79,9 @@
 INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (447, 14, 0);
 INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (447, 22, 0);
 INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (15, 2, 23);
+INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (8, 26, 12);
+INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (8, 27, 12);
+INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (8, 28, 12);
 
 INSERT INTO `Chapter` (`chapter_no`, `sticky`, `chapter_name`) VALUES (11,'no','server models');
 INSERT INTO `Chapter` (`chapter_no`, `sticky`, `chapter_name`) VALUES (12,'no','network switch models');
Index: /home/justintime/workspace/RackTables/inc/navigation.php
===================================================================
--- /home/justintime/workspace/RackTables/inc/navigation.php	(revision 2225)
+++ /home/justintime/workspace/RackTables/inc/navigation.php	(working copy)
@@ -94,6 +94,7 @@
 $tab['object']['ipv4'] = 'IPv4';
 $tab['object']['nat4'] = 'NATv4';
 $tab['object']['livevlans'] = 'Live VLANs';
+$tab['object']['vlans'] = 'VLANs';
 $tab['object']['snmpportfinder'] = 'SNMP port finder';
 $tab['object']['editrspvs'] = 'RS pools';
 $tab['object']['lvsconfig'] = 'LVS config';
@@ -106,6 +107,7 @@
 $tabhandler['object']['ipv4'] = 'renderIPv4ForObject';
 $tabhandler['object']['nat4'] = 'renderNATv4ForObject';
 $tabhandler['object']['livevlans'] = 'renderVLANMembership';
+$tabhandler['object']['vlans'] = 'renderVLANMembership';
 $tabhandler['object']['snmpportfinder'] = 'renderSNMPPortFinder';
 $tabhandler['object']['lvsconfig'] = 'renderLVSConfig';
 $tabhandler['object']['autoports'] = 'renderAutoPortsForm';
@@ -116,6 +118,7 @@
 $trigger['object']['ipv4'] = 'trigger_ipv4';
 $trigger['object']['nat4'] = 'trigger_natv4';
 $trigger['object']['livevlans'] = 'trigger_livevlans';
+$trigger['object']['vlans'] = 'trigger_vlans';
 $trigger['object']['snmpportfinder'] = 'trigger_snmpportfinder';
 $trigger['object']['editrspvs'] = 'trigger_natv4';
 $trigger['object']['lvsconfig'] = 'trigger_lvsconfig';
@@ -138,6 +141,9 @@
 $ophandler['object']['nat4']['delNATv4Rule'] = 'delPortForwarding';
 $ophandler['object']['nat4']['updNATv4Rule'] = 'updPortForwarding';
 $ophandler['object']['livevlans']['setPortVLAN'] = 'setPortVLAN';
+$ophandler['object']['vlans']['editVLANMemberships'] = 'editVLANMembership';
+$ophandler['object']['vlans']['addVLANMembership'] = 'addVLANMembership';
+$ophandler['object']['vlans']['delVLANMembership'] = 'delVLANMembership';
 $ophandler['object']['autoports']['generate'] = 'generateAutoPorts';
 $ophandler['object']['tags']['saveTags'] = 'saveEntityTags';
 $ophandler['object']['editrspvs']['addLB'] = 'addLoadBalancer';
Index: /home/justintime/workspace/RackTables/inc/ophandlers.php
===================================================================
--- /home/justintime/workspace/RackTables/inc/ophandlers.php	(revision 2225)
+++ /home/justintime/workspace/RackTables/inc/ophandlers.php	(working copy)
@@ -166,13 +166,54 @@
 	assertStringArg ('port_name', __FUNCTION__, TRUE);
 	if (empty ($_REQUEST['port_name']))
 		return buildRedirectURL ('ERR1');
-	$error = commitAddPort ($_REQUEST['object_id'], $_REQUEST['port_name'], $_REQUEST['port_type_id'], $_REQUEST['port_label'], $_REQUEST['port_l2address']);
+	$error = commitAddPort ($_REQUEST['object_id'], $_REQUEST['port_name'], $_REQUEST['port_type_id'], $_REQUEST['port_label'], $_REQUEST['port_l2address'],$_REQUEST['port_vlan_id']);
 	if ($error != '')
 		return buildRedirectURL ('ERR2', array ($error));
 	else
 		return buildRedirectURL ('OK', array ($_REQUEST['port_name']));
 }
 
+function addVLANMembership ()
+{
+    $port_id = getPortId($_REQUEST['object_id'],$_REQUEST['port_name']);
+    if (! $port_id > 0) {
+        // Error out here
+        return;
+    }
+    $tagged = ($_REQUEST['port_tagged'] == "on") ? 1 : 0;
+    // Does our VLAN already exist?  If not, create it on the fly.
+    $vlan = getOrCreateVLAN($_REQUEST['port_vlan_id']);
+    $vlan_id = $vlan['Vlan_id'];
+	$error = commitUpdateVLANMembership ($port_id, NULL, $tagged, $vlan_id);
+	if ($error != '')
+		return buildRedirectURL ('ERR2', array ($error));
+	else
+		return buildRedirectURL ('OK', array ($_REQUEST['port_id']));
+}
+
+function delVLANMembership ()
+{
+    if ($_REQUEST['portvlan_id'] > 0)
+    	$error = commitDeleteVLANMembership ($_REQUEST['portvlan_id']);
+	if ($error != '')
+		return buildRedirectURL ('ERR2', array ($error));
+	else
+		return buildRedirectURL ('OK', array ($_REQUEST['port_id']));
+}
+
+function editVLANMembership ()
+{
+    $tagged = ($_REQUEST['port_tagged'] == "on") ? 1 : 0;
+    // Does our VLAN already exist?  If not, create it on the fly.
+    $vlan = getOrCreateVLAN($_REQUEST['vlan_id']);
+    $vlan_id = $vlan['Vlan_id'];
+	$error = commitUpdateVLANMembership ($_REQUEST['port_id'], $_REQUEST['portvlan_id'], $tagged, $vlan_id);
+	if ($error != '')
+		return buildRedirectURL ('ERR2', array ($error));
+	else
+		return buildRedirectURL ('OK', array ($_REQUEST['port_id']));
+}
+
 function editPortForObject ()
 {
 	assertUIntArg ('port_id', __FUNCTION__);
@@ -725,6 +766,7 @@
 		switch ($oldvalues[$attr_id]['type'])
 		{
 			case 'uint':
+			case 'bool':
 			case 'float':
 			case 'string':
 				$oldvalue = $oldvalues[$attr_id]['value'];
Index: /home/justintime/workspace/RackTables/inc/interface.php
===================================================================
--- /home/justintime/workspace/RackTables/inc/interface.php	(revision 2235)
+++ /home/justintime/workspace/RackTables/inc/interface.php	(working copy)
@@ -457,6 +457,22 @@
 	echo '<td class=pcright>';
 	startPortlet ('Optional attributes');
 	$values = getAttrValues ($object_id);
+    $i = 0;
+	foreach ($values as $record)
+	{
+        $triggerFunctionName = "trigger_attribute_";
+        $triggerFunctionName .= strtolower(str_replace(" ","_",$record['name']));
+        if (function_exists($triggerFunctionName)) {
+            echo "Calling $triggerFunctionName<br>";
+            if (! call_user_func($triggerFunctionName)) {
+                echo "Removing ${record['name']} from the array.<br>";
+                array_splice($values,$i,1);
+                // Since we removed the element, we cannot increment $i
+                continue;
+            }
+        }
+        $i++;
+    }
 	echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
 	echo "<tr><th>&nbsp;</th><th>Attribute</th><th>Value</th><th>&nbsp;</th></tr>\n";
 	printOpFormIntro ('updateStickers');
@@ -467,7 +483,7 @@
 	{
 		echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
 		echo '<tr><td>';
-		if (!empty ($record['value']))
+		if (!empty ($record['value']) && $record['type'] != "bool")
 		{
 			echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=clearSticker&object_id=${object_id}&attr_id=${record['id']}'>";
 			printImageHREF ('clear', 'Clear value');
@@ -484,6 +500,12 @@
 			case 'string':
 				echo "<input type=text name=${i}_value value='${record['value']}'>";
 				break;
+			case 'bool':
+				echo "<input type=checkbox name=${i}_value value=1";
+                if ($record['value'] > 0) 
+                    echo " checked=\"checked\"";
+                echo ">";
+				break;
 			case 'dict':
 				$chapter = readChapter ($record['chapter_name']);
 				$chapter[0] = '-- NOT SET --';
@@ -1036,7 +1058,7 @@
 	{
 		printOpFormIntro ('addPort');
 		echo "<tr><td>";
-		printImageHREF ('add', 'add a port', TRUE, 104);
+		printImageHREF ('add', 'add a port', TRUE, 105);
 		echo "</td><td><input type=text size=8 name=port_name tabindex=100></td>\n";
 		echo "<td><input type=text size=24 name=port_label tabindex=101></td>";
 		$types = getPortTypes();
@@ -1053,7 +1075,7 @@
 		echo "</select></td>";
 		echo "<td><input type=text name=port_l2address tabindex=103></td>\n";
 		echo "<td colspan=3>&nbsp;</td><td>";
-		printImageHREF ('add', 'add a port', TRUE, 104);
+		printImageHREF ('add', 'add a port', TRUE, 105);
 		echo "</td></tr></form>";
 	}
 	global $root, $pageno, $tabno;
@@ -3492,6 +3514,7 @@
 		echo "</td><td><input type=text name=attr_name></td>";
 		echo '<td><select name=attr_type>';
 		echo '<option value=uint>uint</option>';
+		echo '<option value=bool>bool</option>';
 		echo '<option value=float>float</option>';
 		echo '<option value=string>string</option>';
 		echo '<option value=dict>dict</option>';
@@ -3535,6 +3558,7 @@
 		printImageHREF ('add', '', TRUE);
 		echo "</td><td><select name=attr_id>";
 		$shortType['uint'] = 'U';
+		$shortType['bool'] = 'B';
 		$shortType['float'] = 'F';
 		$shortType['string'] = 'S';
 		$shortType['dict'] = 'D';
@@ -3802,139 +3826,206 @@
 function renderVLANMembership ($object_id = 0)
 {
 	showMessageOrError();
-	$data = getSwitchVLANs ($object_id);
-	if ($data === NULL)
-	{
-		showError ('getSwitchVLANs() returned NULL', __FUNCTION__);
-		return;
-	}
-	list ($vlanlist, $portlist, $maclist) = $data;
-
-	echo '<table border=0 width="100%"><tr><td colspan=3>';
-
-	startPortlet ('Current status');
-	echo "<table class=widetable cellspacing=3 cellpadding=5 align=center width='100%'><tr>";
-	printOpFormIntro ('setPortVLAN');
-	$portcount = count ($portlist);
-	echo "<input type=hidden name=portcount value=" . $portcount . ">\n";
-	$portno = 0;
-	$ports_per_row = getConfigVar ('PORTS_PER_ROW');
-	foreach ($portlist as $port)
-	{
-		// Don't let wide forms break our fancy pages.
-		if ($portno % $ports_per_row == 0)
-		{
-			if ($portno > 0)
-				echo "</tr>\n";
-			echo "<tr><th>" . ($portno + 1) . "-" . ($portno + $ports_per_row > $portcount ? $portcount : $portno + $ports_per_row) . "</th>";
-		}
-		echo '<td class=port_';
-		if ($port['status'] == 'notconnect')
-			echo 'notconnect';
-		elseif ($port['status'] == 'disabled')
-			echo 'disabled';
-		elseif ($port['status'] != 'connected')
-			echo 'unknown';
-		elseif (!isset ($maclist[$port['portname']]))
-			echo 'connected_none';
-		else
-		{
-			$maccount = 0;
-			foreach ($maclist[$port['portname']] as $vlanid => $addrs)
-				$maccount += count ($addrs);
-			if ($maccount == 1)
-				echo 'connected_single';
-			else
-				echo 'connected_multi';
-		}
-		echo '>' . $port['portname'] . '<br>';
-		echo "<input type=hidden name=portname_${portno} value=" . $port['portname'] . '>';
-		if ($port['vlanid'] == 'trunk')
-		{
-			echo "<input type=hidden name=vlanid_${portno} value='trunk'>";
-			echo "<select disabled multiple='multiple' size=1><option>TRUNK</option></select>";
-		}
-		elseif ($port['vlanid'] == 'routed')
-		{
-			echo "<input type=hidden name=vlanid_${portno} value='routed'>";
-			echo "<select disabled multiple='multiple' size=1><option>ROUTED</option></select>";
-		}
-		else
-		{
-			echo "<select name=vlanid_${portno}>";
-			// A port may belong to a VLAN, which is absent from the VLAN table, this is normal.
-			// We must be able to render its SELECT properly at least.
-			$in_table = FALSE;
-			foreach ($vlanlist as $v => $d)
-			{
-				echo "<option value=${v}";
-				if ($v == $port['vlanid'])
-				{
-					echo ' selected';
-					$in_table = TRUE;
-				}
-				echo ">${v}</option>\n";
-			}
-			if (!$in_table)
-				echo "<option value=${port['vlanid']} selected>${port['vlanid']}</option>\n";
-			echo "</select>";
-		}
-		$portno++;
-		echo "</td>";
-	}
-	echo "</tr><tr><td colspan=" . ($ports_per_row + 1) . "><input type=submit value='Save changes'></form></td></tr></table>";
-	finishPortlet();
-
-	echo '</td></tr><tr><td class=pcleft>';
-	startPortlet ('VLAN table');
-	echo '<table class=cooltable cellspacing=0 cellpadding=5 align=center width="100%">';
-	echo "<tr><th>ID</th><th>Description</th></tr>";
-	$order = 'even';
-	global $nextorder;
-	foreach ($vlanlist as $id => $descr)
-	{
-		echo "<tr class=row_${order}><td class=tdright>${id}</td><td class=tdleft>${descr}</td></tr>";
-		$order = $nextorder[$order];
-	}
-	echo '</table>';
-	finishPortlet();
-
-	echo '</td><td class=pcright>';
-
-	startPortlet ('Color legend');
-	echo '<table>';
-	echo "<tr><th>port state</th><th>color code</th></tr>";
-	echo "<tr><td>not connected</td><td class=port_notconnect>SAMPLE</td></tr>";
-	echo "<tr><td>disabled</td><td class=port_disabled>SAMPLE</td></tr>";
-	echo "<tr><td>unknown</td><td class=port_unknown>SAMPLE</td></tr>";
-	echo "<tr><td>connected with none MAC addresses active</td><td class=port_connected_none>SAMPLE</td></tr>";
-	echo "<tr><td>connected with 1 MAC addresses active</td><td class=port_connected_single>SAMPLE</td></tr>";
-	echo "<tr><td>connected with 1+ MAC addresses active</td><td class=port_connected_multi>SAMPLE</td></tr>";
-	echo '</table>';
-	finishPortlet();
-
-	echo '</td><td class=pcright>';
-
-	if (count ($maclist))
-	{
-		startPortlet ('MAC address table');
-		echo '<table border=0 class=cooltable align=center cellspacing=0 cellpadding=5>';
-		echo "<tr><th>Port</th><th>VLAN ID</th><th>MAC address</th></tr>\n";
-		$order = 'even';
-		foreach ($maclist as $portname => $portdata)
-			foreach ($portdata as $vlanid => $addrgroup)
-				foreach ($addrgroup as $addr)
-				{
-					echo "<tr class=row_${order}><td class=tdleft>$portname</td><td class=tdleft>$vlanid</td>";
-					echo "<td class=tdleft>$addr</td></tr>\n";
-					$order = $nextorder[$order];
-				}
-		echo '</table>';
-		finishPortlet();
-	}
-
-	// End of main table.
-	echo '</td></tr></table>';
+    if (trigger_livevlans()) {
+        $data = getSwitchVLANs ($object_id);
+    	if ($data === NULL)
+    	{
+    		showError ("${function}() returned NULL", __FUNCTION__);
+    		return;
+    	}
+    	list ($vlanlist, $portlist, $maclist) = $data;
+    
+    	echo '<table border=0 width="100%"><tr><td colspan=3>';
+    
+    	startPortlet ('Current status');
+    	echo "<table class=widetable cellspacing=3 cellpadding=5 align=center width='100%'><tr>";
+    	printOpFormIntro ('setPortVLAN');
+    	$portcount = count ($portlist);
+    	echo "<input type=hidden name=portcount value=" . $portcount . ">\n";
+    	$portno = 0;
+    	$ports_per_row = getConfigVar ('PORTS_PER_ROW');
+    	foreach ($portlist as $port)
+    	{
+    		// Don't let wide forms break our fancy pages.
+    		if ($portno % $ports_per_row == 0)
+    		{
+    			if ($portno > 0)
+    				echo "</tr>\n";
+    			echo "<tr><th>" . ($portno + 1) . "-" . ($portno + $ports_per_row > $portcount ? $portcount : $portno + $ports_per_row) . "</th>";
+    		}
+    		echo '<td class=port_';
+    		if ($port['status'] == 'notconnect')
+    			echo 'notconnect';
+    		elseif ($port['status'] == 'disabled')
+    			echo 'disabled';
+    		elseif ($port['status'] != 'connected')
+    			echo 'unknown';
+    		elseif (!isset ($maclist[$port['portname']]))
+    			echo 'connected_none';
+    		else
+    		{
+    			$maccount = 0;
+    			foreach ($maclist[$port['portname']] as $vlanid => $addrs)
+    				$maccount += count ($addrs);
+    			if ($maccount == 1)
+    				echo 'connected_single';
+    			else
+    				echo 'connected_multi';
+    		}
+    		echo '>' . $port['portname'] . '<br>';
+    		echo "<input type=hidden name=portname_${portno} value=" . $port['portname'] . '>';
+    		if ($port['vlanid'] == 'trunk')
+    		{
+    			echo "<input type=hidden name=vlanid_${portno} value='trunk'>";
+    			echo "<select disabled multiple='multiple' size=1><option>TRUNK</option></select>";
+    		}
+    		elseif ($port['vlanid'] == 'routed')
+    		{
+    			echo "<input type=hidden name=vlanid_${portno} value='routed'>";
+    			echo "<select disabled multiple='multiple' size=1><option>ROUTED</option></select>";
+    		}
+    		else
+    		{
+    			echo "<select name=vlanid_${portno}>";
+    			// A port may belong to a VLAN, which is absent from the VLAN table, this is normal.
+    			// We must be able to render its SELECT properly at least.
+    			$in_table = FALSE;
+    			foreach ($vlanlist as $v => $d)
+    			{
+    				echo "<option value=${v}";
+    				if ($v == $port['vlanid'])
+    				{
+    					echo ' selected';
+    					$in_table = TRUE;
+    				}
+    				echo ">${v}</option>\n";
+    			}
+    			if (!$in_table)
+    				echo "<option value=${port['vlanid']} selected>${port['vlanid']}</option>\n";
+    			echo "</select>";
+    		}
+    		$portno++;
+    		echo "</td>";
+    	}
+    	echo "</tr><tr><td colspan=" . ($ports_per_row + 1) . "><input type=submit value='Save changes'></form></td></tr></table>";
+    	finishPortlet();
+    
+    	echo '</td></tr><tr><td class=pcleft>';
+    	startPortlet ('VLAN table');
+    	echo '<table class=cooltable cellspacing=0 cellpadding=5 align=center width="100%">';
+    	echo "<tr><th>ID</th><th>Description</th></tr>";
+    	$order = 'even';
+    	global $nextorder;
+    	foreach ($vlanlist as $id => $descr)
+    	{
+    		echo "<tr class=row_${order}><td class=tdright>${id}</td><td class=tdleft>${descr}</td></tr>";
+    		$order = $nextorder[$order];
+    	}
+    	echo '</table>';
+    	finishPortlet();
+    
+    	echo '</td><td class=pcright>';
+    
+    	startPortlet ('Color legend');
+    	echo '<table>';
+    	echo "<tr><th>port state</th><th>color code</th></tr>";
+    	echo "<tr><td>not connected</td><td class=port_notconnect>SAMPLE</td></tr>";
+    	echo "<tr><td>disabled</td><td class=port_disabled>SAMPLE</td></tr>";
+    	echo "<tr><td>unknown</td><td class=port_unknown>SAMPLE</td></tr>";
+    	echo "<tr><td>connected with none MAC addresses active</td><td class=port_connected_none>SAMPLE</td></tr>";
+    	echo "<tr><td>connected with 1 MAC addresses active</td><td class=port_connected_single>SAMPLE</td></tr>";
+    	echo "<tr><td>connected with 1+ MAC addresses active</td><td class=port_connected_multi>SAMPLE</td></tr>";
+    	echo '</table>';
+    	finishPortlet();
+    
+    	echo '</td><td class=pcright>';
+    
+    	if (count ($maclist))
+    	{
+    		startPortlet ('MAC address table');
+    		echo '<table border=0 class=cooltable align=center cellspacing=0 cellpadding=5>';
+    		echo "<tr><th>Port</th><th>VLAN ID</th><th>MAC address</th></tr>\n";
+    		$order = 'even';
+    		foreach ($maclist as $portname => $portdata)
+    			foreach ($portdata as $vlanid => $addrgroup)
+    				foreach ($addrgroup as $addr)
+    				{
+    					echo "<tr class=row_${order}><td class=tdleft>$portname</td><td class=tdleft>$vlanid</td>";
+    					echo "<td class=tdleft>$addr</td></tr>\n";
+    					$order = $nextorder[$order];
+    				}
+    		echo '</table>';
+    		finishPortlet();
+    	}
+    
+    	// End of main table.
+    	echo '</td></tr></table>';
+    }
+    else {
+        // Render database stored VLAN configs
+    	function printNewItemTR ()
+    	{
+    		printOpFormIntro ('addVLANMembership');
+    		echo "<tr><td>";
+    		printImageHREF ('add', 'add a vlan membership', TRUE, 104);
+    		echo "</td><td><input type=text size=8 name=port_name tabindex=100></td>\n";
+    		echo "<td>&nbsp;</td>";
+    		echo "<td><input type=checkbox name='port_tagged' tabindex=102></td>\n";
+            echo "<td><input type=text size=5 name=port_vlan_id tabindex=103 value=1></td>\n<td>";
+    		printImageHREF ('add', 'add a vlan membership', TRUE, 104);
+    		echo "</td></tr></form>";
+    	}
+    	global $root, $pageno, $tabno;
+    	if ($object_id <= 0)
+    	{
+    		showError ('Invalid object_id', __FUNCTION__);
+    		return;
+    	}
+    	showMessageOrError();
+    	startPortlet ('Port VLAN Membership');
+    	$ports = getObjectPortsAndVLANs ($object_id);
+    	usort($ports, 'sortByName');
+    	echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+    	echo "<tr><th>&nbsp;</th><th>Local name</th><th>Visible label</th><th>Tagged?</th><th>VLAN ID</th></tr>\n";
+    	if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+    		printNewItemTR();
+        $lastportid = 0;
+    	foreach ($ports as $port)
+    	{
+    		printOpFormIntro ('editVLANMemberships', array ('port_id' => $port['id'], 'portvlan_id' => $port['portvlan_id']));
+    		echo "<tr><td><a href='${root}process.php?op=delVLANMembership&page=${pageno}&tab=${tabno}&port_id=${port['id']}&portvlan_id=${port['portvlan_id']}&object_id=$object_id&port_name=${port['name']}'>";
+    		printImageHREF ('delete', 'Delete this VLAN Membership');
+    		echo "</a></td>\n";
+            if ($port['id'] == $lastportid) {
+                echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>";
+            }
+            else {
+        		echo "<td>${port['name']}</td>";
+        		echo "<td>${port['label']}</td>";
+        		echo "<td><input type=checkbox name='port_tagged'";
+                if ($port['tagged'] == 1) 
+                  echo " checked";
+                echo "></td>\n";
+            }
+            $lastportid = $port['id'];
+            echo "<td><input type=text size=5 name=vlan_id value='";
+            if ($port['vlan_vlanid'] > 0) {
+                echo $port['vlan_vlanid'];
+            }
+            else {
+                echo "1";
+            }
+            echo "'></td>\n";
+            echo "<td>";
+    		printImageHREF ('save', 'Save changes', TRUE);
+    		echo "</td></form></tr>\n";
+    	}
+    	if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+    		printNewItemTR();
+    	echo "</table><br>\n";
+    	finishPortlet();
+    
+    }
 }
 
 function renderSNMPPortFinder ($object_id = 0)
Index: /home/justintime/workspace/RackTables/inc/database.php
===================================================================
--- /home/justintime/workspace/RackTables/inc/database.php	(revision 2225)
+++ /home/justintime/workspace/RackTables/inc/database.php	(working copy)
@@ -344,6 +344,48 @@
 	return $ret;
 }
 
+function getObjectPortsAndVLANs ($object_id = 0)
+{
+	if ($object_id == 0)
+	{
+		showError ('Invalid object_id', __FUNCTION__);
+		return;
+	}
+    $vtp_domain = 'IS NULL';
+    #TODO vtp_domain support
+	$query =
+		"select Port.id as Port_id, ".
+		"Port.name as Port_name, ".
+		"Port.label as Port_label, ".
+		"PortVlan.id as PortVlan_id, ".
+		"PortVlan.tagged as Port_tagged, ".
+		"Vlan.id as Vlan_id, ".
+		"Vlan.vlanid as Vlan_vlanid, ".
+		"Vlan.comment as Vlan_comment ".
+		"from Port ".
+		"left join PortVlan on Port.id=PortVlan.port_id ".
+		"left join Vlan on PortVlan.vlan_id=Vlan.id ".
+		"where Port.object_id=${object_id} ".
+        "and Vlan.vtp_domain ${vtp_domain} ".
+		"order by Port_name";
+	$result = useSelectBlade ($query, __FUNCTION__);
+	$ret=array();
+	$count=0;
+	while ($row = $result->fetch (PDO::FETCH_ASSOC))
+	{
+		$ret[$count]['id'] = $row['Port_id'];
+		$ret[$count]['name'] = $row['Port_name'];
+		$ret[$count]['label'] = $row['Port_label'];
+		$ret[$count]['tagged'] = ($row['Port_tagged'] == 1) ? TRUE : FALSE;
+		$ret[$count]['portvlan_id'] = $row['PortVlan_id'];
+		$ret[$count]['vlan_id'] = $row['Vlan_id'];
+		$ret[$count]['vlan_vlanid'] = $row['Vlan_vlanid'];
+		$ret[$count]['vlan_comment'] = $row['Vlan_comment'];
+		$count++;
+	}
+	$result->closeCursor();
+	return $ret;
+}
 function commitAddRack ($name, $height = 0, $row_id = 0, $comment, $taglist)
 {
 	if ($row_id <= 0 or $height <= 0 or empty ($name))
@@ -798,6 +840,82 @@
 	return $errorInfo[2];
 }
 
+function getVLAN ($vlan_id, $vtp_domain = NULL)
+{
+    $query = "SELECT id,vlanid,vtp_domain,comment from Vlan where vlanid = $vlan_id and vtp_domain ";
+    if (strlen($vtp_domain) == 0) {
+        $query .= 'IS NULL';
+    }
+    else {
+        $query .= '= $vtp_domain';
+    }
+	$result = useSelectBlade ($query);
+	$ret = array();
+	while ($row = $result->fetch (PDO::FETCH_ASSOC))
+	{
+		$ret['Vlan_id']=$row['id'];
+		$ret['Vlan_vlan_id']=$row['vlanid'];
+		$ret['Vlan_vtp_domain']=$row['vtp_domain'];
+		$ret['Vlan_comment']=$row['comment'];
+	}
+	$result->closeCursor();
+	return $ret;
+    
+}
+
+function commitAddVLAN ($vlan_id, $vtp_domain = NULL,$comment = NULL)
+{
+	$result = useInsertBlade
+	(
+		'Vlan',
+		array
+		(
+			'id' => 'NULL',
+			'vlanid' => "'${vlan_id}'",
+			'vtp_domain' => (${vtp_domain}) ? '${vtp_domain}' : 'NULL',
+			'comment' => "'${comment}'"
+		)
+	);
+	return $result ? $result : (__FUNCTION__ . '(): useInsertBlade() failed');
+}
+
+function getOrCreateVLAN ($vlan_id, $vtp_domain = NULL)
+{
+    $ret = getVLAN($vlan_id,$vtp_domain);
+    if ($ret != NULL) 
+    	return $ret;
+    commitAddVLAN($vlan_id,$vtp_domain);
+    $ret = getVLAN($vlan_id,$vtp_domain);
+    return $ret;
+}
+
+function commitUpdateVLANMembership ($port_id,$portvlan_id,$tagged = 0,$vlan_id = 1 )
+{
+	global $dbxlink;
+    if ($portvlan_id > 0) {
+        // It's an update
+    	$query =
+    		"update PortVlan set port_id='$port_id', vlan_id='$vlan_id', " .
+    		"tagged = ${tagged} ".
+    		"where id='$portvlan_id'";
+    }
+    else {
+        // It's an insert
+    	$query =
+    		"insert into PortVlan (id,port_id,vlan_id,tagged) values (".
+            "NULL,'$port_id','$vlan_id', '${tagged}') ";
+    }
+	$result = $dbxlink->exec ($query);
+	if ($result == 1)
+		return '';
+	$errorInfo = $dbxlink->errorInfo();
+    echo "$errorInfo[2]";
+	// We could update nothing.
+	if ($errorInfo[0] == '00000')
+		return '';
+	return $errorInfo[2];
+}
+
 function delObjectPort ($port_id)
 {
 	if (unlinkPort ($port_id) != '')
@@ -1889,6 +2007,7 @@
 	}
 	switch ($attr_type)
 	{
+        case 'bool':
 		case 'uint':
 		case 'float':
 		case 'string':
@@ -1984,6 +2103,14 @@
 		$record['type'] = $row['attr_type'];
 		switch ($row['attr_type'])
 		{
+            case 'bool':
+				$record['value'] = $row['uint_value'];
+				if ($record['value'] > 0) {
+                    $record['a_value'] = "Yes";
+                } else {
+                    $record['a_value'] = "No";
+                }
+				break;
 			case 'uint':
 			case 'float':
 			case 'string':
@@ -2057,6 +2184,7 @@
 		case 'string':
 			$column = $attr_type . '_value';
 			break;
+        case 'bool':
 		case 'dict':
 			$column = 'uint_value';
 			break;
@@ -2419,6 +2547,13 @@
 		return TRUE;
 }
 
+function commitDeleteVLANMembership ($id = 0)
+{
+	if ($id <= 0)
+		return FALSE;
+	return useDeleteBlade ('PortVlan', 'id', $id);
+}
+
 function commitUpdateRS ($rsid = 0, $rsip = '', $rsport = 0, $rsconfig = '')
 {
 	if ($rsid <= 0)
Index: /home/justintime/workspace/RackTables/inc/gateways.php
===================================================================
--- /home/justintime/workspace/RackTables/inc/gateways.php	(revision 2225)
+++ /home/justintime/workspace/RackTables/inc/gateways.php	(working copy)
@@ -154,6 +154,79 @@
 	return array ($vlanlist, $portlist, $maclist);
 }
 
+function getVLANs ($object_id = 0)
+{
+	if ($object_id <= 0)
+	{
+		showError ('Invalid object_id', __FUNCTION__);
+		return;
+	}
+	$objectInfo = getObjectInfo ($object_id);
+    
+    
+    
+    
+    
+	$commands = array
+	(
+		"connect ${endpoint} ${hwtype} ${swtype} ${remote_username}",
+		'listvlans',
+		'listports',
+		'listmacs'
+	);
+	$data = queryGateway ('switchvlans', $commands);
+	if ($data == NULL)
+	{
+		showError ('Failed to get any response from queryGateway() or the gateway died', __FUNCTION__);
+		return NULL;
+	}
+	if (strpos ($data[0], 'OK!') !== 0)
+	{
+		showError ("Gateway failure: ${data[0]}.", __FUNCTION__);
+		return NULL;
+	}
+	if (count ($data) != count ($commands))
+	{
+		showError ("Gateway failure: malformed reply.", __FUNCTION__);
+		return NULL;
+	}
+	// Now we have VLAN list in $data[1] and port list in $data[2]. Let's sort this out.
+	$tmp = array_unique (explode (';', substr ($data[1], strlen ('OK!'))));
+	if (count ($tmp) == 0)
+	{
+		showError ("Gateway succeeded, but returned no VLAN records.", __FUNCTION__);
+		return NULL;
+	}
+	$vlanlist = array();
+	foreach ($tmp as $record)
+	{
+		list ($vlanid, $vlandescr) = explode ('=', $record);
+		$vlanlist[$vlanid] = $vlandescr;
+	}
+	$portlist = array();
+	foreach (explode (';', substr ($data[2], strlen ('OK!'))) as $pair)
+	{
+		list ($portname, $pair2) = explode ('=', $pair);
+		list ($status, $vlanid) = explode (',', $pair2);
+		$portlist[] = array ('portname' => $portname, 'status' => $status, 'vlanid' => $vlanid);
+	}
+	if (count ($portlist) == 0)
+	{
+		showError ("Gateway succeeded, but returned no port records.", __FUNCTION__);
+		return NULL;
+	}
+	$maclist = array();
+	foreach (explode (';', substr ($data[3], strlen ('OK!'))) as $pair)
+	{
+		list ($macaddr, $pair2) = explode ('=', $pair);
+		if (empty ($pair2))
+			continue;
+		list ($vlanid, $ifname) = explode ('@', $pair2);
+		$maclist[$ifname][$vlanid][] = $macaddr;
+	}
+	return array ($vlanlist, $portlist, $maclist);
+}
+
 function setSwitchVLANs ($object_id = 0, $setcmd)
 {
 	global $remote_username;
Index: /home/justintime/workspace/RackTables/inc/triggers.php
===================================================================
--- /home/justintime/workspace/RackTables/inc/triggers.php	(revision 2225)
+++ /home/justintime/workspace/RackTables/inc/triggers.php	(working copy)
@@ -38,6 +38,71 @@
 	return FALSE;
 }
 
+function trigger_attribute_vlan_enabled ()
+{
+    assertUIntArg ('object_id', __FUNCTION__);
+    $object_id = $_REQUEST['object_id'];
+    $object = getObjectInfo ($object_id);
+    // Only if Live Vlans are disabled and object is a network switch should vlans be editable
+    if (trigger_livevlans()) {
+        return FALSE;
+    }
+    else {
+        if ($object['objtype_name'] != "network switch") 
+            return FALSE;
+    }
+    return TRUE;
+}
+
+function trigger_attribute_vtp_enabled ()
+{
+    assertUIntArg ('object_id', __FUNCTION__);
+    $object_id = $_REQUEST['object_id'];
+    // Only if Vlans are enabled should vtp be editable
+    if (trigger_vlans()) 
+        return TRUE;
+    echo "In ".__FUNCTION__.", trigger_vlans returned false.<br>";
+    return FALSE;
+}
+
+function trigger_attribute_vtp_domain_name ()
+{
+    assertUIntArg ('object_id', __FUNCTION__);
+    $object_id = $_REQUEST['object_id'];
+    // Only if VTP Vlans are enabled should vtp be editable
+    if (trigger_vtp_vlans())
+        return TRUE;
+    return FALSE;
+}
+
+function trigger_vlans ()
+{
+	assertUIntArg ('object_id', __FUNCTION__);
+	$object_id = $_REQUEST['object_id'];
+	$object = getObjectInfo ($object_id);
+    $values = getAttrValues ($object_id);
+    foreach ($values as $record) {
+        if ($record['name'] == "VLAN Enabled") {
+            return ($record['value'] > 0);
+        }
+    }
+    return FALSE;
+}
+
+function trigger_vtp_vlans ()
+{
+	assertUIntArg ('object_id', __FUNCTION__);
+	$object_id = $_REQUEST['object_id'];
+	$object = getObjectInfo ($object_id);
+    $values = getAttrValues ($object_id);
+    foreach ($values as $record) {
+        if ($record['name'] == "VTP Enabled") {
+            return ($record['value'] > 0);
+        }
+    }
+    return FALSE;
+}
+
 // SNMP port finder tab trigger. At the moment we decide on showing it
 // for pristine switches only. Once a user has begun
 // filling the data in, we stop showing the tab.
Index: /home/justintime/workspace/RackTables/upgrade.php
===================================================================
--- /home/justintime/workspace/RackTables/upgrade.php	(revision 2235)
+++ /home/justintime/workspace/RackTables/upgrade.php	(working copy)
@@ -1413,7 +1413,31 @@
 			$query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('IPV4_JAYWALK','no','string','no','no','Enable IPv4 address allocations w/o covering network')";
 			$query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('ADDNEW_AT_TOP','yes','string','no','no','Render \"add new\" line at top of the list')";
 			$query[] = "update Config set description = 'Extended IPv4 view' where varname = 'EXT_IPV4_VIEW'";
-			$query[] = "update Config set varvalue = '0.16.3' where varname = 'DB_VERSION'";
+            #TODO - insert VTP tables mods here
+            $query[] = "CREATE TABLE `Vlan` (
+                          `id` int(10) unsigned NOT NULL auto_increment,
+                          `vlanid` int(10) unsigned NOT NULL,
+                          `vtp_domain` char(255) default NULL,
+                          `comment` text,
+                          PRIMARY KEY  (`id`)
+                        ) ENGINE=MyISAM";
+            $query[] = "CREATE TABLE `PortVlan` (
+                          `id` int(10) unsigned NOT NULL auto_increment,
+                          `port_id` int(10) unsigned NOT NULL,
+                          `vlan_id` int(10) unsigned NOT NULL,
+                          `tagged` tinyint(3) unsigned NOT NULL default '0',
+                          PRIMARY KEY  (`id`),
+                          KEY `port_index` (`port_id`),
+                          KEY `vlan_index` (`vlan_id`),
+                          KEY `tagged_index` (`tagged`)
+                        ) ENGINE=MyISAM";
+            $query[] = "alter table Attribute MODIFY COLUMN attr_type ENUM('string','uint','float','dict','bool') DEFAULT NULL";
+            $query[] = "INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (26,'bool','VLAN Enabled')";
+            $query[] = "INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (27,'bool','VTP Enabled')";
+            $query[] = "INSERT INTO `Attribute` (`attr_id`, `attr_type`, `attr_name`) VALUES (28,'bool','VTP Domain Name')";
+			$query[] = "INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (8,26,12);";
+			$query[] = "INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (8,27,12);";
+			$query[] = "INSERT INTO `AttributeMap` (`objtype_id`, `attr_id`, `chapter_no`) VALUES (8,28,12);";
 			break;
 		default:
 			showError ("executeUpgradeBatch () failed, because batch '${batchid}' isn't defined", __FILE__);
justin-racktables-vlans.patch (36,412 bytes)   
infrastation

infrastation

2010-02-11 18:29

administrator   ~0000011

working on this
infrastation

infrastation

2010-05-27 15:38

administrator   ~0000089

It took more time, than estimated, but now this work is ready for initial public release.

Issue History

Date Modified Username Field Change
2009-12-24 17:26 user105 New Issue
2009-12-26 17:08 user105 Status new => closed
2010-02-08 16:29 infrastation File Added: justin-racktables-vlans.patch
2010-02-11 18:29 infrastation Note Added: 0000011
2010-02-11 18:29 infrastation Assigned To => infrastation
2010-02-11 18:29 infrastation Status closed => assigned
2010-02-11 18:29 infrastation Category => Database - core
2010-04-02 10:36 infrastation Target Version => 0.18.0
2010-05-06 09:38 infrastation Priority normal => high
2010-05-06 09:38 infrastation Severity feature => major
2010-05-06 09:38 infrastation Projection none => major rework
2010-05-06 09:38 infrastation ETA none => > 1 month
2010-05-06 09:38 infrastation Description Updated
2010-05-06 09:40 infrastation Description Updated
2010-05-27 15:38 infrastation Note Added: 0000089
2010-05-27 15:38 infrastation Status assigned => resolved
2010-05-27 15:38 infrastation Resolution open => fixed
2010-05-27 15:38 infrastation Fixed in Version => 0.18.0
2010-11-29 13:23 infrastation Status resolved => closed