r2232 - last mods before RackTables 0.16.3
[racktables] / upgrade.php
index 1db0658..282c614 100644 (file)
@@ -22,11 +22,14 @@ function getDBUpgradePath ($v1, $v2)
                '0.15.0',
                '0.15.1',
                '0.16.0',
+               '0.16.1',
+               '0.16.2',
+               '0.16.3',
        );
        if (!in_array ($v1, $versionhistory) || !in_array ($v2, $versionhistory))
        {
                showError ("An upgrade path has been requested for versions '${v1}' and '${v2}', " .
-                 "and at least one of those isn't known to me.");
+                 "and at least one of those isn't known to me.", __FILE__);
                die;
        }
        $skip = TRUE;
@@ -48,6 +51,29 @@ function getDBUpgradePath ($v1, $v2)
        return $path;
 }
 
+function printReleaseNotes ($batchid)
+{
+       switch ($batchid)
+       {
+               case '0.16.0':
+                       echo "<font color=red><strong>Release notes for ${batchid}</strong></font><br>";
+                       echo 'The user permission records of this system have been automatically converted ';
+                       echo 'to switch to the new RackCode authorization system. To prevent possible data ';
+                       echo 'leak, the second line of the automatically created configuration bans everything ';
+                       echo '(and the first allows everything to you, the administrator). The whole config can ';
+                       echo "be reviewed on the Permissions page (under Configuration). Sorry for the inconvenience.<br><br>\n";
+                       break;
+               case '0.16.3':
+                       echo "<font color=red><strong>Release notes for ${batchid}</strong></font><br>";
+                       echo 'This release fixes a missing UNIQUE KEY in a table. The upgrade script may find it necessary first to transform some records.<br>';
+                       echo 'Because of this it is normal to see several "update TagStorage ... Duplicate entry" failed queries during the upgrade.<br>';
+                       echo 'Additionally, it is normal to see " Can\'t DROP \'endpoint\'" message during upgrade<br>';
+                       break;
+               default:
+                       break;
+       }
+}
+
 // Upgrade batches are name exactly as the release where they first appear.
 // That simple, but seems sufficient for beginning.
 function executeUpgradeBatch ($batchid)
@@ -1272,11 +1298,7 @@ CREATE TABLE `TagTree` (
                                die ('<b>Cannot upgrade due to multibyte extension not present. See the README for details.</b>');
                        }
                        $query[] = 'alter table TagStorage modify column tag_id int(10) unsigned not null;';
-                       $query[] = "alter table TagStorage modify column target_realm enum('object','ipv4net','rack','ipv4vs','ipv4rspool','user');";
-                       $query[] = "delete from UserPermission where page = 'objects' and tab = 'newobj'";
-                       $query[] = "update UserPermission set tab = 'addmore' where page = 'objects' and (tab = 'newmulti' or tab = 'newobj')";
-                       $query[] = "update UserPermission set tab = 'livevlans' where tab = 'switchvlans'";
-                       $query[] = "update UserPermission set page = 'userlist' where page = 'accounts'";
+                       $query[] = "alter table TagStorage modify column target_realm enum('object','ipv4net','rack','ipv4vs','ipv4rspool','user') NOT NULL default 'object'";
                        $query[] = "create table Script (script_name char(64) not null primary key, script_text text)";
                        // Do the same getUserPermissions() does, but without the function.
                        // We need to generate more specific rules first, otherwise they will
@@ -1284,11 +1306,31 @@ CREATE TABLE `TagTree` (
                        $tq =
                                "select UserPermission.user_id, user_name, page, tab, access from " .
                                "UserPermission natural left join UserAccount where (user_name is not null) or " .
-                               "(user_name is null and UserPermission.user_id = 0) order by user_name desc, page desc, tab desc";
+                               "(user_name is null and UserPermission.user_id = 0) order by user_id desc, page desc, tab desc";
                        $tr = $dbxlink->query ($tq);
-                       $code = '';
+                       $code = "allow {\$userid_1}\ndeny true\n";
+                       // copied and pasted from fixContext()
+                       $pmap = array
+                       (
+                               'accounts' => 'userlist',
+                               'rspools' => 'ipv4rsplist',
+                               'rspool' => 'ipv4rsp',
+                               'vservices' => 'ipv4vslist',
+                               'vservice' => 'ipv4vs',
+                       );
+                       $tmap = array();
+                       $tmap['objects']['newmulti'] = 'addmore';
+                       $tmap['objects']['newobj'] = 'addmore';
+                       $tmap['object']['switchvlans'] = 'livevlans';
+                       $tmap['object']['slb'] = 'editrspvs';
+                       $tmap['object']['portfwrd'] = 'nat4';
+                       $tmap['object']['network'] = 'ipv4';
                        while ($row = $tr->fetch (PDO::FETCH_ASSOC))
                        {
+                               // map, if appropriate
+                               $row['page'] = isset ($pmap[$row['page']]) ? $pmap[$row['page']] : $row['page'];
+                               $row['tab'] = isset ($tmap[$row['page']][$row['tab']]) ? $tmap[$row['page']][$row['tab']] : $row['tab'];
+                               // build a rule
                                $conjunction = '';
                                $rule = $row['access'] == 'yes' ? 'allow' : 'deny';
                                if ($row['user_id'] != 0)
@@ -1309,10 +1351,72 @@ CREATE TABLE `TagTree` (
                        }
                        $query[] = "insert into Script (script_name, script_text) values ('RackCode', '${code}')";
                        $query[] = 'drop table UserPermission';
+                       $new_words[791] = array (13 => '[[Linux%GSKIP%openSUSE 11.0 | http://en.opensuse.org/OpenSUSE_11.0]]');
+                       $new_words[] = array (11 => '[[SGI%GPASS%Altix XE250 | http://www.sgi.com/products/servers/altix/xe/configs.html]]');
+                       $new_words[] = array (11 => '[[SGI%GPASS%Altix XE310 | http://www.sgi.com/products/servers/altix/xe/configs.html]]');
+                       $new_words[] = array (11 => '[[SGI%GPASS%Altix XE320 | http://www.sgi.com/products/servers/altix/xe/configs.html]]');
+                       foreach ($new_words as $dict_key => $tmp)
+                               foreach ($tmp as $chapter_no => $dict_value)
+                                       $query[] = 'INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) ' .
+                                               "VALUES (${chapter_no}, ${dict_key}, '${dict_value}')";
                        $query[] = "update Config set varvalue = '0.16.0' where varname = 'DB_VERSION'";
                        break;
+               case '0.16.1':
+                       $query[] = 'alter table Script modify column script_text longtext';
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('SHOW_LAST_TAB','no','string','yes','no','Remember last tab shown for each page')";
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('COOKIE_TTL','1209600','uint','yes','no','Cookies lifetime in seconds')";
+                       $query[] = "update Config set varvalue = '0.16.1' where varname = 'DB_VERSION'";
+                       break;
+               case '0.16.2':
+                       $query[] = "alter table IPBonds modify column type enum('regular','shared','virtual','router')";
+                       $query[] = "update Dictionary set dict_value = 'spacer' where dict_key = 11";
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('EXT_IPV4_VIEW','yes','string','no','no','Display parent network info for IPv4 addresses')";
+                       $query[] = "ALTER TABLE RackSpace ADD KEY `RackSpace_object_id` (`object_id`)";
+                       $query[] = "update Config set varvalue = '0.16.2' where varname = 'DB_VERSION'";
+                       break;
+               case '0.16.3':
+                       // The network table list used to allow duplicate "prefix-masklen" pairs, which was a bad idea.
+                       // The code, which verified each new network to be "unique", didn't work for "upper" IPv4 space.
+                       // To enable the relevant index now, it is necessary to process all ghost networks, which could
+                       // be accumulated over the time, and move all tags assigned to them to the "master" record, which
+                       // we recognize to be the one with the lowest ID.
+                       $q = 'select ip, mask, count(*) as c from IPRanges group by ip, mask having c > 1';
+                       $r = $dbxlink->query ($q);
+                       // Let's hope there's not many dupes, cause sub-queries won't work.
+                       $dupes = $r->fetchAll (PDO::FETCH_ASSOC);
+                       unset ($r);
+                       foreach ($dupes as $d)
+                       {
+                               $firstid = 0;
+                               $q = "select id from IPRanges where ip = ${d['ip']} and mask = ${d['mask']} order by id";
+                               $r = $dbxlink->query ($q);
+                               while ($row = $r->fetch (PDO::FETCH_ASSOC))
+                               {
+                                       if (!$firstid)
+                                       {
+                                               $firstid = $row['id'];
+                                               continue;
+                                       }
+                                       // Rewrite tags, but don't rebuild the chains. Let regular code sort it out.
+                                       // One of the next two queries will fail.
+                                       $query[] = "update TagStorage set target_id = ${firstid} where target_id = ${row['id']} and target_realm = 'ipv4net'";
+                                       $query[] = "delete from TagStorage where target_id = ${row['id']} and target_realm = 'ipv4net'";
+                                       $query[] = "delete from IPRanges where id = ${row['id']}";
+                               }
+                               unset ($r);
+                       }
+                       $query[] = 'alter table IPRanges add unique `base-len` (`ip`, `mask`)';
+                       $query[] = 'alter table IPVirtualService drop key `endpoint`';
+                       // fix the default value (only seen when upgrading from pre-0.16.0 to pre-0.16.3)
+                       $query[] = "alter table TagStorage modify column target_realm enum('object','ipv4net','rack','ipv4vs','ipv4rspool','user') NOT NULL default 'object'";
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('TREE_THRESHOLD','25','uint','yes','no','Tree view auto-collapse threshold')";
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('IPV4_JAYWALK','no','string','no','no','Enable IPv4 address allocations w/o covering network')";
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('ADDNEW_AT_TOP','yes','string','no','no','Render \"add new\" line at top of the list')";
+                       $query[] = "update Config set description = 'Extended IPv4 view' where varname = 'EXT_IPV4_VIEW'";
+                       $query[] = "update Config set varvalue = '0.16.3' where varname = 'DB_VERSION'";
+                       break;
                default:
-                       showError ("executeUpgradeBatch () failed, because batch '${batchid}' isn't defined");
+                       showError ("executeUpgradeBatch () failed, because batch '${batchid}' isn't defined", __FILE__);
                        die;
                        break;
        }
@@ -1393,8 +1497,24 @@ catch (PDOException $e)
 // will have to dig into the DB for the user accounts.
 require_once 'inc/auth.php';
 
-// This will not fail sanely, because getUserAccounts() depends on showError()
-$accounts = getUserAccounts();
+// 1. This didn't fail sanely, because getUserAccounts() depended on showError()
+// 2. getUserAccounts() doesn't work for old DBs since 0.16.0. Let's have own
+// copy until it breaks too.
+
+function getUserAccounts_local ()
+{
+       global $dbxlink;
+       $query = 'select user_id, user_name, user_password_hash from UserAccount order by user_name';
+       if (($result = $dbxlink->query ($query)) == NULL)
+               die ('SQL query failed in ' . __FUNCTION__);
+       $ret = array();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('user_id', 'user_name', 'user_password_hash') as $cname)
+                       $ret[$row['user_name']][$cname] = $row[$cname];
+       return $ret;
+}
+
+$accounts = getUserAccounts_local();
 
 // Only administrator is always authenticated locally, so reject others
 // for authenticate() to succeed.
@@ -1409,22 +1529,25 @@ if
 {
        header ('WWW-Authenticate: Basic realm="RackTables upgrade"');
        header ('HTTP/1.0 401 Unauthorized');
-       showError ('You must be authenticated as an administrator to complete the upgrade.');
+       showError ('You must be authenticated as an administrator to complete the upgrade.', __FILE__);
        die;
 }
 
 $dbver = getDatabaseVersion();
-echo 'Code version == ' . CODE_VERSION . '<br>';
-echo 'Database version == ' . $dbver . '<br>';
+echo 'Code version: ' . CODE_VERSION . '<br>';
+echo 'Database version: ' . $dbver . '<br>';
 if ($dbver == CODE_VERSION)
 {
-       die ("<p align=justify>Your database seems to be up-to-date. " .
-               "Now the best thing to do would be to follow to the <a href='${root}'>main page</a> " .
-               "and explore your data. Have a nice day.</p>");
+       die ("<p align=justify>No action is necessary. " .
+               "Proceed to the <a href='${root}'>main page</a>, " .
+               "check your data and have a nice day.</p>");
 }
 
 foreach (getDBUpgradePath ($dbver, CODE_VERSION) as $batchid)
+{
        executeUpgradeBatch ($batchid);
+       printReleaseNotes ($batchid);
+}
 
 echo '<br>Database version == ' . getDatabaseVersion();
 echo "<p align=justify>Your database seems to be up-to-date. " .