r3949 copy recent (mostly tested) commits from maintenance to trunk
authorDenis Ovsienko <infrastation@yandex.ru>
Wed, 29 Sep 2010 20:06:16 +0000 (20:06 +0000)
committerDenis Ovsienko <infrastation@yandex.ru>
Wed, 29 Sep 2010 20:06:16 +0000 (20:06 +0000)
16 files changed:
ChangeLog
inc/auth.php
inc/code.php
inc/config.php
inc/database.php
inc/dictionary.php
inc/exceptions.php
inc/functions.php
inc/interface.php
inc/navigation.php
inc/ophandlers.php
inc/snmp.php
install/init-structure.sql
js/racktables.js
pi.css
upgrade.php

index 757f7ee..2b6293e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,9 +3,18 @@
        bugfix: speed up IPv4 VLAN selector (by Boris Lytochkin)
        bugfix: suppress inputs borders in MSIE (by Alexey Andrianov)
        bugfix: justify table alignment in rack view (#367)
+       bugfix: handle locking failure in LDAP caching code
+       bugfix: enable deletion of initialized 802.1Q switch
        update: allow scrollbars in port selector (#361)
+       update: make IPv4 utilization bar a standard element (by Alexey Andrianov)
+       update: make 802.1Q VLANs searchable
+       update: another round of SNMP enhancements
+       update: tag "quick list" redesign (by Alexey Andrianov)
+       update: disable knight button for a filtered IPv4 tree view
        new feature: enable IMS caching of progress bars (by Alexey Andrianov)
        new feature: rebuild tag filter as soon as user changes it (by Alexey Andrianov)
+       new feature: default SLB configuration lines stored in DB (by Alexey Andrianov)
+       new feature: {$attr_X_Y} autotags (by Alexey Andrianov)
 0.18.4 2010-07-13
        bugfix: a race condition could be triggered in permissions editor
        new feature: "any mode" of user port in VLAN switch template
index 0932176..8e96dd1 100644 (file)
@@ -155,7 +155,18 @@ function authenticated_via_ldap ($username, $password, &$ldap_displayname)
        }
        if ($LDAP_options['cache_expiry'] == 0) // immediate expiry set means disabled cache
                return authenticated_via_ldap_nocache ($username, $password, $ldap_displayname);
-       return authenticated_via_ldap_cache ($username, $password, $ldap_displayname);
+       // authenticated_via_ldap_cache()'s way of locking can sometimes result in
+       // a PDO error condition, which convertPDOException() was not able to dispatch.
+       // To avoid reaching printPDOException() (which prints backtrace with password
+       // argument in cleartext), any remaining PDO condition is converted locally.
+       try
+       {
+               return authenticated_via_ldap_cache ($username, $password, $ldap_displayname);
+       }
+       catch (PDOException $e)
+       {
+               throw new RackTablesError ('LDAP caching error', RackTablesError::DB_WRITE_FAILED);
+       }
 }
 
 // Authenticate given user with known LDAP server, completely ignore LDAP cache data.
index 36c1ef8..6389a8a 100644 (file)
@@ -1219,6 +1219,7 @@ function findAutoTagWarnings ($expr)
                                case (preg_match ('/^\$(fromvlan|tovlan)_[[:digit:]]+$/', $expr['load'])):
                                case (preg_match ('/^\$(unmounted|untagged|no_asset_tag|runs_8021Q)$/', $expr['load'])):
                                case (preg_match ('/^\$masklen_(eq|le|ge)_[[:digit:]][[:digit:]]?$/', $expr['load'])):
+                               case (preg_match ('/^\$attr_\d+_\d+$/', $expr['load'])):
                                        return array();
                                default:
                                        return array (array
index 5345f06..3ba269e 100644 (file)
@@ -34,6 +34,7 @@ $max_dict_key = array
        '0.18.2' => 1352,
        '0.18.3' => 1356,
        '0.18.4' => 1364,
+       '0.18.5' => 1367,
 );
 
 define ('TAGNAME_REGEXP', '/^[\p{L}0-9]([. _~-]?[\p{L}0-9])*$/u');
index c5a2c83..507b72a 100644 (file)
@@ -155,6 +155,8 @@ $tablemap_8021q = array
        ),
 );
 
+$object_attribute_cache = array();
+
 function escapeString ($value, $do_db_escape = FALSE)
 {
        $ret = htmlspecialchars ($value, ENT_QUOTES, 'UTF-8');
@@ -241,12 +243,13 @@ function listCells ($realm, $parent_id = 0)
        if (!isset ($SQLSchema[$realm]))
                throw new InvalidArgException ('realm', $realm);
        $SQLinfo = $SQLSchema[$realm];
-       $qparams = array ($realm);
-       $query = 'SELECT tag_id';
+       $qparams = array ();
+       $query = 'SELECT ';
        foreach ($SQLinfo['columns'] as $alias => $expression)
                // Automatically prepend table name to each single column, but leave all others intact.
-               $query .= ', ' . ($alias == $expression ? "${SQLinfo['table']}.${alias}" : "${expression} as ${alias}");
-       $query .= " FROM ${SQLinfo['table']} LEFT JOIN TagStorage on entity_realm = ? and entity_id = ${SQLinfo['table']}.${SQLinfo['keycolumn']}";
+               $query .= ($alias == $expression ? "${SQLinfo['table']}.${alias}" : "${expression} as ${alias}") . ', ';
+       $query = trim($query, ', ');
+       $query .= " FROM ${SQLinfo['table']}";
        if (isset ($SQLinfo['pidcolumn']) and $parent_id)
        {
                $query .= " WHERE ${SQLinfo['table']}.${SQLinfo['pidcolumn']} = ?";
@@ -255,32 +258,27 @@ function listCells ($realm, $parent_id = 0)
        $query .= " ORDER BY ";
        foreach ($SQLinfo['ordcolumns'] as $oc)
                $query .= "${oc}, ";
-       $query .= " tag_id";
+       $query = trim($query, ', ');
        $result = usePreparedSelectBlade ($query, $qparams);
        $ret = array();
-       global $taglist;
        // Index returned result by the value of key column.
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
                $entity_id = $row[$SQLinfo['keycolumn']];
-               // Init the first record anyway, but store tag only if there is one.
-               if (!isset ($ret[$entity_id]))
-               {
-                       $ret[$entity_id] = array ('realm' => $realm);
-                       foreach (array_keys ($SQLinfo['columns']) as $alias)
-                               $ret[$entity_id][$alias] = $row[$alias];
-                       $ret[$entity_id]['etags'] = array();
-                       if ($row['tag_id'] != NULL && isset ($taglist[$row['tag_id']]))
-                               $ret[$entity_id]['etags'][] = array
-                               (
-                                       'id' => $row['tag_id'],
-                                       'tag' => $taglist[$row['tag_id']]['tag'],
-                                       'parent_id' => $taglist[$row['tag_id']]['parent_id'],
-                               );
-               }
-               elseif (isset ($taglist[$row['tag_id']]))
-                       // Meeting existing key later is always more tags on existing list.
-                       $ret[$entity_id]['etags'][] = array
+               $ret[$entity_id] = array ('realm' => $realm);
+               $ret[$entity_id]['etags'] = array();
+               foreach (array_keys ($SQLinfo['columns']) as $alias)
+                       $ret[$entity_id][$alias] = $row[$alias];
+       }
+
+       // select tags and link them to previosly fetched entities
+       $query = 'SELECT entity_id, tag_id FROM TagStorage WHERE entity_realm = ?';
+       $result = usePreparedSelectBlade ($query, array($realm));
+       global $taglist;
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+       {
+               if (array_key_exists($row['entity_id'], $ret))
+                       $ret[$row['entity_id']]['etags'][] = array
                        (
                                'id' => $row['tag_id'],
                                'tag' => $taglist[$row['tag_id']]['tag'],
@@ -290,6 +288,8 @@ function listCells ($realm, $parent_id = 0)
        // Add necessary finish to the list before returning it. Maintain caches.
        if (!$parent_id)
                unset ($entityCache['partial'][$realm]);
+       if ($realm == 'object') // cache all attributes of all objects to speed up autotags calculation
+               cacheAllObjectsAttributes();
        foreach (array_keys ($ret) as $entity_id)
        {
                $ret[$entity_id]['etags'] = getExplicitTagsOnly ($ret[$entity_id]['etags']);
@@ -409,7 +409,7 @@ function amplifyCell (&$record, $dummy = NULL)
                break;
        case 'ipv4rspool':
                $record['lblist'] = array();
-               $query = "select object_id, vs_id, lb.vsconfig, lb.rsconfig from " .
+               $query = "select object_id, vs_id, lb.vsconfig, lb.rsconfig, lb.prio from " .
                        "IPv4LB as lb inner join IPv4VS as vs on lb.vs_id = vs.id " .
                        "where rspool_id = ? order by object_id, vip, vport";
                $result = usePreparedSelectBlade ($query, array ($record['id']));
@@ -418,6 +418,7 @@ function amplifyCell (&$record, $dummy = NULL)
                        (
                                'rsconfig' => $row['rsconfig'],
                                'vsconfig' => $row['vsconfig'],
+                               'prio' => $row['prio'],
                        );
                unset ($result);
                $record['rslist'] = array();
@@ -440,7 +441,7 @@ function amplifyCell (&$record, $dummy = NULL)
                // will be returned as well.
                $record['rspool'] = array();
                $query = "select pool.id, name, pool.vsconfig, pool.rsconfig, object_id, " .
-                       "lb.vsconfig as lb_vsconfig, lb.rsconfig as lb_rsconfig from " .
+                       "lb.vsconfig as lb_vsconfig, lb.rsconfig as lb_rsconfig, lb.prio from " .
                        "IPv4RSPool as pool left join IPv4LB as lb on pool.id = lb.rspool_id " .
                        "where vs_id = ? order by pool.name, object_id";
                $result = usePreparedSelectBlade ($query, array ($record['id']));
@@ -460,6 +461,7 @@ function amplifyCell (&$record, $dummy = NULL)
                        (
                                'vsconfig' => $row['lb_vsconfig'],
                                'rsconfig' => $row['lb_rsconfig'],
+                               'prio' => $row['prio'],
                        );
                }
                unset ($result);
@@ -630,6 +632,8 @@ function commitDeleteObject ($object_id = 0)
        usePreparedDeleteBlade ('IPv4NAT', array ('object_id' => $object_id));
        usePreparedExecuteBlade ('DELETE FROM Atom WHERE molecule_id IN (SELECT new_molecule_id FROM MountOperation WHERE object_id = ?)', array ($object_id));
        usePreparedExecuteBlade ('DELETE FROM Molecule WHERE id IN (SELECT new_molecule_id FROM MountOperation WHERE object_id = ?)', array ($object_id));
+       usePreparedDeleteBlade ('PortVLANMode', array ('object_id' => $object_id));
+       usePreparedDeleteBlade ('VLANSwitch', array ('object_id' => $object_id));
        usePreparedDeleteBlade ('RackObject', array ('id' => $object_id));
        return '';
 }
@@ -1432,6 +1436,40 @@ function getRackSearchResult ($terms)
        return $ret;
 }
 
+function getVLANSearchResult ($terms)
+{
+       $ret = array();
+       $matches = array();
+       if (preg_match ('/^vlan([[:digit:]]+)$/i', $terms, $matches))
+       {
+               $byID = getSearchResultByField
+               (
+                       'VLANDescription',
+                       array ('domain_id', 'vlan_id'),
+                       'vlan_id',
+                       $matches[1],
+                       'domain_id',
+                       1
+               );
+               foreach ($byID as $row)
+                       $ret[] = $row['domain_id'] . '-' . $row['vlan_id'];
+       }
+       $byDescr = getSearchResultByField
+       (
+               'VLANDescription',
+               array ('domain_id', 'vlan_id'),
+               'vlan_descr',
+               $terms
+       );
+       foreach ($byDescr as $row)
+       {
+               $vlan_ck = $row['domain_id'] . '-' . $row['vlan_id'];
+               if (!in_array ($vlan_ck, $ret))
+                       $ret[] = $vlan_ck;
+       }
+       return $ret;
+}
+
 function getSearchResultByField ($tname, $rcolumns, $scolumn, $terms, $ocolumn = '', $exactness = 0)
 {
        $pfx = '';
@@ -2016,26 +2054,44 @@ function commitReduceAttrMap ($attr_id = 0, $objtype_id)
        return usePreparedDeleteBlade ('AttributeMap', array ('attr_id' => $attr_id, 'objtype_id' => $objtype_id));
 }
 
-// This function returns all optional attributes for requested object
-// as an array of records. NULL is returned on error and empty array
-// is returned, if there are no attributes found.
-function getAttrValues ($object_id)
+function cacheAllObjectsAttributes()
+{
+       global $object_attribute_cache;
+       $object_attribute_cache = fetchAttrsForObjects(NULL);
+}
+
+// Fetches a list of attributes for each object in $object_set array.
+// If $object_set is not set, returns attributes for all objects in DB
+// Returns an array with object_id keys
+function fetchAttrsForObjects($object_set)
 {
        $ret = array();
-       $result = usePreparedSelectBlade
-       (
-               "select A.id as attr_id, A.name as attr_name, A.type as attr_type, C.name as chapter_name, " .
-               "C.id as chapter_id, AV.uint_value, AV.float_value, AV.string_value, D.dict_value from " .
-               "RackObject as RO inner join AttributeMap as AM on RO.objtype_id = AM.objtype_id " .
-               "inner join Attribute as A on AM.attr_id = A.id " .
+       $query =
+               "select AV.attr_id, A.name as attr_name, A.type as attr_type, C.name as chapter_name, " .
+               "C.id as chapter_id, AV.uint_value, AV.float_value, AV.string_value, D.dict_value, RO.id as object_id from " .
+               "RackObject as RO left join AttributeMap as AM on RO.objtype_id = AM.objtype_id " .
+               "left join Attribute as A on AM.attr_id = A.id " .
                "left join AttributeValue as AV on AV.attr_id = AM.attr_id and AV.object_id = RO.id " .
                "left join Dictionary as D on D.dict_key = AV.uint_value and AM.chapter_id = D.chapter_id " .
-               "left join Chapter as C on AM.chapter_id = C.id " .
-               "where RO.id = ? order by A.name, A.type",
-               array ($object_id)
-       );
+               "left join Chapter as C on AM.chapter_id = C.id";
+       if (is_array ($object_set) && !empty ($object_set))
+       {
+               $query .= ' WHERE RO.id IN (';
+               foreach ($object_set as $object_id)
+                       $query .= intval ($object_id) . ', ';
+               $query = trim ($query, ', ') . ')';
+       }
+
+       $result = usePreparedSelectBlade ($query);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
+               $object_id = $row['object_id'];
+               $attr_id = $row['attr_id'];
+               if (!array_key_exists ($object_id, $ret))
+                       $ret[$object_id] = array();
+               if (! isset($attr_id))
+                       continue;
+
                $record = array();
                $record['id'] = $row['attr_id'];
                $record['name'] = $row['attr_name'];
@@ -2058,12 +2114,33 @@ function getAttrValues ($object_id)
                                $record['value'] = NULL;
                                break;
                }
-               $ret[$row['attr_id']] = $record;
+               $ret[$object_id][$attr_id] = $record;
        }
        $result->closeCursor();
        return $ret;
 }
 
+// This function returns all optional attributes for requested object
+// as an array of records. NULL is returned on error and empty array
+// is returned, if there are no attributes found.
+function getAttrValues ($object_id)
+{
+       global $object_attribute_cache;
+       if (isset ($object_attribute_cache[$object_id]))
+               return $object_attribute_cache[$object_id];
+
+       $ret = fetchAttrsForObjects(array($object_id));
+       $attrs = array();
+       if (empty($ret))
+               return NULL;
+       else
+       {
+               $attrs = $ret[$object_id];
+               $object_attribute_cache[$object_id] = $attrs;
+               return $attrs;
+       }
+}
+
 function commitResetAttrValue ($object_id = 0, $attr_id = 0)
 {
        return usePreparedDeleteBlade ('AttributeValue', array ('object_id' => $object_id, 'attr_id' => $attr_id));
@@ -2423,15 +2500,16 @@ function commitUpdateRS ($rsid = 0, $rsip = '', $rsport = 0, $rsconfig = '')
        );
 }
 
-function commitUpdateLB ($object_id = 0, $pool_id = 0, $vs_id = 0, $vsconfig = '', $rsconfig = '')
+function commitUpdateLB ($object_id = 0, $pool_id = 0, $vs_id = 0, $vsconfig = '', $rsconfig = '', $prio = '')
 {
        return usePreparedExecuteBlade
        (
-               'UPDATE IPv4LB SET vsconfig=?, rsconfig=? WHERE object_id=? AND rspool_id=? AND vs_id=?',
+               'UPDATE IPv4LB SET vsconfig=?, rsconfig=?, prio=? WHERE object_id=? AND rspool_id=? AND vs_id=?',
                array
                (
                        !strlen ($vsconfig) ? NULL : $vsconfig,
                        !strlen ($rsconfig) ? NULL : $rsconfig,
+                       !strlen ($prio) ? NULL : $prio,
                        $object_id,
                        $pool_id,
                        $vs_id,
@@ -2494,7 +2572,7 @@ function getRSPoolsForObject ($object_id = 0)
        $result = usePreparedSelectBlade
        (
                'select vs_id, inet_ntoa(vip) as vip, vport, proto, vs.name, pool.id as pool_id, ' .
-               'pool.name as pool_name, count(rsip) as rscount, lb.vsconfig, lb.rsconfig from ' .
+               'pool.name as pool_name, count(rsip) as rscount, lb.vsconfig, lb.rsconfig, lb.prio from ' .
                'IPv4LB as lb inner join IPv4RSPool as pool on lb.rspool_id = pool.id ' .
                'inner join IPv4VS as vs on lb.vs_id = vs.id ' .
                'left join IPv4RS as rs on lb.rspool_id = rs.rspool_id ' .
@@ -2504,7 +2582,7 @@ function getRSPoolsForObject ($object_id = 0)
        );
        $ret = array ();
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               foreach (array ('vip', 'vport', 'proto', 'name', 'pool_id', 'pool_name', 'rscount', 'vsconfig', 'rsconfig') as $cname)
+               foreach (array ('vip', 'vport', 'proto', 'name', 'pool_id', 'pool_name', 'rscount', 'vsconfig', 'rsconfig', 'prio') as $cname)
                        $ret[$row['vs_id']][$cname] = $row[$cname];
        return $ret;
 }
@@ -2587,7 +2665,7 @@ function getSLBConfig ($object_id)
        (
                'select vs_id, inet_ntoa(vip) as vip, vport, proto, vs.name as vs_name, ' .
                'vs.vsconfig as vs_vsconfig, vs.rsconfig as vs_rsconfig, ' .
-               'lb.vsconfig as lb_vsconfig, lb.rsconfig as lb_rsconfig, pool.id as pool_id, pool.name as pool_name, ' .
+               'lb.vsconfig as lb_vsconfig, lb.rsconfig as lb_rsconfig, lb.prio as prio, pool.id as pool_id, pool.name as pool_name, ' .
                'pool.vsconfig as pool_vsconfig, pool.rsconfig as pool_rsconfig, ' .
                'rs.id as rs_id, inet_ntoa(rsip) as rsip, rsport, rs.rsconfig as rs_rsconfig from ' .
                'IPv4LB as lb inner join IPv4RSPool as pool on lb.rspool_id = pool.id ' .
@@ -2602,7 +2680,7 @@ function getSLBConfig ($object_id)
                $vs_id = $row['vs_id'];
                if (!isset ($ret[$vs_id]))
                {
-                       foreach (array ('vip', 'vport', 'proto', 'vs_name', 'vs_vsconfig', 'vs_rsconfig', 'lb_vsconfig', 'lb_rsconfig', 'pool_vsconfig', 'pool_rsconfig', 'pool_id', 'pool_name') as $c)
+                       foreach (array ('vip', 'vport', 'proto', 'vs_name', 'vs_vsconfig', 'vs_rsconfig', 'lb_vsconfig', 'lb_rsconfig', 'pool_vsconfig', 'pool_rsconfig', 'pool_id', 'pool_name', 'prio') as $c)
                                $ret[$vs_id][$c] = $row[$c];
                        $ret[$vs_id]['rslist'] = array();
                }
@@ -2612,6 +2690,25 @@ function getSLBConfig ($object_id)
        return $ret;
 }
 
+function commitUpdateSLBDefConf ($data)
+{
+       return saveScript('DefaultVSConfig', $data['vs']) &&
+               saveScript('DefaultRSConfig', $data['rs']);
+}
+
+function getSLBDefaults ($do_cache_result = FALSE) {
+       static $ret = array();
+
+       if (! $do_cache_result)
+               $ret = array();
+       elseif (! empty ($ret))
+               return $ret;
+
+       $ret['vs'] = loadScript('DefaultVSConfig');
+       $ret['rs'] = loadScript('DefaultRSConfig');
+       return $ret;
+}
+
 function commitSetInService ($rs_id = 0, $inservice = '')
 {
        if (!strlen ($inservice))
@@ -3303,6 +3400,10 @@ function touchLDAPCacheRecord ($form_username)
 
 function replaceLDAPCacheRecord ($form_username, $password_hash, $dname, $memberof)
 {
+       // FIXME: This sequence is able to trigger a deadlock, namely, when executed
+       // in parallel from multiple working copies of the same user, which for some
+       // reason has no valid record in LDAPCache. Perhaps, using REPLACE INTO can
+       // lower the chances of this.
        deleteLDAPCacheRecord ($form_username);
        usePreparedInsertBlade ('LDAPCache',
                array
index 7eb19a1..163e7c2 100644 (file)
@@ -1442,6 +1442,9 @@ $dictionary = array
        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'),
+       1365 => array ('chapter_id' => 14, 'dict_value' => 'Cisco NX-OS 4.2'),
+       1366 => array ('chapter_id' => 14, 'dict_value' => 'JunOS 9'),
+       1367 => array ('chapter_id' => 14, 'dict_value' => 'JunOS 10'),
 );
 
 ?>
index ce84579..72964a7 100644 (file)
@@ -115,6 +115,26 @@ class RTGatewayError extends RackTablesError
        }
 }
 
+class RTBuildLVSConfigError extends RackTablesError
+{
+       public $message_list;
+       public $config_to_display;
+       public $balancer_id;
+       function __construct($message_list, $config_to_display, $object_id) {
+               $this->code = parent::INTERNAL;
+               $this->message_list = $message_list;
+               $this->config_to_display = $config_to_display;
+               $this->balancer_id = $object_id;
+               parent::__construct("LVS config build error for balancer $object_id: " . implode("\n", $message_list));
+       }
+       public function dispatch()
+       {
+               // redirect user to a page with config errors highlighted
+               header ("Location: index.php?page=object&tab=lvsconfig&object_id=" . urlencode ($this->balancer_id));
+               die;
+       }
+}
+
 function dumpArray($arr)
 {
        echo '<table class="exceptionParametersDump">';
index b2b73f0..e334fa5 100644 (file)
@@ -988,6 +988,12 @@ function generateEntityAutoTags ($cell)
                                $ret[] = array ('tag' => '$no_asset_tag');
                        if ($cell['runs8021Q'])
                                $ret[] = array ('tag' => '$runs_8021Q');
+
+                       // dictionary attribute autotags '$attr_X_Y'
+                       $attrs = getAttrValues($cell['id']);
+                       foreach ($attrs as $attr_id => $attr_record)
+                               if (isset($attr_record['chapter_id']))
+                                       $ret[] = array ('tag' => "\$attr_{$attr_id}_{$attr_record['key']}");
                        break;
                case 'ipv4net':
                        $ret[] = array ('tag' => '$ip4netid_' . $cell['id']);
@@ -1509,14 +1515,42 @@ function getRackImageHeight ($units)
 
 // Perform substitutions and return resulting string
 // used solely by buildLVSConfig()
-function apply_macros ($macros, $subject)
+function apply_macros ($macros, $subject, &$error_macro_stat)
 {
-       $ret = $subject;
+       // clear all text before last %RESET% macro
+       $reset_keyword = '%RESET%';
+       $reset_position = mb_strpos($subject, $reset_keyword, 0);
+       if ($reset_position === FALSE)
+               $ret = $subject;
+       else
+               $ret = trim
+               (
+                       mb_substr($subject, $reset_position + mb_strlen($reset_keyword)),
+                       "\n\r"
+               );
+
        foreach ($macros as $search => $replace)
-               $ret = str_replace ($search, $replace, $ret);
+       {
+               if (empty($replace))
+               {
+                       $replace = "<span class=\"msg_error\">$search</span>";
+                       $count = 0;
+                       $ret = str_replace ($search, $replace, $ret, $count);
+                       if ($count)
+                       {
+                               if (array_key_exists($search, $error_macro_stat))
+                                       $error_macro_stat[$search] += $count;
+                               else
+                                       $error_macro_stat[$search] = $count;
+                       }
+               }
+               else
+                       $ret = str_replace ($search, $replace, $ret);
+       }
        return $ret;
 }
 
+// throws RTBuildLVSConfigError exception if undefined macros found
 function buildLVSConfig ($object_id = 0)
 {
        if ($object_id <= 0)
@@ -1525,6 +1559,7 @@ function buildLVSConfig ($object_id = 0)
                return;
        }
        $oInfo = spotEntity ('object', $object_id);
+       $defaults = getSLBDefaults (TRUE);
        $lbconfig = getSLBConfig ($object_id);
        if ($lbconfig === NULL)
        {
@@ -1533,6 +1568,8 @@ function buildLVSConfig ($object_id = 0)
        }
        $newconfig = "#\n#\n# This configuration has been generated automatically by RackTables\n";
        $newconfig .= "# for object_id == ${object_id}\n# object name: ${oInfo['name']}\n#\n#\n\n\n";
+       
+       $error_stat = array();
        foreach ($lbconfig as $vs_id => $vsinfo)
        {
                $newconfig .=  "########################################################\n" .
@@ -1546,17 +1583,20 @@ function buildLVSConfig ($object_id = 0)
                        '%VPORT%' => $vsinfo['vport'],
                        '%PROTO%' => $vsinfo['proto'],
                        '%VNAME%' =>  $vsinfo['vs_name'],
-                       '%RSPOOLNAME%' => $vsinfo['pool_name']
+                       '%RSPOOLNAME%' => $vsinfo['pool_name'],
+                       '%PRIO%' => $vsinfo['prio']
                );
                $newconfig .=  "virtual_server ${vsinfo['vip']} ${vsinfo['vport']} {\n";
                $newconfig .=  "\tprotocol ${vsinfo['proto']}\n";
-               $newconfig .= apply_macros
+               $newconfig .= lf_wrap (apply_macros
                (
                        $macros,
+                       lf_wrap ($defaults['vs']) .
                        lf_wrap ($vsinfo['vs_vsconfig']) .
                        lf_wrap ($vsinfo['lb_vsconfig']) .
-                       lf_wrap ($vsinfo['pool_vsconfig'])
-               );
+                       lf_wrap ($vsinfo['pool_vsconfig']),
+                       $error_stat
+               ));
                foreach ($vsinfo['rslist'] as $rs)
                {
                        if (!strlen ($rs['rsport']))
@@ -1564,18 +1604,28 @@ function buildLVSConfig ($object_id = 0)
                        $macros['%RSIP%'] = $rs['rsip'];
                        $macros['%RSPORT%'] = $rs['rsport'];
                        $newconfig .=  "\treal_server ${rs['rsip']} ${rs['rsport']} {\n";
-                       $newconfig .= apply_macros
+                       $newconfig .= lf_wrap (apply_macros
                        (
                                $macros,
+                               lf_wrap ($defaults['rs']) .
                                lf_wrap ($vsinfo['vs_rsconfig']) .
                                lf_wrap ($vsinfo['lb_rsconfig']) .
                                lf_wrap ($vsinfo['pool_rsconfig']) .
-                               lf_wrap ($rs['rs_rsconfig'])
-                       );
+                               lf_wrap ($rs['rs_rsconfig']),
+                               $error_stat
+                       ));
                        $newconfig .=  "\t}\n";
                }
                $newconfig .=  "}\n\n\n";
        }
+       if (! empty($error_stat))
+       {
+               $error_messages = array();
+               foreach ($error_stat as $macro => $count)
+                       $error_messages[] = "Error: macro $macro can not be empty ($count occurences)";
+               throw new RTBuildLVSConfigError($error_messages, $newconfig, $object_id);
+       }
+       
        // FIXME: deal somehow with Mac-styled text, the below replacement will screw it up
        return dos2unix ($newconfig);
 }
@@ -2402,7 +2452,12 @@ function formatVLANName ($vlaninfo, $context = 'markup long')
        case 'plain long':
                $ret = 'VLAN' . $vlaninfo['vlan_id'];
                if ($vlaninfo['vlan_descr'] != '')
-                       $ret .= ' (' . niftyString ($vlaninfo['vlan_descr']) . ')';
+                       $ret .= ' (' . niftyString ($vlaninfo['vlan_descr'], 20, FALSE) . ')';
+               return $ret;
+       case 'hyperlink':
+               $ret = '<a href="';
+               $ret .= makeHref (array ('page' => 'vlan', 'vlan_ck' => $vlaninfo['domain_id'] . '-' . $vlaninfo['vlan_id']));
+               $ret .= '">' . formatVLANName ($vlaninfo, 'markup long') . '</a>';
                return $ret;
        case 'markup long':
        default:
@@ -3472,7 +3527,7 @@ function sortPortList ($plist)
 // This is a dual-purpose formating function:
 // 1. Replace empty strings with nbsp.
 // 2. Cut strings, which are too long, append "cut here" indicator and provide a mouse hint.
-function niftyString ($string, $maxlen = 30)
+function niftyString ($string, $maxlen = 30, $usetags = TRUE)
 {
        $cutind = '&hellip;'; // length is 1
        if (!mb_strlen ($string))
@@ -3481,8 +3536,11 @@ function niftyString ($string, $maxlen = 30)
        $string = preg_replace ("/\t/", ' ', $string);
        if (!$maxlen or mb_strlen ($string) <= $maxlen)
                return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8');
-       return "<span title='" . htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . "'>" .
-               str_replace (' ', '&nbsp;', htmlspecialchars (mb_substr ($string, 0, $maxlen - 1), ENT_QUOTES, 'UTF-8')) . $cutind . '</span>';
+       return
+               ($usetags ? ("<span title='" . htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . "'>") : '') .
+               str_replace (' ', '&nbsp;', htmlspecialchars (mb_substr ($string, 0, $maxlen - 1), ENT_QUOTES, 'UTF-8')) .
+               $cutind .
+               ($usetags ? '</span>' : '');
 }
 
 // return a "?, ?, ?, ... ?, ?" string consisting of N question marks
index 33dec69..3547bc9 100644 (file)
@@ -60,6 +60,17 @@ $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';
@@ -1134,7 +1145,7 @@ function renderRackObject ($object_id)
                $order = 'odd';
                startPortlet ('Real server pools (' . count ($pools) . ')');
                echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-               echo "<tr><th>VS</th><th>RS pool</th><th>RS</th><th>VS config</th><th>RS config</th></tr>\n";
+               echo "<tr><th>VS</th><th>RS pool</th><th>RS</th><th>VS config</th><th>RS config</th><th>Prio</th></tr>\n";
                foreach ($pools as $vs_id => $info)
                {
                        echo "<tr valign=top class=row_${order}><td class=tdleft>";
@@ -1144,6 +1155,7 @@ function renderRackObject ($object_id)
                        echo '</td><td class=tdleft>' . $info['rscount'] . '</td>';
                        echo "<td class=slbconf>${info['vsconfig']}</td>";
                        echo "<td class=slbconf>${info['rsconfig']}</td>";
+                       echo "<td class=slbconf>${info['prio']}</td>";
                        echo "</tr>\n";
                        $order = $nextorder[$order];
                }
@@ -2048,7 +2060,7 @@ function renderRackspaceHistory ()
        echo '</td></tr></table>';
 }
 
-function renderIPv4SpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
+function renderIPv4SpaceRecords ($tree, $baseurl, $target = 0, $knight, $level = 1)
 {
        $self = __FUNCTION__;
        static $vdomlist = NULL;
@@ -2068,14 +2080,13 @@ function renderIPv4SpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
                $maxtotal = binInvMaskFromDec ($item['mask']) + 1;
                if (isset ($item['id']))
                {
+                       $decor = array ('indent' => $level);
                        if ($item['symbol'] == 'node-collapsed')
-                               $expandurl = "${baseurl}&eid=" . $item['id'] . "#netid" . $item['id'];
+                               $decor['symbolurl'] = "${baseurl}&eid=" . $item['id'] . "#netid" . $item['id'];
                        elseif ($item['symbol'] == 'node-expanded')
-                               $expandurl = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}#netid${item['parent_id']}" : '');
-                       else
-                               $expandurl = '';
+                               $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}#netid${item['parent_id']}" : '');
                        echo "<tr valign=top>";
-                       printIPv4NetInfoTDs ($item, 'tdleft', $level, $item['symbol'], $expandurl);
+                       printIPv4NetInfoTDs ($item, $decor);
                        echo "<td class=tdcenter>";
                        if ($target == $item['id'])
                                echo "<a name=netid${target}></a>";
@@ -2107,12 +2118,12 @@ function renderIPv4SpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
                                printRoutersTD (findRouters ($item['addrlist']), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
                        echo "</tr>";
                        if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
-                               $self ($item['kids'], $baseurl, $target, $level + 1);
+                               $self ($item['kids'], $baseurl, $target, $knight, $level + 1);
                }
                else
                {
                        echo "<tr valign=top>";
-                       printIPv4NetInfoTDs ($item, 'tdleft sparenetwork', $level, $item['symbol']);
+                       printIPv4NetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
                        echo "<td class=tdcenter>";
                        if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
                        {
@@ -2132,7 +2143,9 @@ function renderIPv4Space ()
 {
        global $pageno, $tabno;
        $cellfilter = getCellFilter();
-       $netlist = filterCellList (listCells ('ipv4net'), $cellfilter['expression']);
+       $netlist = listCells ('ipv4net');
+       $allcount = count ($netlist);
+       $netlist = filterCellList ($netlist, $cellfilter['expression']);
        array_walk ($netlist, 'amplifyCell');
 
        $netcount = count ($netlist);
@@ -2168,7 +2181,7 @@ function renderIPv4Space ()
                echo "<th>routed by</th>";
        echo "</tr>\n";
        $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
-       renderIPv4SpaceRecords ($tree, $baseurl, $eid);
+       renderIPv4SpaceRecords ($tree, $baseurl, $eid, $netcount == $allcount and getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
        echo "</table>\n";
        finishPortlet();
        echo '</td><td class=pcright>';
@@ -2176,6 +2189,21 @@ function renderIPv4Space ()
        echo "</td></tr></table>\n";
 }
 
+function renderSLBDefConfig()
+{
+       $defaults = getSLBDefaults ();
+       startPortlet ('SLB default configs');
+       echo '<table cellspacing=0 cellpadding=5 align=center>';
+       printOpFormIntro ('save');
+       echo '<tr><th class=tdright>VS config</th><td colspan=2><textarea tabindex=103 name=vsconfig rows=10 cols=80>' . htmlspecialchars($defaults['vs']) . '</textarea></td>';
+       echo '<td rowspan=2>';
+       printImageHREF ('SAVE', 'Save changes', TRUE);
+       echo '</td></tr>';
+       echo '<tr><th class=tdright>RS config</th><td colspan=2><textarea tabindex=104 name=rsconfig rows=10 cols=80>' . htmlspecialchars($defaults['rs']) . '</textarea></td></tr>';
+       echo '</form></table>';
+       finishPortlet();
+}
+
 function renderIPv4SLB ()
 {
        global $page, $nextorder;
@@ -2320,7 +2348,10 @@ function renderIPv4SpaceEditor ()
                        }
                        echo '</td><td class=tdleft><a href="' . makeHref (array ('page' => 'ipv4net', 'id' => $netinfo['id'])) . '">';
                        echo "${netinfo['ip']}/${netinfo['mask']}</a></td>";
-                       echo '<td class=tdleft>' . htmlspecialchars ($netinfo['name']) . '</td><td>';
+                       echo '<td class=tdleft>' . niftyString ($netinfo['name']);
+                       if (count ($netinfo['etags']))
+                               echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
+                       echo '</td><td>';
                        renderProgressBar ($maxdirect ? $used/$maxdirect : 0);
                        echo "<br><small>${used}/${maxdirect}" . ($maxdirect == $maxtotal ? '' : "/${maxtotal}") . '</small></td>';
                        echo '</tr>';
@@ -3007,6 +3038,12 @@ function renderSearchResults ()
                        $lasthit = 'rack';
                        $summary['rack'] = $tmp;
                }
+               if (count ($tmp = getVLANSearchResult ($terms)))
+               {
+                       $nhits += count ($tmp);
+                       $lasthit = 'vlan';
+                       $summary['vlan'] = $tmp;
+               }
        }
        if ($nhits == 0)
                echo "<center><h2>Nothing found for '${terms}'</h2></center>";
@@ -3056,6 +3093,9 @@ function renderSearchResults ()
                        case 'rack':
                                echo "<script language='Javascript'>document.location='index.php?page=rack&rack_id=${record['id']}';//</script>";
                                break;
+                       case 'vlan':
+                               echo "<script language='Javascript'>document.location='index.php?page=vlan&vlan_ck=${record}';//</script>";
+                               break;
                }
                return;
        }
@@ -3233,6 +3273,18 @@ function renderSearchResults ()
                                        echo '</table>';
                                        finishPortlet();
                                        break;
+                               case 'vlan':
+                                       startPortlet ("<a href='index.php?page=8021q'>VLANs</a>");
+                                       echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
+                                       foreach ($what as $vlan_ck)
+                                       {
+                                               echo "<tr class=row_${order}><td class=tdleft>";
+                                               echo formatVLANName (getVLANInfo ($vlan_ck), 'hyperlink') . "</td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
                        }
        }
 }
@@ -4187,12 +4239,21 @@ function renderUIResetForm()
 function renderLVSConfig ($object_id)
 {
        echo '<br>';
-       printOpFormIntro ('submitSLBConfig');
-       echo "<center><input type=submit value='Submit for activation'></center>";
-       echo "</form>";
-       echo '<pre>';
-       echo buildLVSConfig ($object_id);
-       echo '</pre>';
+       try
+       {
+               $config = buildLVSConfig ($object_id);
+
+               printOpFormIntro ('submitSLBConfig');
+               echo "<center><input type=submit value='Submit for activation'></center>";
+               echo "</form>";
+       }
+       catch(RTBuildLVSConfigError $e)
+       {
+               $config = $e->config_to_display;
+               foreach ($e->message_list as $msg)
+                       echo '<div class="msg_error">' . $msg . '</div>';
+       }
+       echo "<pre>$config</pre>";
 }
 
 function renderVirtualService ($vsid)
@@ -4262,6 +4323,9 @@ function renderVirtualService ($vsid)
                                        echo "<tr><th>VS config</th><td class='dashed slbconf'>${lbInfo['vsconfig']}</td></tr>";
                                if (strlen ($lbInfo['rsconfig']))
                                        echo "<tr><th>RS config</th><td class='dashed slbconf'>${lbInfo['rsconfig']}</td></tr>";
+                               if (strlen ($lbInfo['prio']))
+                                       echo "<tr><th>Prio</th><td class='dashed slbconf'>${lbInfo['prio']}</td></tr>";
+
                        }
                        echo '</table>';
                }
@@ -4360,115 +4424,174 @@ function renderRSPoolLBForm ($pool_id)
 {
        $poolInfo = spotEntity ('ipv4rspool', $pool_id);
        amplifyCell ($poolInfo);
-
-       function printNewItemTR ()
-       {
-               startPortlet ('Add new');
-               echo "<table cellspacing=0 cellpadding=5 align=center>";
-               printOpFormIntro ('addLB');
-               echo "<tr valign=top><th class=tdright>Load balancer</th><td class=tdleft>";
-               printSelect (getNarrowObjectList ('IPV4LB_LISTSRC'), array ('name' => 'object_id', 'tabindex' => 1));
-               echo '</td><td class=tdcenter valign=middle rowspan=2>';
-               printImageHREF ('ADD', 'Configure LB', TRUE, 5);
-               echo '</td></tr><tr><th class=tdright>Virtual service</th><td class=tdleft>';
-               printSelect (getIPv4VSOptions(), array ('name' => 'vs_id', 'tabindex' => 2));
-               echo "</td></tr>\n";
-               echo "<tr><th class=tdright>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
-               echo "<tr><th class=tdright>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
-
-
-               echo "</form></table>\n";
-               finishPortlet();
-       }
-       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
-               printNewItemTR();
-       if (count ($poolInfo['lblist']))
+       $triplets = array();
+       $display_count = 0;
+       foreach ($poolInfo['lblist'] as $object_id => $vslist)
        {
-               startPortlet ('Manage existing (' . count ($poolInfo['lblist']) . ')');
-               echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
-               global $nextorder;
-               $order = 'odd';
-               foreach ($poolInfo['lblist'] as $object_id => $vslist)
-                       foreach ($vslist as $vs_id => $configs)
-                       {
-                               printOpFormIntro ('updLB', array ('vs_id' => $vs_id, 'object_id' => $object_id));
-                               echo "<tr valign=top class=row_${order}><td rowspan=2 class=tdright valign=middle><a href='".makeHrefProcess(array('op'=>'delLB', 'pool_id'=>$pool_id, 'object_id'=>$object_id, 'vs_id'=>$vs_id))."'>";
-                               printImageHREF ('DELETE', 'Unconfigure');
-                               echo "</a></td>";
-                               echo "<td class=tdleft valign=bottom>";
-                               renderLBCell ($object_id);
-                               echo "</td><td>VS config &darr;<br><textarea name=vsconfig rows=5 cols=70>${configs['vsconfig']}</textarea></td>";
-                               echo '<td class=tdleft rowspan=2 valign=middle>';
-                               printImageHREF ('SAVE', 'Save changes', TRUE);
-                               echo "</td></tr><tr class=row_${order}><td class=tdleft valign=top>";
-                               renderCell (spotEntity ('ipv4vs', $vs_id));
-                               echo '</td><td>';
-                               echo "<textarea name=rsconfig rows=5 cols=70>${configs['rsconfig']}</textarea><br>RS config &uarr;";
-                               echo '</td></tr></form>';
-                               $order = $nextorder[$order];
-                       }
-               echo "</table>\n";
-               finishPortlet();
+               ++$display_count;
+               foreach ($vslist as $vs_id => $configs)
+                       $triplets[] = array(
+                               'ids' => array(
+                                       object_id => $object_id,
+                                       vs_id => $vs_id,
+                                       pool_id => $pool_id
+                               ),
+                               'rsconfig' => $configs['rsconfig'],
+                               'vsconfig' => $configs['vsconfig'],
+                               'prio' => $configs['prio']
+                       );
        }
-       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
-               printNewItemTR();
+       renderSLBTriplets('object', 'ipv4vs', $triplets, $display_count);
 }
 
 function renderVServiceLBForm ($vs_id)
 {
-       global $nextorder;
        $vsinfo = spotEntity ('ipv4vs', $vs_id);
        amplifyCell ($vsinfo);
+       $triplets = array();
+       $display_count = 0;
+       foreach ($vsinfo['rspool'] as $pool_id => $rspinfo)
+       {
+               ++$display_count;
+               foreach ($rspinfo['lblist'] as $object_id => $configs)
+                       $triplets[] = array(
+                               'ids' => array(
+                                       object_id => $object_id,
+                                       vs_id => $vs_id,
+                                       pool_id => $pool_id
+                               ),
+                               'rsconfig' => $configs['rsconfig'],
+                               'vsconfig' => $configs['vsconfig'],
+                               'prio' => $configs['prio']
+                       );
+       }
+       renderSLBTriplets('object', 'ipv4rspool', $triplets, $display_count);
+}
 
-       function printNewItemTR ()
+function renderObjectSLB ($object_id)
+{
+       $focus = spotEntity ('object', $object_id);
+       amplifyCell ($focus);
+       $triplets = array();
+       foreach ($focus['ipv4rspools'] as $vs_id => $vsinfo)
+               $triplets[] = array(
+                       'ids' => array(
+                               object_id => $object_id,
+                               vs_id => $vs_id,
+                               pool_id => $vsinfo['pool_id']
+                       ),
+                       'rsconfig' => $vsinfo['rsconfig'],
+                       'vsconfig' => $vsinfo['vsconfig'],
+                       'prio' => $vsinfo['prio']
+               );
+       $display_count = count($triplets);
+       renderSLBTriplets('ipv4vs', 'ipv4rspool', $triplets, $display_count);
+}
+
+// called exclusively by renderSLBTriplets. Renders form to add new SLB link.
+// realms 1 and 2 are realms to draw inputs for
+function renderNewSLBItemForm ($realm1, $realm2)
+{
+       function print_realm_select_input($realm)
        {
-               startPortlet ('Add new');
-               echo '<table cellspacing=0 cellpadding=5 align=center>';
-               printOpFormIntro ('addLB');
-               echo '<tr valign=top><th class=tdright>Load balancer</th><td class=tdleft>';
-               printSelect (getNarrowObjectList ('IPV4LB_LISTSRC'), array ('name' => 'object_id', 'tabindex' => 101));
-               echo '</td><td rowspan=2 class=tdcenter valign=middle>';
-               printImageHREF ('ADD', 'Configure LB', TRUE, 105);
-               echo '</td></tr><tr><th class=tdright>RS pool</th><td class=tdleft>';
-               printSelect (getIPv4RSPoolOptions(), array ('name' => 'pool_id', 'tabindex' => 102));
-               echo '</td></tr>';
-               echo '<tr><th class=tdright>VS config</th><td colspan=2><textarea tabindex=103 name=vsconfig rows=10 cols=80></textarea></td></tr>';
-               echo '<tr><th class=tdright>RS config</th><td colspan=2><textarea tabindex=104 name=rsconfig rows=10 cols=80></textarea></td></tr>';
-               echo '</form></table>';
-               finishPortlet();
+               switch ($realm)
+               {
+                       case 'object':
+                               echo "<tr valign=top><th class=tdright>Load balancer</th><td class=tdleft>";
+                               printSelect (getNarrowObjectList ('IPV4LB_LISTSRC'), array ('name' => 'object_id', 'tabindex' => 1));
+                               break;
+                       case 'ipv4vs':
+                               echo '</td></tr><tr><th class=tdright>Virtual service</th><td class=tdleft>';
+                               printSelect (getIPv4VSOptions(), array ('name' => 'vs_id', 'tabindex' => 2));
+                               break;
+                       case 'ipv4rspool':
+                               echo '</td></tr><tr><th class=tdright>RS pool</th><td class=tdleft>';
+                               printSelect (getIPv4RSPoolOptions(), array ('name' => 'pool_id', 'tabindex' => 102));
+                               break;
+                       default:
+                               throw new InvalidArgException('realm', $realm);
+               }
        }
-       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
-               printNewItemTR();
-       if (count ($vsinfo['rspool']))
+
+       startPortlet ('Add new');
+       echo "<table cellspacing=0 cellpadding=5 align=center>";
+       printOpFormIntro ('addLB');
+       print_realm_select_input($realm1);
+       echo '</td><td class=tdcenter valign=middle rowspan=2>';
+       printImageHREF ('ADD', 'Configure LB', TRUE, 5);
+       print_realm_select_input($realm2);
+       echo "</td></tr>\n";
+       echo "<tr><th class=tdright>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th class=tdright>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th class=tdright>Priority</th><td class=tdleft colspan=2><input tabindex=5 name=prio size=10></td></tr>";
+       echo "</form></table>\n";
+       finishPortlet();
+}
+
+// renders a list of slb links. it is called from 3 different pages, wich compute their links lists differently.
+// each triplet in $triplets array contains balancer id, pool id, VS id and config values for triplet: RS, VS configs and pair.
+// realms 1 and 2 are needed to indicate the order of displaying cells in left column.
+// e.g. if we draw a RS pool page, realm1 should be set to 'object'(balancer), realm2 - to 'ipv4vs'
+function renderSLBTriplets ($realm1, $realm2, $triplets, $display_count) {
+       function get_object_id_by_realm($realm, $triplet)
        {
-               startPortlet ('Manage existing (' . count ($vsinfo['rspool']) . ')');
-               echo '<table cellspacing=0 cellpadding=5 align=center class=cooltable>';
+               switch ($realm) {
+                       case 'object':
+                               $key ='object_id';
+                               break;
+                       case 'ipv4vs':
+                               $key = 'vs_id';
+                               break;
+                       case 'ipv4rspool':
+                               $key = 'pool_id';
+                               break;
+                       default:
+                               throw new InvalidArgException('realm', $realm);
+               }
+               return $triplet['ids'][$key];
+       }
+
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               renderNewSLBItemForm($realm1, $realm2);
+
+       if (count($triplets)) {
+               startPortlet ('Manage existing (' . $display_count . ')');
+               echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
+
+               echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
+               global $nextorder;
                $order = 'odd';
-               foreach ($vsinfo['rspool'] as $pool_id => $rspinfo)
-                       foreach ($rspinfo['lblist'] as $object_id => $configs)
-                       {
-                               printOpFormIntro ('updLB', array ('pool_id' => $pool_id, 'object_id' => $object_id));
-                               echo "<tr valign=middle class=row_${order}><td rowspan=2>";
-                               echo "<a href='".makeHrefProcess(array('op'=>'delLB', 'pool_id'=>$pool_id, 'object_id'=>$object_id, 'vs_id'=>$vs_id))."'>";
-                               printImageHREF ('DELETE', 'Unconfigure');
-                               echo "</a></td>";
-                               echo '<td class=tdleft valign=bottom>';
-                               renderLBCell ($object_id);
-                               echo "</td><td>VS config &darr;<br><textarea name=vsconfig rows=5 cols=70>${configs['vsconfig']}</textarea></td>";
-                               echo '<td rowspan=2>';
-                               printImageHREF ('SAVE', 'Save changes', TRUE);
-                               echo '</td></tr>';
-                               echo "<tr class=row_${order}><td valign=top>";
-                               renderCell (spotEntity ('ipv4rspool', $pool_id));
-                               echo "</td><td><textarea name=rsconfig rows=5 cols=70>${configs['rsconfig']}</textarea><br>";
-                               echo 'RS config &uarr;</td></tr></form>';
-                               $order = $nextorder[$order];
-                       }
-               echo '</table>';
+               $i = 0;
+               foreach ($triplets as $slb)
+               {
+                       ++$i;
+                       $del_params = $slb['ids'];
+                       $del_params['op'] = 'delLB';
+                       printOpFormIntro ('updLB', $slb['ids']);
+                       echo "<tr valign=top class=row_${order}><td rowspan=2 class=tdright valign=middle><a href='".makeHrefProcess($del_params)."'>";
+                       printImageHREF ('DELETE', 'Unconfigure');
+                       echo "</a></td>";
+                       echo "<td class=tdleft valign=bottom>";
+                       renderSLBEntityCell ($realm1, get_object_id_by_realm($realm1, $slb));
+                       echo "</td><td>VS config &darr;<br><textarea name=vsconfig rows=5 cols=70>${slb['vsconfig']}</textarea></td>";
+                       echo '<td class=tdleft rowspan=2 valign=middle>';
+                       printImageHREF ('SAVE', 'Save changes', TRUE);
+                       echo "</td></tr><tr class=row_${order}><td class=tdleft valign=top>";
+                       renderSLBEntityCell ($realm2, get_object_id_by_realm($realm2, $slb));
+                       echo '</td><td>';
+                       echo "<textarea name=rsconfig rows=5 cols=70>${slb['rsconfig']}</textarea><br>RS config &uarr;";
+                       $prio_id = "prio-$i";
+                       $prio_value = htmlspecialchars($slb['prio']);
+                       echo "<div style='float:left; margin-top:10px'><input name=prio type=text size=10 id=\"$prio_id\" value=\"$prio_value\"><label for=\"$prio_id\"> &larr; Priority (integer)</label></div>";
+                       echo '</td></tr></form>';
+                       $order = $nextorder[$order];
+               }
+               echo "</table>\n";
                finishPortlet();
        }
+
        if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
-               printNewItemTR();
+               renderNewSLBItemForm($realm1, $realm2);
 }
 
 function renderRSPool ($pool_id)
@@ -4504,7 +4627,7 @@ function renderRSPool ($pool_id)
 
        startPortlet ('Load balancers (' . count ($poolInfo['lblist']) . ')');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>VS</th><th>LB</th><th>VS config</th><th>RS config</th></tr>";
+       echo "<tr><th>VS</th><th>LB</th><th>VS config</th><th>RS config</th><th>Prio</th></tr>";
        $order = 'odd';
        foreach ($poolInfo['lblist'] as $object_id => $vslist)
                foreach ($vslist as $vs_id => $configs)
@@ -4514,7 +4637,8 @@ function renderRSPool ($pool_id)
                echo "</td><td>";
                renderLBCell ($object_id);
                echo "</td><td class=slbconf>${configs['vsconfig']}</td>";
-               echo "<td class=slbconf>${configs['rsconfig']}</td></tr>\n";
+               echo "<td class=slbconf>${configs['rsconfig']}</td>\n";
+               echo "<td class=slbconf>${configs['prio']}</td></tr>\n";
                $order = $nextorder[$order];
        }
        echo "</table>\n";
@@ -4980,15 +5104,21 @@ function renderTagCheckbox ($inputname, $preselect, $taginfo, $refcnt_realm = ''
        if (tagOnChain ($taginfo, $preselect))
        {
                $selected = ' checked';
-               $class = 'seltagbox';
+               $td_class = 'seltagbox';
        }
        else
        {
                $selected = '';
-               $class = 'tagbox';
+               $td_class = 'tagbox';
        }
-       echo "<tr><td colspan=2 class=${class} style='padding-left: " . ($level * 16) . "px;'>";
-       echo "<label><input type=checkbox class='tag-cb' name='${inputname}[]' value='${taginfo['id']}'${selected}> ";
+       // calculate html classnames for separators feature 
+       static $is_first_time = TRUE;
+       $input_class = 'tag-cb' . ($level == 0 ? ' root' : '');
+       $tr_class = ($level == 0 && $taginfo['id'] > 0 && !$is_first_time ? 'separator' : '');
+       $is_first_time = FALSE;
+       
+       echo "<tr class='$tr_class'><td colspan=2 class='$td_class' style='padding-left: " . ($level * 16) . "px;'>";
+       echo "<label><input type=checkbox class='$input_class' name='${inputname}[]' value='${taginfo['id']}'${selected}> ";
        echo $taginfo['tag'];
        if (strlen ($refcnt_realm) and isset ($taginfo['refcnt'][$refcnt_realm]))
                echo ' <i>(' . $taginfo['refcnt'][$refcnt_realm] . ')</i>';
@@ -5001,7 +5131,8 @@ function renderTagCheckbox ($inputname, $preselect, $taginfo, $refcnt_realm = ''
 function renderEntityTagsPortlet ($title, $tags, $preselect, $realm)
 {
        startPortlet ($title);
-       echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
+       echo  '<a class="toggleTreeMode" style="display:none" href="#"></a>';
+       echo '<table border=0 cellspacing=0 cellpadding=3 align=center class="tagtree">';
        printOpFormIntro ('saveTags');
        foreach ($tags as $taginfo)
                renderTagCheckbox ('taglist', $preselect, $taginfo, $realm);
@@ -5031,15 +5162,22 @@ function renderEntityTags ($entity_id)
                // It could happen, that none of existing tags have been used in the current realm.
                if (count ($minilist))
                {
-                       echo '<td class=pcleft width="50%">';
-                       renderEntityTagsPortlet ('Quick list', $minilist, $target_given_tags, $etype_by_pageno[$pageno]);
-                       echo '</td>';
+                       $js_code = "<script type=\"text/javascript\">\ntagShortList = {";
+                       $is_first = TRUE;
+                       foreach($minilist as $tag) {
+                               if (! $is_first)
+                                       $js_code .= ",";
+                               $is_first = FALSE;
+                               $js_code .= "\n\t${tag['id']} : 1";
+                       }
+                       $js_code .= "\n};\n$(document).ready(compactTreeMode);\n</script>";
+                       echo $js_code;
                }
        }
 
        // do not do anything about empty tree, trigger function ought to work this out
        echo '<td class=pcright>';
-       renderEntityTagsPortlet ('Full tree', $tagtree, $target_given_tags, $etype_by_pageno[$pageno]);
+       renderEntityTagsPortlet ('Tag tree', $tagtree, $target_given_tags, $etype_by_pageno[$pageno]);
        echo '</td>';
 
        echo '</tr></table>';
@@ -5077,7 +5215,7 @@ function renderCellFilterPortlet ($preselect, $realm, $cell_list = array(), $byp
        $title = $filterc ? "filters (${filterc})" : 'filters';
        startPortlet ($title);
        echo "<form method=get>\n";
-       echo '<table border=0 align=center>';
+       echo '<table border=0 align=center cellspacing=0 class="tagtree">';
        $ruler = "<tr><td colspan=2 class=tagbox><hr></td></tr>\n";
        $hr = '';
        // "reset filter" button only gets active when a filter is applied
@@ -5209,7 +5347,7 @@ function renderNewEntityTags ($for_realm = '')
                echo "No tags defined";
                return;
        }
-       echo '<div class=tagselector><table border=0 align=center>';
+       echo '<div class=tagselector><table border=0 align=center cellspacing=0 class="tagtree">';
        foreach ($tagtree as $taginfo)
                renderTagCheckbox ('taglist', array(), $taginfo, $for_realm);
        echo '</table></div>';
@@ -5233,63 +5371,6 @@ function renderTagRollerForRow ($row_id)
        echo "</table></form>";
 }
 
-function renderObjectSLB ($object_id)
-{
-       function printNewItemTR ()
-       {
-               startPortlet ('Add new');
-               echo '<table cellspacing=0 cellpadding=5 align=center>';
-               printOpFormIntro ('addLB');
-               echo '<tr><th class=tdright>Virtual service</th><td class=tdleft>';
-               printSelect (getIPv4VSOptions(), array ('name' => 'vs_id', 'tabindex' => 101));
-               echo '</td><td class=tdcenter valign=middle rowspan=2>';
-               printImageHREF ('ADD', 'Configure LB', TRUE, 105);
-               echo '</td></tr>';
-               echo '</tr><th class=tdright>RS pool</th><td class=tdleft>';
-               printSelect (getIPv4RSPoolOptions(), array ('name' => 'pool_id', 'tabindex' => 102));
-               echo "</td></tr>";
-               echo '<tr><th class=tdright>VS config</th><td colspan=2><textarea tabindex=103 name=vsconfig rows=10 cols=80></textarea></td></tr>';
-               echo '<tr><th class=tdright>RS config</th><td colspan=2><textarea tabindex=104 name=rsconfig rows=10 cols=80></textarea></td></tr>';
-               echo '</form></table>';
-               finishPortlet();
-       }
-       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
-               printNewItemTR();
-
-       $focus = spotEntity ('object', $object_id);
-       amplifyCell ($focus);
-       if (count ($focus['ipv4rspools']))
-       {
-               startPortlet ('Manage existing (' . count ($focus['ipv4rspools']) . ')');
-               echo '<table cellspacing=0 cellpadding=5 align=center class=cooltable>';
-               $order = 'odd';
-               global $nextorder;
-               foreach ($focus['ipv4rspools'] as $vs_id => $vsinfo)
-               {
-                       printOpFormIntro ('updLB', array ('vs_id' => $vs_id, 'pool_id' => $vsinfo['pool_id']));
-                       echo "<tr class=row_${order}><td rowspan=2 valign=center><a href='".makeHrefProcess(array('op'=>'delLB', 'pool_id'=>$vsinfo['pool_id'], 'object_id'=>$object_id, 'vs_id'=>$vs_id))."'>";
-                       printImageHREF ('DELETE', 'Unconfigure');
-                       echo "</a></td>";
-                       echo "</td><td class=tdleft valign=bottom>";
-                       renderCell (spotEntity ('ipv4vs', $vs_id));
-                       echo '</td>';
-                       echo "<td>VS config &darr;<br><textarea name=vsconfig rows=5 cols=70>${vsinfo['vsconfig']}</textarea></td>";
-                       echo "<td rowspan=2 valign=middle>";
-                       printImageHREF ('SAVE', 'Save changes', TRUE);
-                       echo '</td></tr>';
-                       echo "<tr class=row_${order}><td valign=top>";
-                       renderCell (spotEntity ('ipv4rspool', $vsinfo['pool_id']));
-                       echo "</td><td><textarea name=rsconfig rows=5 cols=70>${vsinfo['rsconfig']}</textarea><br>RS config &uarr;</td></tr>";
-                       echo '</form>';
-                       $order = $nextorder[$order];
-               }
-               echo "</table>\n";
-               finishPortlet();
-       }
-       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
-               printNewItemTR();
-}
-
 function renderEditRSPool ($pool_id)
 {
        $poolinfo = spotEntity ('ipv4rspool', $pool_id);
@@ -5809,20 +5890,23 @@ function printRoutersTD ($rlist, $as_cell = 'yes')
 }
 
 // Same as for routers, but produce two TD cells to lay the content out better.
-function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbol = 'spacer', $symbolurl = '')
+function printIPv4NetInfoTDs ($netinfo, $decor = array())
 {
-       if ($symbol == 'spacer')
+       if ($netinfo['symbol'] == 'spacer')
        {
-               $indent++;
-               $symbol = '';
+               $decor['indent']++;
+               $netinfo['symbol'] = '';
        }
-       echo "<td class='${tdclass}' style='padding-left: " . ($indent * 16) . "px;'>";
-       if (strlen ($symbol))
+       echo '<td class="tdleft';
+       if (array_key_exists ('tdclass', $decor))
+               echo ' ' . $decor['tdclass'];
+       echo '" style="padding-left: ' . ($decor['indent'] * 16) . 'px;">';
+       if (strlen ($netinfo['symbol']))
        {
-               if (strlen ($symbolurl))
-                       echo "<a href='${symbolurl}'>";
-               printImageHREF ($symbol, $symbolurl);
-               if (strlen ($symbolurl))
+               if (array_key_exists ('symbolurl', $decor))
+                       echo "<a href='${decor['symbolurl']}'>";
+               printImageHREF ($netinfo['symbol']);
+               if (array_key_exists ('symbolurl', $decor))
                        echo '</a>';
        }
        if (isset ($netinfo['id']))
@@ -5830,11 +5914,14 @@ function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbo
        echo "${netinfo['ip']}/${netinfo['mask']}";
        if (isset ($netinfo['id']))
                echo '</a>';
-       echo "</td><td class='${tdclass}'>";
+       echo '</td><td class="tdleft';
+       if (array_key_exists ('tdclass', $decor))
+               echo ' ' . $decor['tdclass'];
+       echo '">';
        if (!isset ($netinfo['id']))
        {
                printImageHREF ('dragons', 'Here be dragons.');
-               if (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes')
+               if ($decor['knight'])
                {
                        echo '<a href="' . makeHref (array
                        (
@@ -5933,12 +6020,25 @@ function renderCell ($cell)
                echo "<table class='slbcell vscell'><tr><td rowspan=3 width='5%'>";
                printImageHREF ('NET');
                echo '</td>';
-               echo "<td><a href='index.php?page=ipv4net&id=${cell['id']}'>${cell['ip']}/${cell['mask']}</a></td></tr>";
+               echo "<td><a href='index.php?page=ipv4net&id=${cell['id']}'>${cell['ip']}/${cell['mask']}</a>";
+               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+               {
+                       countOwnIPv4Addresses ($cell);
+                       loadOwnIPv4Addresses ($cell);
+                       $used = $cell['addrc'];
+                       $maxdirect = $cell['addrt'];
+                       echo '<div class="net-usage">';
+                       echo "<small>$used/$maxdirect</small> ";
+                       renderProgressBar ($maxdirect ? $used/$maxdirect : 0);
+                       echo '</div>';
+               }
+               echo '</td></tr>';
+
                if (strlen ($cell['name']))
                        echo "<tr><td><strong>" . niftyString ($cell['name']) . "</strong></td></tr>";
                else
                        echo "<tr><td class=sparenetwork>no name</td></tr>";
-               echo '<td>';
+               echo '<tr><td>';
                echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
                echo "</td></tr></table>";
                break;
@@ -5984,6 +6084,23 @@ function renderLBCell ($object_id)
        echo "</td></tr></table>";
 }
 
+function renderSLBEntityCell ($realm, $object_id)
+{
+       switch($realm)
+       {
+               case 'object':
+                       renderLBCell($object_id);
+                       break;
+               case 'ipv4vs':
+               case 'ipv4rspool':
+                       $cell = spotEntity ($realm, $object_id);
+                       renderCell($cell);
+                       break;
+               default:
+                       throw new InvalidArgException('realm', $realm);
+       }
+}
+
 function renderRouterCell ($dottedquad, $ifname, $cell)
 {
        echo "<table class=slbcell><tr><td rowspan=3>${dottedquad}";
@@ -6337,7 +6454,7 @@ function dynamic_title_decoder ($path_position)
                        throw new EntityNotFoundException ('VLAN domain', $vdom_id);
                return array
                (
-                       'name' => niftyString ("domain '" . $vdlist[$vdom_id] . "'"),
+                       'name' => niftyString ("domain '" . $vdlist[$vdom_id] . "'", 20, FALSE),
                        'params' => array ('vdom_id' => $vdom_id)
                );
        case 'vlan':
@@ -6741,7 +6858,7 @@ function renderVLANDomain ($vdom_id)
                global $vtdecoder;
                echo '<table class=cooltable align=center border=0 cellpadding=5 cellspacing=0>';
                echo '<tr><th>VLAN ID</th><th>propagation</th><th>';
-               printImageHREF ('net');
+               printImageHREF ('net', 'IPv4 networks linked');
                echo '</th><th>ports</th><th>description</th></tr>';
                foreach ($myvlans as $vlan_id => $vlan_info)
                {
@@ -7627,6 +7744,7 @@ function renderVST ($vst_id)
                startPortlet ('no rules');
        else
        {
+               global $port_role_options;
                startPortlet ('rules (' . count ($vst['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>';
@@ -7636,7 +7754,7 @@ function renderVST ($vst_id)
                        echo "<tr class=row_${order} align=left>";
                        echo "<td>${item['rule_no']}</td>";
                        echo "<td><tt>${item['port_pcre']}</tt></td>";
-                       echo "<td>${item['port_role']}</td>";
+                       echo '<td>' . $port_role_options[$item['port_role']] . '</td>';
                        echo "<td>${item['wrt_vlans']}</td>";
                        echo "<td>${item['description']}</td>";
                        echo '</tr>';
@@ -7702,15 +7820,7 @@ function renderVSTRulesEditor ($vst_id)
        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>';
-       $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',
-       );
+       global $port_role_options;
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
                printNewItemTR ($port_role_options);
        foreach ($vst['rules'] as $item)
index 7341abc..35265f4 100644 (file)
@@ -227,6 +227,10 @@ $ophandler['ipaddress']['assignment']['addIPv4Allocation'] = 'addIPv4Allocation'
 $page['ipv4slb']['title'] = 'IPv4 SLB';
 $page['ipv4slb']['parent'] = 'index';
 $page['ipv4slb']['handler'] = 'renderIPv4SLB';
+$tab['ipv4slb']['default'] = 'Browse';
+$tab['ipv4slb']['defconfig'] = 'Default configs';
+$tabhandler['ipv4slb']['defconfig'] = 'renderSLBDefConfig';
+$ophandler['ipv4slb']['defconfig']['save'] = 'updateSLBDefConfig';
 
 $page['ipv4vslist']['title'] = 'Virtual services';
 $page['ipv4vslist']['parent'] = 'ipv4slb';
index fe143b3..7fa19d0 100644 (file)
@@ -1222,6 +1222,20 @@ function deleteVService ()
                return buildRedirectURL (__FUNCTION__, 'OK');
 }
 
+$msgcode['updateSLBDefConfig']['OK'] = 43;
+$msgcode['updateSLBDefConfig']['ERR'] = 109;
+function updateSLBDefConfig ()
+{
+       $data = array(
+               'vs' => $_REQUEST['vsconfig'],
+               'rs' => $_REQUEST['rsconfig']
+       );
+       if (!commitUpdateSLBDefConf ($data))
+               return buildRedirectURL (__FUNCTION__, 'ERR');
+       else
+               return buildRedirectURL (__FUNCTION__, 'OK');
+}
+
 $msgcode['updateRealServer']['OK'] = 36;
 $msgcode['updateRealServer']['ERR'] = 133;
 function updateRealServer ()
@@ -1250,12 +1264,16 @@ function updateLoadBalancer ()
        assertUIntArg ('vs_id');
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
+       if (! empty($_REQUEST['prio']))
+               assertUIntArg('prio', TRUE);
+
        if (FALSE === commitUpdateLB (
                $_REQUEST['object_id'],
                $_REQUEST['pool_id'],
                $_REQUEST['vs_id'],
                $_REQUEST['vsconfig'],
-               $_REQUEST['rsconfig']
+               $_REQUEST['rsconfig'],
+               $_REQUEST['prio']
        ))
                return buildRedirectURL (__FUNCTION__, 'ERR');
        else
@@ -1296,12 +1314,16 @@ function addLoadBalancer ()
        assertUIntArg ('vs_id');
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
+       if (! empty($_REQUEST['prio']))
+               assertUIntArg('prio', TRUE);
+
        if (!addLBtoRSPool (
                $_REQUEST['pool_id'],
                $_REQUEST['object_id'],
                $_REQUEST['vs_id'],
                $_REQUEST['vsconfig'],
-               $_REQUEST['rsconfig']
+               $_REQUEST['rsconfig'],
+               $_REQUEST['prio']
        ))
                return buildRedirectURL (__FUNCTION__, 'ERR');
        else
@@ -1347,7 +1369,14 @@ function updateRSPool ()
        assertStringArg ('name', TRUE);
        assertStringArg ('vsconfig', TRUE);
        assertStringArg ('rsconfig', TRUE);
-       if (FALSE === commitUpdateRSPool ($_REQUEST['pool_id'], $_REQUEST['name'], $_REQUEST['vsconfig'], $_REQUEST['rsconfig']))
+       if (FALSE === commitUpdateRSPool
+               (
+                       $_REQUEST['pool_id'],
+                       $_REQUEST['name'],
+                       $_REQUEST['vsconfig'],
+                       $_REQUEST['rsconfig']
+               )
+       )
                return buildRedirectURL (__FUNCTION__, 'ERR');
        else
                return buildRedirectURL (__FUNCTION__, 'OK');
index 749b510..134ec18 100644 (file)
@@ -317,6 +317,24 @@ $iftable_processors['juniper-DPCE-R-4XGE-XFP'] = array
        'try_next_proc' => FALSE,
 );
 
+$iftable_processors['juniper-ex-pic0-1000T'] = array
+(
+       'pattern' => '@^ge-([[:digit:]]+)/0/([[:digit:]]+)$@',
+       'replacement' => '\\0',
+       'dict_key' => '1-24',
+       'label' => 'unit \\1 port \\2',
+       'try_next_proc' => FALSE,
+);
+
+$iftable_processors['juniper-ex-mgmt'] = array
+(
+       'pattern' => '/^me0$/',
+       'replacement' => 'me0',
+       'dict_key' => '1-24',
+       'label' => 'MGMT',
+       'try_next_proc' => FALSE,
+);
+
 $iftable_processors['quidway-21-to-24-comboT'] = array
 (
        'pattern' => '@^GigabitEthernet([[:digit:]]+/[[:digit:]]+/)(21|22|23|24)$@',
@@ -749,6 +767,19 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
                'text' => 'MX240 modular router',
                'processors' => array ('juniper-DPCE-R-4XGE-XFP'),
        ),
+       // Juniper Networks assigns single SNMP OID per series:
+       // EX2200 2636.1.1.1.1.43
+       // EX3200 2636.1.1.1.2.30
+       // EX4200 2636.1.1.1.2.31
+       // EX4500 2636.1.1.1.1.44
+       // There is a special workaround in code below to derive specific
+       // product number from sysDescr string.
+       '2636.1.1.1.2.31' => array
+       (
+               'dict_key' => 905,
+               'text' => 'Juniper EX4200 series',
+               'processors' => array ('juniper-ex-pic0-1000T', 'juniper-ex-mgmt'),
+       ),
        '2011.2.23.96' => array
        (
                'dict_key' => 1321,
@@ -806,6 +837,16 @@ $known_switches = array // key is system OID w/o "enterprises" prefix
        ),
 );
 
+$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,
+       // FIXME: get sysDescr for IronWare 5 and add a pattern
+       '/^Brocade Communications Systems.+, IronWare Version 07\./' => 1364,
+       '/^Juniper Networks,.+JUNOS 9\./' => 1366,
+       '/^Juniper Networks,.+JUNOS 10\./' => 1367,
+);
+
 function updateStickerForCell ($cell, $attr_id, $new_value)
 {
        if (!strlen ($cell['attrs'][$attr_id]['value']) && strlen ($new_value))
@@ -939,39 +980,29 @@ 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;
-                       }
+               detectSoftwareType ($objectInfo, $sysDescr);
                checkPIC ('1-681');
                commitAddPort ($objectInfo['id'], 'con0', '1-681', 'console', ''); // DB-9 RS-232 console
                $log = mergeLogs ($log, oneLiner (81, array ('huawei-generic')));
                break;
+       case '2636.1.1.1.2.31' == $sysObjectID: // Juniper EX4200
+               detectSoftwareType ($objectInfo, $sysDescr);
+               checkPIC ('1-29');
+               commitAddPort ($objectInfo['id'], 'con', '1-29', 'CON', ''); // RJ-45 RS-232 console
+               // EX4200-24T is already in DB
+               if (preg_match ('/^Juniper Networks, Inc. ex4200-48t internet router/', $sysDescr))
+                       updateStickerForCell ($objectInfo, 2, 907);
+               $log = mergeLogs ($log, oneLiner (81, array ('juniper-ex')));
+               break;
        case preg_match ('/^2636\.1\.1\.1\.2\./', $sysObjectID): // Juniper
+               detectSoftwareType ($objectInfo, $sysDescr);
                checkPIC ('1-681');
                commitAddPort ($objectInfo['id'], 'console', '1-681', 'console', ''); // DB-9 RS-232 console
                $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
-               $swtype_pcre = array
-               (
-                       // FIXME: get sysDescr for IronWare 5 and add a pattern
-                       '/^Brocade Communications Systems.+, IronWare Version 07\./' => 1364,
-               );
-               foreach ($swtype_pcre as $pattern => $dict_key)
-                       if (preg_match ($pattern, $sysDescr))
-                       {
-                               updateStickerForCell ($objectInfo, 4, $dict_key);
-                               break;
-                       }
+               detectSoftwareType ($objectInfo, $sysDescr);
                $exact_release = preg_replace ('/^.*, IronWare Version ([^ ]+) .*$/', '\\1', $sysDescr);
                updateStickerForCell ($objectInfo, 5, $exact_release);
                # FOUNDRY-SN-AGENT-MIB::snChasSerNum.0
@@ -1067,6 +1098,11 @@ function doSwitchSNMPmining ($objectInfo, $hostname, $community)
                foreach ($known_switches[$sysObjectID]['processors'] as $processor_name)
                {
                        $newname = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['replacement'], $iface['ifDescr'], 1, $count);
+                       if ($newname === NULL)
+                       {
+                               $log = mergeLogs ($log, oneLiner (100, array ('PCRE pattern error, terminating')));
+                               break 2;
+                       }
                        if (!$count)
                                continue; // try next processor on current port
                        $newlabel = preg_replace ($iftable_processors[$processor_name]['pattern'], $iftable_processors[$processor_name]['label'], $iface['ifDescr'], 1, $count);
@@ -1289,4 +1325,14 @@ function generatePortsForCatModule ($object_id, $slotno = 1, $mtype = 'X6748', $
        }
 }
 
+function detectSoftwareType ($objectInfo, $sysDescr)
+{
+       global $swtype_pcre;
+       foreach ($swtype_pcre as $pattern => $dict_key)
+               if (preg_match ($pattern, $sysDescr))
+               {
+                       updateStickerForCell ($objectInfo, 4, $dict_key);
+                       return;
+               }
+}
 ?>
index 1f42b4e..7855963 100644 (file)
@@ -132,6 +132,7 @@ CREATE TABLE `IPv4LB` (
   `object_id` int(10) unsigned default NULL,
   `rspool_id` int(10) unsigned default NULL,
   `vs_id` int(10) unsigned default NULL,
+  `prio` int(10) unsigned default NULL,
   `vsconfig` text,
   `rsconfig` text,
   UNIQUE KEY `LB-VS` (`object_id`,`vs_id`),
index 3d446af..b7ef75a 100644 (file)
@@ -81,3 +81,63 @@ function init_cb_click() {
                });
        });
 }
+
+// uses global tagShortList array
+function compactTreeMode() {
+       // reconfigure toggle link
+       var link = $('a.toggleTreeMode')[0];
+       if ($(link).filter(':visible')) {
+               $(link).after('<p>');
+       }
+       link.onclick = function () {fullTreeMode(); return false;};
+       $(link).html('show full tree').show();
+       
+       $('.tagtree').addClass('compact'); // disable hierachical padding
+       
+       var separator = false; // next visible row is separator
+       var bPrevSeparator = true; // prev visible row was separator
+       $('input.tag-cb').each(function (i, item) {
+               var tr = $(item).closest('tr');
+               
+               if ($(item).hasClass('root'))
+                       separator = true;
+               
+               if (! item.checked && ! tagShortList[item.value]) {
+                       tr.hide();
+                       return;
+               }
+               
+               if (separator && ! bPrevSeparator) { // do not draw two separators together or very first separator
+                       tr.addClass('separator');
+                       bPrevSeparator = true;
+               }
+               else {
+                       tr.removeClass('separator');
+                       bPrevSeparator = false;
+               }
+               separator = false;
+       });
+}
+
+// uses global tagShortList array
+function fullTreeMode() {
+       // reconfigure toggle link
+       var link = $('a.toggleTreeMode')[0];
+       link.onclick = function () {compactTreeMode(); return false;};
+       $(link).html('show compact tree').show();
+       
+       $('.tagtree').removeClass('compact'); // restore hierachical padding
+       
+       var bPrevSeparator = true; // prev visible row was separator
+       $('input.tag-cb').each(function (i, item) { // // do not draw two separators together or very first separator
+               var tr = $(item).closest('tr');
+               tr.removeClass('separator');
+
+               var separator = $(item).hasClass('root');
+               if (separator && ! bPrevSeparator)
+                       tr.addClass('separator');
+               bPrevSeparator = separator;
+
+               tr.show();
+       });
+}
diff --git a/pi.css b/pi.css
index 66d5b3c..dee4bb9 100644 (file)
--- a/pi.css
+++ b/pi.css
@@ -515,3 +515,19 @@ div.tagselector td {
 .rsvtext {
        font-style: italic;
 }
+
+.tagtree.compact td {
+       padding-left: 0px !important;
+}
+
+.tagtree tr.separator td {
+       border-top: 2px solid gray;
+}
+
+a.toggleTreeMode {
+}
+
+.net-usage {
+               float: right;
+               margin-left: 15px;
+}
index 2f99b17..104169c 100644 (file)
@@ -747,7 +747,9 @@ CREATE TABLE `VLANValidID` (
                        $query[] = "UPDATE Config SET varvalue = '0.18.4' WHERE varname = 'DB_VERSION'";
                        break;
                case '0.18.5':
+                       $query = array_merge ($query, reloadDictionary ($batchid));
                        $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, is_userdefined, description) VALUES ('SHRINK_TAG_TREE_ON_CLICK','yes','string','no','no','yes','Dynamically hide useless tags in tagtree')";
+                       $query[] = "ALTER TABLE `IPv4LB` ADD COLUMN `prio` int(10) unsigned DEFAULT NULL AFTER `vs_id`";
                        $query[] = "UPDATE Config SET varvalue = '0.18.5' WHERE varname = 'DB_VERSION'";
                        break;
                default: