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__);
