r4070 update: 802.1Q template editor now supports single-submit edit and concerns...
authorAlexey Andriyanov <alan@al-an.info>
Mon, 27 Dec 2010 12:40:33 +0000 (12:40 +0000)
committerAlexey Andriyanov <alan@al-an.info>
Mon, 27 Dec 2010 12:40:33 +0000 (12:40 +0000)
inc/ophandlers.php:
 -addVSTRule: unneeded op removed (no more ops for single rule)
 -delVSTRule: idem
 +cloneVSTRule: template cloning code moved to 'clone' op, uses common commitUpdateVSTRules
 updVSTRule: function redesigned to update whole template, not the single rule. Takes input data as text in json format. Uses common commitUpdateVSTRules.

inc/navigation.php: [editrules]add/del removed, clone added, tab renamed from 'Rules' to 'Edit'

inc/interface.php:
 global $port_role_options moved to database.php
 renderVSTRules: code to render read-only rules list, separated from renderVST
 renderVST: uses new renderVSTRules
 renderVSTRulesEditor: redesigned to render new JS-based VST editor.

inc/database.php:
 $port_role_options moved from interfaces.php
 getVLANSwitchTemplate: selects new fields from VLANSwitchTemplate table
 commitUpdateVSTRules: new function, replaces all the rules in the VST. Updates mutex and author in VLANSwitchTemplate.

inc/functions.php:
 isInteger: new function to check values to be integer. returns BOOL.
 isPCRE: idem for PCRE

upgrade.php: new columns 'mutex_rev' and 'saved_by' in VLANSwitchTemplate table
install/init-structure.sql: idem

ChangeLog
inc/database.php
inc/functions.php
inc/interface.php
inc/navigation.php
inc/ophandlers.php
install/init-structure.sql
js/vst_editor.js [new file with mode: 0644]
upgrade.php

index 2b507ae..6095f8a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -18,6 +18,7 @@
        update: Huawei VRP 5.70 pseudo-interactive telnet support (fixes multiple problems caused by fast commands post through netcat)
        new feature: UI: live switchport info (port config, link status, learned mac list) showed inline on any object tab
        update: selective including of JS and CSS files. No more unneeded js code loaded.
+       update: 802.1Q template editor now supports single-submit edit and concerns concurrent submits
 0.18.7
        bugfix: adjust 802.1Q command generation
        bugfix: fixed telnet session hanging in NX-OS4 connector
index 53ad679..457e8ba 100644 (file)
@@ -171,6 +171,17 @@ $tablemap_8021q = array
        ),
 );
 
+// VST roles
+$port_role_options = array
+(
+       'none' => 'none',
+       'access' => 'user: access only',
+       'trunk' => 'user: trunk only',
+       'anymode' => 'user: any mode',
+       'uplink' => 'system: uplink trunk',
+       'downlink' => 'system: downlink trunk',
+);
+
 $object_attribute_cache = array();
 
 function escapeString ($value, $do_db_escape = FALSE)
@@ -4326,7 +4337,7 @@ function getVSTOptions()
 
 function getVLANSwitchTemplate ($vst_id)
 {
-       $result = usePreparedSelectBlade ('SELECT id, max_local_vlans, description FROM VLANSwitchTemplate WHERE id = ?', array ($vst_id));
+       $result = usePreparedSelectBlade ('SELECT * FROM VLANSwitchTemplate WHERE id = ?', array ($vst_id));
        if (!($ret = $result->fetch (PDO::FETCH_ASSOC)))
                throw new EntityNotFoundException ('vst', $vst_id);
        unset ($result);
@@ -4366,6 +4377,26 @@ function commitUpdateVSTRule ($vst_id, $rule_no, $new_rule_no, $port_pcre, $port
        );
 }
 
+function commitUpdateVSTRules ($vst_id, $rules)
+{
+       global $dbxlink, $remote_username;
+       $dbxlink->beginTransaction();
+       try
+       {
+               usePreparedDeleteBlade ('VLANSTRule', array ('vst_id' => $vst_id));
+               foreach ($rules as $rule)
+                       usePreparedInsertBlade ('VLANSTRule', array_merge (array ('vst_id' => $vst_id), $rule));
+               usePreparedExecuteBlade ('UPDATE VLANSwitchTemplate SET mutex_rev=mutex_rev+1, saved_by=? WHERE id=?', array ($remote_username, $vst_id));
+       }
+       catch (Exception $e)
+       {
+               $dbxlink->rollBack();
+               return FALSE;
+       }
+       $dbxlink->commit();
+       return TRUE;
+}
+
 function getIPv4Network8021QBindings ($ipv4net_id)
 {
        $prepared = usePreparedSelectBlade
index 29d7582..a65f6a9 100644 (file)
@@ -133,6 +133,15 @@ function assertUIntArg ($argname, $allow_zero = FALSE)
                throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is zero');
 }
 
+function isInteger ($arg, $allow_zero = FALSE)
+{
+       if (! is_numeric ($arg))
+               return FALSE;
+       if (! $allow_zero and ! $arg)
+               return FALSE;
+       return TRUE;
+}
+
 // This function assures that specified argument was passed
 // and is a non-empty string.
 function assertStringArg ($argname, $ok_if_empty = FALSE)
@@ -200,6 +209,13 @@ function assertPCREArg ($argname)
                throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'PCRE validation failed');
 }
 
+function isPCRE ($arg)
+{
+       if (! isset ($arg) or FALSE === preg_match ($arg, 'test'))
+               return FALSE;
+       return TRUE;
+}
+
 // Objects of some types should be explicitly shown as
 // anonymous (labelless). This function is a single place where the
 // decision about displayed name is made.
index f2767f6..f7d5c70 100644 (file)
@@ -62,17 +62,6 @@ $dqtitle = array
        'done' => 'Up to date',
 );
 
-// VST roles
-$port_role_options = array
-(
-       'none' => 'none',
-       'access' => 'user: access only',
-       'trunk' => 'user: trunk only',
-       'anymode' => 'user: any mode',
-       'uplink' => 'system: uplink trunk',
-       'downlink' => 'system: downlink trunk',
-);
-
 // Let's have it here, so extensions can add their own images.
 $image = array();
 $image['error']['path'] = 'pix/error.png';
@@ -8795,23 +8784,18 @@ function renderVSTListEditor()
        echo '</table>';
 }
 
-function renderVST ($vst_id)
+function renderVSTRules ($rules, $title = NULL)
 {
-       global $nextorder;
-       $vst = getVLANSwitchTemplate ($vst_id);
-       echo '<table border=0 class=objectview cellspacing=0 cellpadding=0>';
-       echo "<tr><td colspan=2 align=center><h1>${vst['description']}</h1><h2>";
-       echo "<tr><td class=pcleft width='50%'>";
-       if (!count ($vst['rules']))
-               startPortlet ('no rules');
+       if (!count ($rules))
+               startPortlet (isset ($title) ? $title : 'no rules');
        else
        {
                global $port_role_options;
-               startPortlet ('rules (' . count ($vst['rules']) . ')');
+               startPortlet (isset ($title) ? $title : 'rules (' . count ($rules) . ')');
                echo '<table class=cooltable align=center border=0 cellpadding=5 cellspacing=0>';
                echo '<tr><th>sequence</th><th>regexp</th><th>role</th><th>VLAN IDs</th><th>comment</th></tr>';
                $order = 'odd';
-               foreach ($vst['rules'] as $item)
+               foreach ($rules as $item)
                {
                        echo "<tr class=row_${order} align=left>";
                        echo "<td>${item['rule_no']}</td>";
@@ -8825,6 +8809,15 @@ function renderVST ($vst_id)
                echo '</table>';
        }
        finishPortlet();
+}
+
+function renderVST ($vst_id)
+{
+       $vst = getVLANSwitchTemplate ($vst_id);
+       echo '<table border=0 class=objectview cellspacing=0 cellpadding=0>';
+       echo "<tr><td colspan=2 align=center><h1>${vst['description']}</h1><h2>";
+       echo "<tr><td class=pcleft width='50%'>";
+       renderVSTRules ($vst['rules']);
        echo '</td><td class=pcright>';
        if (!count ($vst['switches']))
                startPortlet ('no orders');
@@ -8864,19 +8857,6 @@ function renderVSTEditor ($vst_id)
 
 function renderVSTRulesEditor ($vst_id)
 {
-       function printNewItemTR ($port_role_options)
-       {
-               printOpFormIntro ('add', array ('submode' => 'addnew'));
-               echo '<tr>';
-               echo '<td>' . getImageHREF ('add', 'add rule', TRUE, 110) . '</td>';
-               echo '<td><input type=text tabindex=101 name=rule_no size=3></td>';
-               echo '<td><input type=text tabindex=102 name=port_pcre></td>';
-               echo '<td>' . getSelect ($port_role_options, array ('name' => 'port_role', 'tabindex' => 103), 'none') . '</td>';
-               echo '<td><input type=text tabindex=104 name=wrt_vlans></td>';
-               echo '<td><input type=text tabindex=105 name=description></td>';
-               echo '<td>' . getImageHREF ('add', 'add rule', TRUE, 110) . '</td>';
-               echo '</tr></form>';
-       }
        $vst = getVLANSwitchTemplate ($vst_id);
        if (count ($vst['rules']))
                $source_options = array();
@@ -8888,40 +8868,46 @@ function renderVSTRulesEditor ($vst_id)
                        if ($vst_info['rulec'])
                                $source_options[$vst_id] = niftyString ('(' . $vst_info['rulec'] . ') ' . $vst_info['description']);
        }
+       addJS ('js/vst_editor.js');
        echo '<center><h1>' . niftyString ($vst['description']) . '</h1></center>';
        if (count ($source_options))
        {
                startPortlet ('clone another template');
-               printOpFormIntro ('add', array ('submode' => 'copyfrom'));
+               printOpFormIntro ('clone');
+               echo '<input type=hidden name="mutex_rev" value="' . $vst['mutex_rev'] . '">';
                echo '<table cellspacing=0 cellpadding=5 align=center class=widetable>';
                echo '<tr><td>' . getSelect ($source_options, array ('name' => 'from_id')) . '</td>';
                echo '<td>' . getImageHREF ('COPY', 'copy from selected', TRUE) . '</td></tr></table></form>';
                finishPortlet();
                startPortlet ('add rules one by one');
        }
-       echo '<table cellspacing=0 cellpadding=5 align=center class=widetable>';
-       echo '<tr><th>&nbsp;</th><th>sequence</th><th>regexp</th><th>role</th>';
-       echo '<th>VLAN IDs</th><th>comment</th><th>&nbsp;</th></tr>';
+       echo '<table cellspacing=0 cellpadding=5 align=center class="widetable template-rules">';
+       echo '<tr><th></th><th>sequence</th><th>regexp</th><th>role</th>';
+       echo '<th>VLAN IDs</th><th>comment</th><th><a href="#" class="vst-add-rule initial">' . getImageHREF ('add', 'add rule') . '</a></th></tr>';
        global $port_role_options;
-       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
-               printNewItemTR ($port_role_options);
-       foreach ($vst['rules'] as $item)
+       $row_html  = '<td><a href="#" class="vst-del-rule">' . getImageHREF ('destroy', 'delete rule') . '</a></td>';
+       $row_html .= '<td><input type=text name=rule_no value="%s" size=3></td>';
+       $row_html .= '<td><input type=text name=port_pcre value="%s"></td>';
+       $row_html .= '<td>%s</td>';
+       $row_html .= '<td><input type=text name=wrt_vlans value="%s"></td>';
+       $row_html .= '<td><input type=text name=description value="%s"></td>';
+       $row_html .= '<td><a href="#" class="vst-add-rule">' . getImageHREF ('add', 'add rule') . '</a></td>';
+       addJS ("var new_vst_row = '" . addslashes (sprintf ($row_html, '', '', getSelect ($port_role_options, array ('name' => 'port_role'), 'anymode'), '', '')) . "';", TRUE);
+       foreach (isset ($_SESSION['vst_edited']) ? $_SESSION['vst_edited'] : $vst['rules'] as $item)
+               printf ('<tr>' . $row_html . '</tr>', $item['rule_no'],  niftyString ($item['port_pcre'], 0),  getSelect ($port_role_options, array ('name' => 'port_role'), $item['port_role']), $item['wrt_vlans'], $item['description']);
+       echo '</table>';
+       printOpFormIntro ('upd');
+       echo '<input type=hidden name="template_json">';
+       echo '<input type=hidden name="mutex_rev" value="' . $vst['mutex_rev'] . '">';
+       echo '<center>' . getImageHref ('SAVE', 'Save template', TRUE) . '</center>';
+       echo '</form>';
+       if (isset ($_SESSION['vst_edited']))
        {
-               printOpFormIntro ('upd', array ('rule_no' => $item['rule_no']));
-               echo '<tr>';
-               echo '<td><a href="' . makeHrefProcess (array ('op' => 'del', 'vst_id' => $vst_id, 'rule_no' => $item['rule_no'])) . '">';
-               echo getImageHREF ('destroy', 'delete rule') . '</a></td>';
-               echo "<td><input type=text name=new_rule_no value=${item['rule_no']} size=3></td>";
-               echo "<td><input type=text name=port_pcre value='" . niftyString ($item['port_pcre'], 0) . "'></td>";
-               echo '<td>' . getSelect ($port_role_options, array ('name' => 'port_role'), $item['port_role']) . '</td>';
-               echo "<td><input type=text name=wrt_vlans value='${item['wrt_vlans']}'></td>";
-               echo "<td><input type=text name=description value='${item['description']}'></td>";
-               echo '<td>' . getImageHref ('save', 'update rule', TRUE) . '</td>';
-               echo '</tr></form>';
+               // draw current template
+               renderVSTRules ($vst['rules'], 'currently saved tamplate');
+               unset ($_SESSION['vst_edited']);
        }
-       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
-               printNewItemTR ($port_role_options);
-       echo '</table>';
+       
        if (count ($source_options))
                finishPortlet();
 }
index a5b457c..d031c8c 100644 (file)
@@ -603,7 +603,7 @@ $page['vst']['parent'] = '8021q';
 $page['vst']['bypass'] = 'vst_id';
 $page['vst']['bypass_type'] = 'uint';
 $tab['vst']['default'] = 'View';
-$tab['vst']['editrules'] = 'Rules';
+$tab['vst']['editrules'] = 'Edit';
 $tab['vst']['8021qorder'] = '802.1Q orders';
 $trigger['vst']['editrules'] = 'trigger_vst_editrules';
 $tabhandler['vst']['default'] = 'renderVST';
@@ -611,8 +611,7 @@ $tabhandler['vst']['editvst'] = 'renderVSTEditor';
 $tabhandler['vst']['editrules'] = 'renderVSTRulesEditor';
 $tabhandler['vst']['8021qorder'] = 'render8021QOrderForm';
 $ophandler['vst']['editvst']['upd'] = 'updVLANSwitchTemplate';
-$ophandler['vst']['editrules']['add'] = 'addVSTRule';
-$ophandler['vst']['editrules']['del'] = 'delVSTRule';
+$ophandler['vst']['editrules']['clone'] = 'cloneVSTRule';
 $ophandler['vst']['editrules']['upd'] = 'updVSTRule';
 $ophandler['vst']['8021qorder']['add'] = 'add8021QOrder';
 $ophandler['vst']['8021qorder']['del'] = 'del8021QOrder';
index adac640..1b48d20 100644 (file)
@@ -2709,98 +2709,86 @@ function updVLANSwitchTemplate()
        return buildRedirectURL (__FUNCTION__, $result !== FALSE ? 'OK' : 'ERR');
 }
 
-$msgcode['addVSTRule']['OK'] = 48;
-$msgcode['addVSTRule']['ERR1'] = 110;
-$msgcode['addVSTRule']['ERR2'] = 179;
-function addVSTRule()
+$msgcode['cloneVSTRule']['OK'] = 48;
+$msgcode['cloneVSTRule']['ERR'] = 179;
+function cloneVSTRule()
 {
+       global $dbxlink;
+       $message = '';
        assertUIntArg ('vst_id');
-       assertStringArg ('submode');
-       switch ($_REQUEST['submode'])
+       assertUIntArg ('mutex_rev', TRUE);
+       $dst_vst = getVLANSwitchTemplate ($_REQUEST['vst_id']);
+       if ($dst_vst['mutex_rev'] != $_REQUEST['mutex_rev'])
+               $message = "User ${dst_vst['saved_by']} saved this template after you started to edit it. Please concern differencies";
+       else
        {
-       case 'addnew':
-               assertUIntArg ('rule_no');
-               assertPCREArg ('port_pcre');
-               assertStringArg ('port_role');
-               assertStringArg ('wrt_vlans', TRUE);
-               assertStringArg ('description', TRUE);
-               global $sic;
-               $result = usePreparedInsertBlade
-               (
-                       'VLANSTRule',
-                       array
-                       (
-                               'vst_id' => $sic['vst_id'],
-                               'rule_no' => $sic['rule_no'],
-                               'port_pcre' => $sic['port_pcre'],
-                               'port_role' => $sic['port_role'],
-                               'wrt_vlans' => $sic['wrt_vlans'],
-                               'description' => $sic['description'],
-                       )
-               );
-               return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR1');
-               break;
-       case 'copyfrom':
-               $dst_vst = getVLANSwitchTemplate ($_REQUEST['vst_id']);
-               // FIXME: this is a race condition, which is better handled with proper locking
-               if (count ($dst_vst['rules']))
-                       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR2');
                assertUIntArg ('from_id');
                $src_vst = getVLANSwitchTemplate ($_REQUEST['from_id']);
-               foreach ($src_vst['rules'] as $rule)
-               {
-                       $rule['vst_id'] = $_REQUEST['vst_id'];
-                       usePreparedInsertBlade ('VLANSTRule', $rule);
-               }
-               return buildRedirectURL (__FUNCTION__, 'OK');
-               break;
-       default:
-               throw new InvalidArgException ('submode', $_REQUEST['submode']);
+               if (! commitUpdateVSTRules ($_REQUEST['vst_id'], $src_vst['rules']))
+                       $message = 'DB error';
        }
+       $result = !(BOOL) $message;
+       if ($result)
+               $message = 'Supplement succeeded';
+       return buildWideRedirectURL (array (array ('code' => $result ? 'success' : 'error', 'message' => $message)));
 }
 
-$msgcode['delVSTRule']['OK'] = 49;
-$msgcode['delVSTRule']['ERR'] = 111;
-function delVSTRule()
-{
-       assertUIntArg ('vst_id');
-       assertUIntArg ('rule_no');
-       global $sic;
-       $result = FALSE !== usePreparedDeleteBlade
-       (
-               'VLANSTRule',
-               array
-               (
-                       'vst_id' => $sic['vst_id'],
-                       'rule_no' => $sic['rule_no']
-               )
-       );
-       return buildRedirectURL (__FUNCTION__, $result ? 'OK' : 'ERR');
-}
-
-$msgcode['updVSTRule']['OK'] = 51;
-$msgcode['updVSTRule']['ERR'] = 109;
 function updVSTRule()
 {
+       global $port_role_options;
        assertUIntArg ('vst_id');
-       assertUIntArg ('rule_no');
-       assertUIntArg ('new_rule_no');
-       assertPCREArg ('port_pcre');
-       assertStringArg ('port_role');
-       assertStringArg ('wrt_vlans', TRUE);
-       assertStringArg ('description', TRUE);
-       global $sic;
-       $result = commitUpdateVSTRule
-       (
-               $sic['vst_id'],
-               $sic['rule_no'],
-               $sic['new_rule_no'],
-               $sic['port_pcre'],
-               $sic['port_role'],
-               $sic['wrt_vlans'],
-               $sic['description']
-       );
-       return buildRedirectURL (__FUNCTION__, $result !== FALSE ? 'OK' : 'ERR');
+       assertUIntArg ('mutex_rev', TRUE);
+       assertStringArg ('template_json');
+       $message = '';
+       $data = json_decode ($_POST['template_json'], TRUE);
+       if (! isset ($data) or ! is_array ($data))
+               $message = 'Invalid JSON code received from client';
+       else
+       {
+               $rule_no = 0;
+               foreach ($data as $rule)
+               {
+                       $rule_no++;
+                       if (! isInteger ($rule['rule_no']))
+                               $message = 'Invalid rule number';
+                       elseif (! isPCRE ($rule['port_pcre']))
+                               $message = 'Invalid port regexp';
+                       elseif (! isset ($rule['port_role']) or ! array_key_exists ($rule['port_role'], $port_role_options))
+                               $message = 'Invalid port role';
+                       elseif (! isset ($rule['wrt_vlans']) or ! preg_match ('/^[ 0-9\-,]*$/', $rule['wrt_vlans']))
+                               $message = 'Invalid port vlan mask';
+                       elseif (! isset ($rule['description']))
+                               $message = 'Invalid description';
+
+                       if ($message)
+                       {
+                               $message .= " in rule $rule_no";
+                               break;
+                       }
+               }
+               if (! $message)
+               {
+                       $vst = getVLANSwitchTemplate ($_REQUEST['vst_id']);
+                       if ($vst['mutex_rev'] != $_REQUEST['mutex_rev'])
+                               $message = "User ${vst['saved_by']} saved this template after you started to edit it. Please concern differencies";
+               }
+               if (! $message)
+                       if (! commitUpdateVSTRules ($_REQUEST['vst_id'], $data))
+                               $message = 'DB update error';
+               if ($message)
+                       $_SESSION['vst_edited'] = $data;
+       }
+       if ($message)
+       {
+               $result = FALSE;
+               $message = "Template update failed: $message";
+       }
+       else
+       {
+               $result = TRUE;
+               $message = "Update success";
+       }
+       return buildWideRedirectURL (array (array ('code' => $result ? 'success' : 'error', 'message' => $message)));
 }
 
 $msgcode['importDPData']['OK'] = 44;
index e0b54e0..df77188 100644 (file)
@@ -571,6 +571,8 @@ CREATE TABLE `VLANSwitchTemplate` (
   `id` int(10) unsigned NOT NULL auto_increment,
   `max_local_vlans` int(10) unsigned default NULL,
   `description` char(255) default NULL,
+  `mutex_rev` int(10) NOT NULL,
+  `saved_by` char(64) NOT NULL,
   PRIMARY KEY  (`id`),
   UNIQUE KEY `description` (`description`)
 ) ENGINE=InnoDB;
diff --git a/js/vst_editor.js b/js/vst_editor.js
new file mode 100644 (file)
index 0000000..5faa13e
--- /dev/null
@@ -0,0 +1,40 @@
+// var new_vst_row is set through inline JS script
+
+$(document).ready(function() {
+       $('a.vst-add-rule').click(AddVSTRule)
+       $('a.vst-del-rule').click(RemoveVSTRule)
+       $('form#upd').submit(VSTSubmit);
+});
+
+function AddVSTRule(event) {
+       var tr = $(this).closest('tr');
+       var new_tr;
+       if ($(event.target).parents('.vst-add-rule.initial').length)
+               new_tr = $('<tr />').html(new_vst_row);
+       else
+               new_tr = tr.clone();
+       tr.after(new_tr);
+       new_tr.find('a.vst-add-rule').click(AddVSTRule);
+       new_tr.find('a.vst-del-rule').click(RemoveVSTRule);
+
+       return false;
+}
+
+function RemoveVSTRule(event) {
+       $(this).closest('tr').remove();
+       return false;
+}
+
+function VSTSubmit() {
+       var result = [];
+       $('table.template-rules tr').each(function(i, item) {
+               var line = {};
+               $(item).find('input , select').each(function(i, item) { line[item.name] = $(item).val(); });
+               if (line['rule_no'] != undefined)
+                       result.push(line);
+       });
+       if (! result.length && ! confirm('The template is empty. Do you really want to save it?'))
+               return false;
+       $(this).find('input[name|="template_json"]').val(JSON.stringify(result));
+       return true;
+}
index 52bb113..899c512 100644 (file)
@@ -870,6 +870,8 @@ END;
                        $query[] = 'ALTER TABLE RackSpace ADD CONSTRAINT `RackSpace-FK-rack_id` FOREIGN KEY (rack_id) REFERENCES Rack (id)';
                        $query[] = "ALTER TABLE `IPv4Allocation` ADD CONSTRAINT `IPv4Allocation-FK-object_id` FOREIGN KEY (`object_id`) REFERENCES `RackObject` (`id`) ON DELETE CASCADE";
                        $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('SYNCDOMAIN_MAX_PROCESSES','0','uint','yes','no', 'How many worker proceses syncdomain cron script should create')";
+                       $query[] = "ALTER TABLE `VLANSwitchTemplate` ADD COLUMN `mutex_rev` int(10) NOT NULL AFTER `max_local_vlans`";
+                       $query[] = "ALTER TABLE `VLANSwitchTemplate` ADD COLUMN `saved_by` char(64) NOT NULL AFTER `description`";
                        $query[] = "UPDATE Config SET varvalue = '0.19.0' WHERE varname = 'DB_VERSION'";
                        break;
                default: