r3887 copy most recent commits from maintenance into trunk
authorDenis Ovsienko <infrastation@yandex.ru>
Sat, 3 Jul 2010 11:22:29 +0000 (11:22 +0000)
committerDenis Ovsienko <infrastation@yandex.ru>
Sat, 3 Jul 2010 11:22:29 +0000 (11:22 +0000)
15 files changed:
ChangeLog
gateways/deviceconfig/vrp55.connector [new file with mode: 0755]
inc/config.php
inc/database.php
inc/dictionary.php
inc/exceptions.php
inc/functions.php
inc/gateways.php
inc/interface.php
inc/ophandlers.php
inc/snmp.php
install/init-structure.sql
popup.php
process.php
upgrade.php

index 86b9391..e5241b6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 0.18.4
        bugfix: a race condition could be triggered in permissions editor
+       new feature: "any mode" of user port in VLAN switch template
 0.18.3 2010-06-15
        bugfix: fix mktemp not working on Slackware (by Rafael Ganascim)
        bugfix: Nexus 802.1Q fixes
diff --git a/gateways/deviceconfig/vrp55.connector b/gateways/deviceconfig/vrp55.connector
new file mode 100755 (executable)
index 0000000..99de0d0
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+[ $# = 3 ] || exit 1
+
+ENDPOINT=$1
+COMMAND=$2
+WORKFILE=$3
+
+prepare_connect_commands()
+{
+       [ $# = 1 ] || exit 2
+       local skip=yes cval found=no MYDIR=`dirname $0`
+       while read line; do
+               if [ "$skip" = "yes" -a "$line" = "# S-T-A-R-T" ]; then
+                       skip=no
+                       continue
+               fi
+               if [ "$skip" = "no" -a "$line" = "# S-T-O-P" ]; then
+                       skip=yes
+                       continue
+               fi
+               [ "$skip" = "yes" ] && continue
+               # ignore comments
+               [ -z "${line###*}" ] && continue
+
+               # First endpoint string/regexp match is sufficient for us.
+               cval=`echo $line | cut -s -d' ' -f1`
+               if [ -z "${1##$cval}" ]; then
+                       found=yes
+                       username=`echo $line | cut -s -d' ' -f5`
+                       [ "$username" != "-" ] && echo $username > "$SESSION"
+                       # access password
+                       access_password=`echo $line | cut -s -d' ' -f6`
+                       [ "$access_password" != "-" ] && echo "$access_password" >> "$SESSION"
+                       printf "super\n" >> "$SESSION"
+                       enable_password=`echo $line | cut -s -d' ' -f7`
+                       [ "$enable_password" != "-" ] && echo $enable_password >> "$SESSION"
+                       break
+               fi
+       done < "$MYDIR/switch.secrets.php"
+       [ "$found" = "yes" ] && return
+       exit 3
+}
+
+MYNAME=`basename $0`
+SESSION=`mktemp /tmp/$MYNAME.XXXXXX`
+[ -f "$SESSION" ] || exit 5
+prepare_connect_commands $ENDPOINT
+case $COMMAND in
+get8021q)
+       printf 'display current-configuration\n' >> "$SESSION"
+       outfile="$WORKFILE"
+       ;;
+getlldpstatus)
+       printf 'display lldp neighbor\n' >> "$SESSION"
+       outfile="$WORKFILE"
+       ;;
+gethndp)
+       printf 'display ndp\n' >> "$SESSION"
+       outfile="$WORKFILE"
+       ;;
+deploy)
+       cat "$WORKFILE" >> "$SESSION"
+       outfile=/dev/null
+       ;;
+*)
+       rm -f "$SESSION"
+       exit 6
+       ;;
+esac
+printf 'quit\n' >> "$SESSION"
+rc=0
+# VRP 5.50 telnet server is broken, hence -i
+nc -w 30 -i1 $ENDPOINT 23 < "$SESSION" > "$outfile" || rc=4
+rm -f "$SESSION"
+exit $rc
index 89201b0..52552e1 100644 (file)
@@ -33,7 +33,7 @@ $max_dict_key = array
        '0.18.1' => 1352,
        '0.18.2' => 1352,
        '0.18.3' => 1356,
-       '0.18.4' => 1361,
+       '0.18.4' => 1364,
 );
 
 define ('TAGNAME_REGEXP', '/^[\p{L}0-9]([. _~-]?[\p{L}0-9])*$/u');
index 290f658..0e6abb7 100644 (file)
@@ -2115,22 +2115,25 @@ function commitUseupPort ($port_id = 0)
 
 function convertPDOException ($e)
 {
-       if ($e->getCode() != 23000)
-               return $e;
-       switch ($e->errorInfo[1])
+       switch ($e->getCode() . '-' . $e->errorInfo[1])
        {
-       case 1062:
+       case '23000-1062':
+               $text = 'such record already exists';
+               break;
+       case '23000-1205':
                $text = 'such record already exists';
                break;
-       case 1451:
-       case 1452:
+       case '23000-1451':
+       case '23000-1452':
                $text = 'foreign key violation';
                break;
-       default:
-               $text = 'unknown error code ' . $e->errorInfo[1];
+       case 'HY000-1205':
+               $text = 'lock wait timeout';
                break;
+       default:
+               return $e;
        }
-       return new RTDBConstraintError ($text);
+       return new RTDatabaseError ($text);
 }
 
 // This is a swiss-knife blade to insert a record into a table.
@@ -2140,7 +2143,7 @@ function usePreparedInsertBlade ($tablename, $columns)
 {
        global $dbxlink;
        $query = "INSERT INTO ${tablename} (" . implode (', ', array_keys ($columns));
-       $query .= ') VALUES (' . implode (', ', array_fill (0, count ($columns), '?')) . ')';
+       $query .= ') VALUES (' . questionMarks (count ($columns)) . ')';
        // Now the query should be as follows:
        // INSERT INTO table (c1, c2, c3) VALUES (?, ?, ?)
        try
@@ -2185,9 +2188,16 @@ function usePreparedSelectBlade ($query, $args = array())
 {
        global $dbxlink;
        $prepared = $dbxlink->prepare ($query);
-       if (!$prepared->execute ($args))
-               return FALSE;
-       return $prepared;
+       try
+       {
+               if (!$prepared->execute ($args))
+                       return FALSE;
+               return $prepared;
+       }
+       catch (PDOException $e)
+       {
+               throw convertPDOException ($e);
+       }
 }
 
 // Prepare and execute the statement with parameters, then return number of
@@ -3733,12 +3743,16 @@ function upd8021QPort ($instance = 'desired', $object_id, $port_name, $port)
        );
        if (FALSE === usePreparedDeleteBlade ($tablemap_8021q[$instance]['pav'], array ('object_id' => $object_id, 'port_name' => $port_name)))
                throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
-       // FIXME: The goal is to INSERT as many rows as there are values in 'allowed' list
+       // The goal is to INSERT as many rows as there are values in 'allowed' list
        // without wrapping each row with own INSERT (otherwise the SQL connection
        // instantly becomes the bottleneck).
-       foreach ($port['allowed'] as $vlan_id)
-               if (!usePreparedInsertBlade ($tablemap_8021q[$instance]['pav'], array ('object_id' => $object_id, 'port_name' => $port_name, 'vlan_id' => $vlan_id)))
-                       throw new RackTablesError ('', RackTablesError::DB_WRITE_FAILED);
+       foreach (listToRanges ($port['allowed']) as $range)
+               usePreparedExecuteBlade
+               (
+                       'INSERT INTO ' . $tablemap_8021q[$instance]['pav'] . ' (object_id, port_name, vlan_id) ' .
+                       'SELECT ?, ?, vlan_id FROM VLANValidID WHERE vlan_id BETWEEN ? AND ?',
+                       array ($object_id, $port_name, $range['from'], $range['to'])
+               );
        if
        (
                $port['native'] and
index 84451d1..7eb19a1 100644 (file)
@@ -1439,6 +1439,9 @@ $dictionary = array
        1359 => array ('chapter_id' => 12, 'dict_value' => 'Huawei%GPASS%Quidway S9312'),
        1360 => array ('chapter_id' => 14, 'dict_value' => 'Huawei VRP 5.3'),
        1361 => array ('chapter_id' => 14, 'dict_value' => 'Huawei VRP 5.5'),
+       1362 => array ('chapter_id' => 12, 'dict_value' => '[[Brocade%GPASS%FCX 648 | http://www.brocade.com/sites/dotcom/products-solutions/products/ethernet-switches-routers/enterprise-mobility/product-details/fcx-series-data-center/index.page ]]'),
+       1363 => array ('chapter_id' => 14, 'dict_value' => 'IronWare 5'),
+       1364 => array ('chapter_id' => 14, 'dict_value' => 'IronWare 7'),
 );
 
 ?>
index 0076063..ce84579 100644 (file)
@@ -98,11 +98,11 @@ class InvalidRequestArgException extends RackTablesError
 
 // this wraps certain known PDO errors and is caught in process.php
 // as a "soft" error
-class RTDBConstraintError extends RackTablesError
+class RTDatabaseError extends RackTablesError
 {
        public function dispatch()
        {
-               RackTablesError::genHTMLPage ('Database constraint violation', '<h2>Constraint violation</h2><br>' . $this->message);
+               RackTablesError::genHTMLPage ('Database soft error', '<h2>Database soft error</h2><br>' . $this->message);
        }
 }
 
index f214d33..1f3109f 100644 (file)
@@ -2428,6 +2428,7 @@ function buildVLANFilter ($role, $string)
        case 'trunk': // 2-4094
        case 'uplink':
        case 'downlink':
+       case 'anymode':
                $min = VLAN_MIN_ID + 1;
                $max = VLAN_MAX_ID;
                break;
@@ -2446,16 +2447,31 @@ function buildVLANFilter ($role, $string)
 
 // pack set of integers into list of integer ranges
 // e.g. (1, 2, 3, 5, 6, 7, 9, 11) => ((1, 3), (5, 7), (9, 9), (11, 11))
-function listToRanges ($vlanidlist)
+// The second argument, when it is different from 0, limits amount of
+// items in each generated range.
+function listToRanges ($vlanidlist, $limit = 0)
 {
        sort ($vlanidlist);
        $ret = array();
        $from = $to = NULL;
        foreach ($vlanidlist as $vlan_id)
                if ($from == NULL)
-                       $from = $to = $vlan_id;
+               {
+                       if ($limit == 1)
+                               $ret[] = array ('from' => $vlan_id, 'to' => $vlan_id);
+                       else
+                               $from = $to = $vlan_id;
+               }
                elseif ($to + 1 == $vlan_id)
+               {
                        $to = $vlan_id;
+                       if ($to - $from + 1 == $limit)
+                       {
+                               // cut accumulated range and start over
+                               $ret[] = array ('from' => $from, 'to' => $to);
+                               $from = $to = NULL;
+                       }
+               }
                else
                {
                        $ret[] = array ('from' => $from, 'to' => $to);
@@ -2722,12 +2738,13 @@ function filter8021QChangeRequests
        $ret = array();
        foreach ($changes as $port_name => $port)
        {
+               // VST violation ?
+               if (!goodModeForVSTRole ($port['mode'], $port['vst_role']))
+                       continue; // ignore change request
                // find and cancel any changes regarding immune VLANs
-               switch ($port['vst_role'])
+               switch ($port['mode'])
                {
                case 'access':
-                       if ($port['mode'] != 'access') // VST violation
-                               continue 2; // ignore change request
                        foreach ($domain_immune_vlans as $immune)
                                // Reverting an attempt to set an access port from
                                // "normal" VLAN to immune one (or vice versa) requires
@@ -2747,10 +2764,6 @@ function filter8021QChangeRequests
                                }
                        break;
                case 'trunk':
-               case 'uplink':
-               case 'downlink':
-                       if ($port['mode'] != 'trunk')
-                               continue 2;
                        foreach ($domain_immune_vlans as $immune)
                                if (in_array ($immune, $before[$port_name]['allowed'])) // was allowed before
                                {
@@ -2768,7 +2781,7 @@ function filter8021QChangeRequests
                                }
                        break;
                default:
-                       continue 2;
+                       throw new InvalidArgException ('mode', $port['mode']);
                }
                // save work
                $ret[$port_name] = $port;
@@ -2814,6 +2827,27 @@ function same8021QConfigs ($a, $b)
                $a['native'] == $b['native'];
 }
 
+// Return TRUE, if the port can be edited by the user.
+function editable8021QPort ($port)
+{
+       return in_array ($port['vst_role'], array ('trunk', 'access', 'anymode'));
+}
+
+// Decide, whether the given 802.1Q port mode is permitted by
+// VST port role.
+function goodModeForVSTRole ($mode, $role)
+{
+       switch ($mode)
+       {
+       case 'access':
+               return in_array ($role, array ('access', 'anymode'));
+       case 'trunk':
+               return in_array ($role, array ('trunk', 'uplink', 'downlink', 'anymode'));
+       default:
+               throw new InvalidArgException ('mode', $mode);
+       }
+}
+
 /*
 
 Relation between desired (D), cached (C) and running (R)
@@ -2967,7 +3001,7 @@ function get8021QSyncOptions
                else // D != C, C != R, D != R: version conflict
                        $ret[$pn] = array
                        (
-                               'status' => ($port['vst_role'] == 'access' or $port['vst_role'] == 'trunk') ?
+                               'status' => editable8021QPort ($port) ?
                                        // In case the port is normally updated by user, let him
                                        // resolve the conflict. If the system manages this port,
                                        // arrange the data to let remote version go down.
@@ -3387,4 +3421,10 @@ function niftyString ($string, $maxlen = 30)
                str_replace (' ', '&nbsp;', htmlspecialchars (mb_substr ($string, 0, $maxlen - 1), ENT_QUOTES, 'UTF-8')) . $cutind . '</span>';
 }
 
+// return a "?, ?, ?, ... ?, ?" string consisting of N question marks
+function questionMarks ($count = 0)
+{
+       return implode (', ', array_fill (0, $count, '?'));
+}
+
 ?>
index f5f1dd3..68afed7 100644 (file)
@@ -30,6 +30,7 @@ $gwrxlator['get8021q'] = array
        'ios12' => 'ios12ReadVLANConfig',
        'fdry5' => 'fdry5ReadVLANConfig',
        'vrp53' => 'vrp53ReadVLANConfig',
+       'vrp55' => 'vrp55Read8021QConfig',
        'nxos4' => 'nxos4Read8021QConfig',
        'xos12' => 'xos12Read8021QConfig',
 );
@@ -41,6 +42,7 @@ $gwpushxlator = array
        'ios12' => 'ios12TranslatePushQueue',
        'fdry5' => 'fdry5TranslatePushQueue',
        'vrp53' => 'vrp53TranslatePushQueue',
+       'vrp55' => 'vrp55TranslatePushQueue',
        'nxos4' => 'ios12TranslatePushQueue', // employ syntax compatibility
        'xos12' => 'xos12TranslatePushQueue',
 );
@@ -250,6 +252,8 @@ function gwRecvFile ($endpoint, $handlername, &$output)
        );
        $output = file_get_contents ($tmpfilename);
        unlink ($tmpfilename);
+       if ($output === FALSE)
+               throw new RTGatewayError ('failed to read temporary file');
        // Being here means having 'OK!' in the response.
        return oneLiner (66, array ($handlername)); // ignore provided "Ok" text
 }
@@ -282,43 +286,21 @@ function gwRecvFileFromObject ($object_id = 0, $handlername, &$output)
 
 function detectDeviceBreed ($object_id)
 {
+       $breed_by_swcode = array
+       (
+               251 => 'ios12',
+               252 => 'ios12',
+               254 => 'ios12',
+               963 => 'nxos4',
+               964 => 'nxos4',
+               1352 => 'xos12',
+               1360 => 'vrp53',
+               1361 => 'vrp55',
+               1363 => 'fdry5',
+       );
        foreach (getAttrValues ($object_id) as $record)
-       {
-               if
-               (
-                       $record['name'] == 'SW type' &&
-                       strlen ($record['o_value']) &&
-                       preg_match ('/^Cisco IOS 12\./', execGMarker ($record['o_value']))
-               )
-                       return 'ios12';
-               if
-               (
-                       $record['name'] == 'SW type' &&
-                       strlen ($record['o_value']) &&
-                       preg_match ('/^Cisco NX-OS 4\./', execGMarker ($record['o_value']))
-               )
-                       return 'nxos4';
-               if
-               (
-                       $record['id'] == 4 &&
-                       $record['key'] == 1352
-               )
-                       return 'xos12';
-               if
-               (
-                       $record['name'] == 'HW type' &&
-                       strlen ($record['o_value']) &&
-                       preg_match ('/^Foundry FastIron GS /', execGMarker ($record['o_value']))
-               )
-                       return 'fdry5';
-               if
-               (
-                       $record['name'] == 'HW type' &&
-                       strlen ($record['o_value']) &&
-                       preg_match ('/^Huawei Quidway S53/', execGMarker ($record['o_value']))
-               )
-                       return 'vrp53';
-       }
+               if ($record['id'] == 4 and array_key_exists ($record['key'], $breed_by_swcode))
+                       return $breed_by_swcode[$record['key']];
        return '';
 }
 
@@ -362,10 +344,12 @@ function gwRetrieveDeviceConfig ($object_id, $command)
                'deviceconfig',
                array ("${command} ${endpoint} ${breed} ${tmpfilename}")
        );
-       $configtext = dos2unix (file_get_contents ($tmpfilename));
+       $configtext = file_get_contents ($tmpfilename);
        unlink ($tmpfilename);
+       if ($configtext === FALSE)
+               throw new RTGatewayError ('failed to read temporary file');
        // Being here means it was alright.
-       return $gwrxlator[$command][$breed] ($configtext);
+       return $gwrxlator[$command][$breed] (dos2unix ($configtext));
 }
 
 function gwDeployDeviceConfig ($object_id, $breed, $text)
@@ -959,6 +943,116 @@ function vrp53PickInterfaceSubcommand (&$work, $line)
        return __FUNCTION__;
 }
 
+function vrp55Read8021QConfig ($input)
+{
+       $ret = array
+       (
+               'vlanlist' => array (1), // VRP 5.50 hides VLAN1 from config text
+               'portdata' => array(),
+       );
+       foreach (explode ("\n", $input) as $line)
+       {
+               $matches = array();
+               // top level
+               if (!array_key_exists ('current', $ret))
+                       switch (TRUE)
+                       {
+                       case (preg_match ('@^ vlan batch (.+)$@', $line, $matches)):
+                               foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
+                                       $ret['vlanlist'][] = $vlan_id;
+                               continue 2;
+                       case (preg_match ('@^interface ((GigabitEthernet|Eth-Trunk)([[:digit:]]+(/[[:digit:]]+)*))$@', $line, $matches)):
+                               $matches[1] = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $matches[1]);
+                               $ret['current'] = array ('port_name' => $matches[1]);
+                               continue 2;
+                       default:
+                               continue 2;
+                       }
+               // inside an interface block
+               switch (TRUE)
+               {
+               case preg_match ('/^ port (link-type )?hybrid /', $line):
+                       throw new RTGatewayError ("unsupported configuration: ${line}");
+               case preg_match ('/^ port link-type (.+)$/', $line, $matches):
+                       $ret['current']['link-type'] = $matches[1];
+                       break;
+               // Native VLAN is configured differently for each link-type case, but
+               // VRP is known to filter off clauses, which don't make sense for
+               // current link-type. This way any interface section should contain
+               // only one kind of "set native" clause (but if this constraint breaks,
+               // we get a problem).
+               case preg_match ('/^ port (default|trunk pvid) vlan ([[:digit:]]+)$/', $line, $matches):
+                       $ret['current']['native'] = $matches[2];
+                       if (!array_key_exists ('allowed', $ret['current']))
+                               $ret['current']['allowed'] = array();
+                       if (!in_array ($ret['current']['native'], $ret['current']['allowed']))
+                               $ret['current']['allowed'][] = $ret['current']['native'];
+                       break;
+               case preg_match ('/^ port trunk allow-pass vlan (.+)$/', $line, $matches):
+                       if (!array_key_exists ('allowed', $ret['current']))
+                               $ret['current']['allowed'] = array();
+                       foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
+                               if (!in_array ($vlan_id, $ret['current']['allowed']))
+                                       $ret['current']['allowed'][] = $vlan_id;
+                       break;
+               case $line == ' undo portswitch':
+               case preg_match ('/^ ip address /', $line):
+                       $ret['current']['link-type'] = 'IP';
+                       break;
+               case preg_match ('/^ eth-trunk /', $line):
+                       $ret['current']['link-type'] = 'SKIP';
+                       break;
+               case substr ($line, 0, 1) == '#': // end of interface section
+                       if (!array_key_exists ('link-type', $ret['current']))
+                               throw new RTGatewayError ('unsupported configuration: link-type is neither trunk nor access for ' . $ret['current']['port_name']);
+                       if (!array_key_exists ('allowed', $ret['current']))
+                               $ret['current']['allowed'] = array();
+                       if (!array_key_exists ('native', $ret['current']))
+                               $ret['current']['native'] = 0;
+                       switch ($ret['current']['link-type'])
+                       {
+                       case 'access':
+                               // In VRP 5.50 an access port has default VLAN ID == 1
+                               $ret['portdata'][$ret['current']['port_name']] =
+                                       $ret['current']['native'] ? array
+                                       (
+                                               'mode' => 'access',
+                                               'allowed' => $ret['current']['allowed'],
+                                               'native' => $ret['current']['native'],
+                                       ) : array
+                                       (
+                                               'mode' => 'access',
+                                               'allowed' => array (VLAN_DFL_ID),
+                                               'native' => VLAN_DFL_ID,
+                                       );
+                               break;
+                       case 'trunk':
+                               $ret['portdata'][$ret['current']['port_name']] = array
+                               (
+                                       'mode' => 'trunk',
+                                       'allowed' => $ret['current']['allowed'],
+                                       'native' => $ret['current']['native'],
+                               );
+                               break;
+                       case 'IP':
+                               $ret['portdata'][$ret['current']['port_name']] = array
+                               (
+                                       'mode' => 'none',
+                                       'allowed' => array(),
+                                       'native' => 0,
+                               );
+                               break;
+                       case 'SKIP':
+                       default: // dot1q-tunnel ?
+                       }
+                       unset ($ret['current']);
+                       break;
+               default: // nom-nom
+               }
+       }
+       return $ret;
+}
+
 function nxos4Read8021QConfig ($input)
 {
        $ret = array
@@ -1234,6 +1328,72 @@ function vrp53TranslatePushQueue ($queue)
        return $ret;
 }
 
+function vrp55TranslatePushQueue ($queue)
+{
+       $ret = '';
+       foreach ($queue as $cmd)
+               switch ($cmd['opcode'])
+               {
+               case 'create VLAN':
+                       if ($cmd['arg1'] != 1)
+                               $ret .= "vlan ${cmd['arg1']}\nquit\n";
+                       break;
+               case 'destroy VLAN':
+                       if ($cmd['arg1'] != 1)
+                               $ret .= "undo vlan ${cmd['arg1']}\n";
+                       break;
+               case 'add allowed':
+               case 'rem allowed':
+                       $undo = $cmd['opcode'] == 'add allowed' ? '' : 'undo ';
+                       $ret .= "interface ${cmd['port']}\n";
+                       foreach (listToRanges ($cmd['vlans']) as $range)
+                               $ret .=  "${undo}port trunk allow-pass vlan " .
+                                       ($range['from'] == $range['to'] ? $range['to'] : "${range['from']} to ${range['to']}") .
+                                       "\n";
+                       $ret .= "quit\n";
+                       break;
+               case 'set native':
+                       $ret .= "interface ${cmd['arg1']}\nport trunk pvid vlan ${cmd['arg2']}\nquit\n";
+                       break;
+               case 'set access':
+                       $ret .= "interface ${cmd['arg1']}\nport default vlan ${cmd['arg2']}\nquit\n";
+                       break;
+               case 'unset native':
+                       $ret .= "interface ${cmd['arg1']}\nundo port trunk pvid vlan\nquit\n";
+                       break;
+               case 'unset access':
+                       $ret .= "interface ${cmd['arg1']}\nundo port default vlan\nquit\n";
+                       break;
+               case 'set mode':
+                       // VRP 5.50's meaning of "trunk" is much like the one of IOS
+                       // (unlike the way VRP 5.30 defines "trunk" and "hybrid"),
+                       // but it is necessary to undo configured VLANs on a port
+                       // for mode change command to succeed.
+                       $undo = array
+                       (
+                               'access' => "undo port trunk allow-pass vlan all\n" .
+                                       "port trunk allow-pass vlan 1\n" .
+                                       "undo port trunk pvid vlan\n",
+                               'trunk' => "undo port default vlan\n",
+                       );
+                       $ret .= "interface ${cmd['arg1']}\n" . $undo[$cmd['arg2']];
+                       $ret .= "port link-type ${cmd['arg2']}\nquit\n";
+                       break;
+               case 'begin configuration':
+                       $ret .= "system-view\n";
+                       break;
+               case 'end configuration':
+                       $ret .= "return\n";
+                       break;
+               case 'save configuration':
+                       $ret .= "save\nY\n";
+                       break;
+               default:
+                       throw new InvalidArgException ('opcode', $cmd['opcode']);
+               }
+       return $ret;
+}
+
 function xos12TranslatePushQueue ($queue)
 {
        $ret = '';
index 932fed7..573d12c 100644 (file)
@@ -1559,7 +1559,7 @@ function showMessageOrError ()
                                105 => array ('code' => 'error', 'format' => 'default VLAN cannot be changed'),
 // ...
                                107 => array ('code' => 'error', 'format' => 'Assertion failed: %s'),
-                               108 => array ('code' => 'error', 'format' => 'Constraint error: %s'),
+                               108 => array ('code' => 'error', 'format' => 'Database error: %s'),
                                109 => array ('code' => 'error', 'format' => 'Update failed!'),
                                110 => array ('code' => 'error', 'format' => 'Supplement failed!'),
                                111 => array ('code' => 'error', 'format' => 'Reduction failed!'),
@@ -6865,25 +6865,7 @@ function renderObject8021QPorts ($object_id)
                                $port['vst_role'] != $port['mode'] or
                                count (array_diff ($port['allowed'], array_keys ($vdom['vlanlist'])))
                        ) ? 'trwarning' : 'trbusy';
-                       $linkparams = array
-                       (
-                               'page' => $pageno,
-                               'tab' => $tabno,
-                               'object_id' => $object_id,
-                       );
-                       if ($port_name == $req_port_name)
-                       {
-                               $imagename = 'Zooming';
-                               $imagetext = 'zoom out';
-                       }
-                       else
-                       {
-                               $imagename = 'Zoom';
-                               $imagetext = 'zoom in';
-                               $linkparams['port_name'] = $port_name;
-                       }
-                       $text_right = "<a href='" . makeHref ($linkparams) . "'>"  .
-                               getImageHREF ($imagename, $imagetext) . '</a>';
+                       $text_right = getTrunkPortCursorCode ($object_id, $port_name, $req_port_name);
                        break;
                case 'access':
                        $trclass =
@@ -6891,42 +6873,18 @@ function renderObject8021QPorts ($object_id)
                                $port['vst_role'] != $port['mode'] or
                                !array_key_exists ($port['native'], $vdom['vlanlist'])
                        ) ? 'trwarning' : 'trbusy';
-                       if ($req_port_name != '')
-                       {
-                               // don't render a form for access ports, when a trunk port is zoomed
-                               $text_right = '&nbsp;';
-                               break;
-                       }
-                       if
-                       (
-                               array_key_exists ($port['native'], $vdom['vlanlist']) and
-                               $vdom['vlanlist'][$port['native']]['vlan_type'] == 'alien'
-                       )
-                       {
-                               $text_right = formatVLANName ($vdom['vlanlist'][$port['native']], 'label');
-                               break;
-                       }
-                       $text_right = "<input type=hidden name=pn_${nports} value=${port_name}>";
-                       $text_right .= "<input type=hidden name=pm_${nports} value=access>";
-                       $options = array();
-                       // Offer only options, which are listed in domain and fit into VST.
-                       // Never offer immune VLANs regardless of VST filter for this port.
-                       // Also exclude current VLAN from the options, unless current port
-                       // mode is "trunk" (in this case it should be possible to set VST-
-                       // approved mode without changing native VLAN ID).
-                       foreach ($vdom['vlanlist'] as $vlan_id => $vlan_info)
-                               if
-                               (
-                                       ($vlan_id != $port['native'] or $port['mode'] == 'trunk') and
-                                       $vlan_info['vlan_type'] != 'alien' and
-                                       matchVLANFilter ($vlan_id, $port['wrt_vlans'])
-                               )
-                                       $options[$vlan_id] = formatVLANName ($vlan_info, 'option');
-                       ksort ($options);
-                       $options['same'] = '-- no change --';
-                       $text_right .= getSelect ($options, array ('name' => "pnv_${nports}"), 'same');
-                       $nports++;
+                       // ---
+                       $text_right = getAccessPortControlCode ($req_port_name, $vdom, $port_name, $port, $nports);
+                       break;
+               case 'anymode':
+                       $trclass = count (array_diff ($port['allowed'], array_keys ($vdom['vlanlist']))) ?
+                               'trwarning' : 'trbusy';
+                       $text_right = getAccessPortControlCode ($req_port_name, $vdom, $port_name, $port, $nports);
+                       $text_right .= '&nbsp;';
+                       $text_right .= getTrunkPortCursorCode ($object_id, $port_name, $req_port_name);
                        break;
+               default:
+                       throw new InvalidArgException ('vst_role', $port['vst_role']);
                }
                if (!array_key_exists ($port_name, $sockets))
                {
@@ -6941,7 +6899,7 @@ function renderObject8021QPorts ($object_id)
                                $socket_columns .= '<td>' . $tmp . '</td>';
                }
                echo "<tr class=${trclass} valign=top><td${td_extra}>${port_name}</td>" . $socket_columns;
-               echo "<td${td_extra}>${text_left}</td><td${td_extra}>${text_right}</td></tr>";
+               echo "<td${td_extra}>${text_left}</td><td class=tdright nowrap${td_extra}>${text_right}</td></tr>";
                if (!array_key_exists ($port_name, $sockets))
                        continue;
                $first_socket = TRUE;
@@ -6974,7 +6932,7 @@ function renderObject8021QPorts ($object_id)
                printOpFormIntro ('save8021QConfig', array ('mutex_rev' => $vswitch['mutex_rev'], 'form_mode' => 'duplicate'));
                $port_options = array();
                foreach ($desired_config as $pn => $portinfo)
-                       if ($portinfo['vst_role'] == 'trunk' or $portinfo['vst_role'] == 'access')
+                       if (editable8021QPort ($portinfo))
                                $port_options[$pn] = same8021QConfigs ($desired_config[$pn], $cached_config[$pn]) ?
                                        $pn : "${pn} (*)";
                echo '<tr><td>' . getSelect ($port_options, array ('name' => 'from_port')) . '</td></tr>';
@@ -6996,6 +6954,67 @@ function renderObject8021QPorts ($object_id)
        echo '</tr></table>';
 }
 
+// Return the text to place into control column of VLAN ports list
+// and modify $nports, when this text was a series of INPUTs.
+function getAccessPortControlCode ($req_port_name, $vdom, $port_name, $port, &$nports)
+{
+       // don't render a form for access ports, when a trunk port is zoomed
+       if ($req_port_name != '')
+               return '&nbsp;';
+       if
+       (
+               array_key_exists ($port['native'], $vdom['vlanlist']) and
+               $vdom['vlanlist'][$port['native']]['vlan_type'] == 'alien'
+       )
+               return formatVLANName ($vdom['vlanlist'][$port['native']], 'label');
+
+       $ret = "<input type=hidden name=pn_${nports} value=${port_name}>";
+       $ret .= "<input type=hidden name=pm_${nports} value=access>";
+       $options = array();
+       // Offer only options, which are listed in domain and fit into VST.
+       // Never offer immune VLANs regardless of VST filter for this port.
+       // Also exclude current VLAN from the options, unless current port
+       // mode is "trunk" (in this case it should be possible to set VST-
+       // approved mode without changing native VLAN ID).
+       foreach ($vdom['vlanlist'] as $vlan_id => $vlan_info)
+               if
+               (
+                       ($vlan_id != $port['native'] or $port['mode'] == 'trunk') and
+                       $vlan_info['vlan_type'] != 'alien' and
+                       matchVLANFilter ($vlan_id, $port['wrt_vlans'])
+               )
+                       $options[$vlan_id] = formatVLANName ($vlan_info, 'option');
+       ksort ($options);
+       $options['same'] = '-- no change --';
+       $ret .= getSelect ($options, array ('name' => "pnv_${nports}"), 'same');
+       $nports++;
+       return $ret;
+}
+
+function getTrunkPortCursorCode ($object_id, $port_name, $req_port_name)
+{
+       global $pageno, $tabno;
+       $linkparams = array
+       (
+               'page' => $pageno,
+               'tab' => $tabno,
+               'object_id' => $object_id,
+       );
+       if ($port_name == $req_port_name)
+       {
+               $imagename = 'Zooming';
+               $imagetext = 'zoom out';
+       }
+       else
+       {
+               $imagename = 'Zoom';
+               $imagetext = 'zoom in';
+               $linkparams['port_name'] = $port_name;
+       }
+       return "<a href='" . makeHref ($linkparams) . "'>"  .
+               getImageHREF ($imagename, $imagetext) . '</a>';
+}
+
 function renderTrunkPortControls ($vswitch, $vdom, $port_name, $vlanport)
 {
        if (!count ($vdom['vlanlist']))
@@ -7048,7 +7067,7 @@ function renderTrunkPortControls ($vswitch, $vdom, $port_name, $vlanport)
                // particular port, but it cannot be changed by user.
                if ($option['vlan_type'] == 'alien')
                        $selected .= ' disabled';
-               echo "<tr><td colspan=2 class=${class}>";
+               echo "<tr><td nowrap colspan=2 class=${class}>";
                echo "<label><input type=checkbox name='pav_0[]' value='${vlan_id}'${selected}> ";
                echo $option['text'] . "</label></td></tr>";
        }
@@ -7096,7 +7115,7 @@ function renderTrunkPortControls ($vswitch, $vdom, $port_name, $vlanport)
                                $option['vlan_type'] == 'alien'
                        )
                                $selected .= ' disabled';
-                       echo "<tr><td colspan=2 class=${class}>";
+                       echo "<tr><td nowrap colspan=2 class=${class}>";
                        echo "<label><input type=radio name='pnv_0' value='${vlan_id}'${selected}> ";
                        echo $option['text'] . "</label></td></tr>";
                }
@@ -7447,7 +7466,7 @@ function renderObject8021QSync ($object_id)
                        (
                                !acceptable8021QConfig ($item['right']) or
                                count (array_diff ($item['right']['allowed'], $domvlans)) or
-                               $item['vst_role'] != $item['right']['mode']
+                               !goodModeForVSTRole ($item['right']['mode'], $item['vst_role'])
                        )
                                $radio_attrs['left'] = ' disabled';
                        break;
@@ -7652,10 +7671,11 @@ function renderVSTRulesEditor ($vst_id)
        $port_role_options = array
        (
                'none' => 'none',
-               'access' => 'access',
-               'trunk' => 'trunk',
-               'uplink' => 'uplink',
-               'downlink' => 'downlink',
+               'access' => 'user: access only',
+               'trunk' => 'user: trunk only',
+               'anymode' => 'user: any mode',
+               'uplink' => 'system: uplink trunk',
+               'downlink' => 'system: downlink trunk',
        );
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
                printNewItemTR ($port_role_options);
index d1ea528..ba46bca 100644 (file)
@@ -2331,22 +2331,14 @@ function unbindVLANfromIPv4 ()
 }
 
 $msgcode['process8021QSyncRequest']['OK'] = 63;
-$msgcode['process8021QSyncRequest']['ERR1'] = 109;
-$msgcode['process8021QSyncRequest']['ERR2'] = 191;
+$msgcode['process8021QSyncRequest']['ERR'] = 191;
 function process8021QSyncRequest ()
 {
        // behave depending on current operation: exec8021QPull or exec8021QPush
        global $sic, $op;
-       try
-       {
-               if (FALSE === $done = exec8021QDeploy ($sic['object_id'], $op == 'exec8021QPush'))
-                       return buildRedirectURL (__FUNCTION__, 'ERR2'); // specific case
-               return buildRedirectURL (__FUNCTION__, 'OK', array ($done));
-       }
-       catch (Exception $e)
-       {
-               return buildRedirectURL (__FUNCTION__, 'ERR1'); // generic failure
-       }
+       if (FALSE === $done = exec8021QDeploy ($sic['object_id'], $op == 'exec8021QPush'))
+               return buildRedirectURL (__FUNCTION__, 'ERR');
+       return buildRedirectURL (__FUNCTION__, 'OK', array ($done));
 }
 
 $msgcode['resolve8021QConflicts']['OK'] = 63;
index e754491..475ba67 100644 (file)
@@ -408,6 +408,24 @@ $iftable_processors['fgs-uplinks'] = array
        'try_next_proc' => FALSE,
 );
 
+$iftable_processors['fcx-uplinks'] = array
+(
+       'pattern' => '@^10GigabitEthernet1/2/([[:digit:]]+)$@',
+       'replacement' => 'e1/2/\\1',
+       'dict_key' => '9-1084',
+       'label' => 'X\\1',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['fcx-management'] = array
+(
+       'pattern' => '@^Management$@',
+       'replacement' => 'management1',
+       'dict_key' => '1-24',
+       'label' => 'Management',
+       'try_next_proc' => FALSE,
+);
+
 $iftable_processors['summit-25-to-26-XFP-uplinks'] = array
 (
        'pattern' => '@^.+ Port (25|26)$@',
@@ -527,6 +545,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'WS-C2960-48TT-L: 48 RJ-45/10-100TX + 2 RJ-45/10-100-1000T(X)',
                'processors' => array ('catalyst-chassis-any-100TX', 'catalyst-chassis-any-1000T'),
        ),
+       '9.1.950' => array
+       (
+               'dict_key' => 1347,
+               'text' => 'WS-C2960-24PC: 44 RJ-45/10-100TX + 2 combo-gig',
+               'processors' => array ('catalyst-chassis-1-to-2-combo-1000SFP', 'catalyst-chassis-any-1000T', 'catalyst-chassis-any-100TX'),
+       ),
        '9.1.527' => array
        (
                'dict_key' => 210,
@@ -755,6 +779,12 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'FGS648P-POE: 48 RJ-45/10-100-1000T(X) + 4 combo-gig + uplink slot',
                'processors' => array ('fgs-1-to-4-comboSFP', 'fgs-any-1000T', 'fgs-uplinks'),
        ),
+       '1991.1.3.54.2.4.1.1' => array
+       (
+               'dict_key' => 1362,
+               'text' => 'FCX 648: 48 RJ-45/10-100-1000T(X) + uplink slot with 4 SFP+',
+               'processors' => array ('fgs-any-1000T', 'fcx-uplinks', 'fcx-management'),
+       ),
        '1916.2.71' => array
        (
                'dict_key' => 694,
@@ -909,6 +939,17 @@ function doSwitchSNMPmining ($objectInfo, $hostname, $community)
                $log = mergeLogs ($log, oneLiner (81, array ('netgear-generic')));
                break;
        case preg_match ('/^2011\.2\.23\./', $sysObjectID): // Huawei
+               $swtype_pcre = array
+               (
+                       '/Huawei Versatile Routing Platform Software.+VRP.+Software, Version 5.30 /s' => 1360,
+                       '/Huawei Versatile Routing Platform Software.+VRP.+Software, Version 5.50 /s' => 1361,
+               );
+               foreach ($swtype_pcre as $pattern => $dict_key)
+                       if (preg_match ($pattern, $sysDescr))
+                       {
+                               updateStickerForCell ($objectInfo, 4, $dict_key);
+                               break;
+                       }
                checkPIC ('1-681');
                commitAddPort ($objectInfo['id'], 'con0', '1-681', 'console', ''); // DB-9 RS-232 console
                $log = mergeLogs ($log, oneLiner (81, array ('huawei-generic')));
@@ -919,6 +960,7 @@ function doSwitchSNMPmining ($objectInfo, $hostname, $community)
                $log = mergeLogs ($log, oneLiner (81, array ('juniper-generic')));
                break;
        case preg_match ('/^1991\.1\.3\.45\./', $sysObjectID): // snFGSFamily
+       case preg_match ('/^1991\.1\.3\.54\.2\.4\.1\.1$/', $sysObjectID): // FCX 648
                $exact_release = preg_replace ('/^.*, IronWare Version ([^ ]+) .*$/', '\\1', $sysDescr);
                updateStickerForCell ($objectInfo, 5, $exact_release);
                # FOUNDRY-SN-AGENT-MIB::snChasSerNum.0
index 6509ad7..1f42b4e 100644 (file)
@@ -449,7 +449,7 @@ CREATE TABLE `VLANSTRule` (
   `vst_id` int(10) unsigned NOT NULL,
   `rule_no` int(10) unsigned NOT NULL,
   `port_pcre` char(255) NOT NULL,
-  `port_role` enum('access','trunk','uplink','downlink','none') NOT NULL default 'none',
+  `port_role` enum('access','trunk','anymode','uplink','downlink','none') NOT NULL default 'none',
   `wrt_vlans` char(255) default NULL,
   `description` char(255) default NULL,
   UNIQUE KEY `vst-rule` (`vst_id`,`rule_no`),
index e1df5b2..b8906aa 100644 (file)
--- a/popup.php
+++ b/popup.php
@@ -44,7 +44,7 @@ function findSparePorts ($port_id, $only_racks = array())
        if (count ($only_racks))
        {
                $query .= 'AND object_id IN (SELECT DISTINCT object_id FROM RackSpace WHERE rack_id IN (' .
-                       implode (', ', array_fill (0, count ($only_racks), '?')) . '))';
+                       questionMarks (count ($only_racks)) . '))';
                $qparams = array_merge ($qparams, $only_racks);
        }
        $query .= ' ORDER BY object_id, name';
index 555ba3e..01a6b69 100644 (file)
@@ -36,7 +36,7 @@ catch (InvalidRequestArgException $e)
        ob_end_clean();
        header ('Location: ' . buildWideRedirectURL (oneLiner (107, array ($e->getMessage()))));
 }
-catch (RTDBConstraintError $e)
+catch (RTDatabaseError $e)
 {
        ob_end_clean();
        header ('Location: ' . buildWideRedirectURL (oneLiner (108, array ($e->getMessage()))));
index a6a3571..f9c52fa 100644 (file)
@@ -742,6 +742,7 @@ CREATE TABLE `VLANValidID` (
                        break;
                case '0.18.4':
                        $query = array_merge ($query, reloadDictionary ($batchid));
+                       $query[] = "ALTER TABLE VLANSTRule MODIFY port_role enum('access','trunk','anymode','uplink','downlink','none') NOT NULL default 'none'";
                        $query[] = "UPDATE Config SET varvalue = '0.18.4' WHERE varname = 'DB_VERSION'";
                        break;
                default: