r1289 + storeConfigVar(): fix failure on storing an empty value
[racktables] / upgrade.php
CommitLineData
b325120a 1<?php
fbbb74fb
DO
2
3// At the moment we assume, that for any two releases we can
4// sequentally execute all batches, that separate them, and
5// nothing will break. If this changes one day, the function
6// below will have to generate smarter upgrade paths, while
7// the upper layer will remain the same.
8// Returning an empty array means that no upgrade is necessary.
9function getDBUpgradePath ($v1, $v2)
10{
11 $versionhistory = array ('0.14.4', '0.14.5', '0.14.6');
12 if (!in_array ($v1, $versionhistory) || !in_array ($v2, $versionhistory))
13 {
14 showError ("An upgrade path has been requested for versions '${v1}' and '${v2}', " .
15 "and at least one of those isn't known to me.");
16 die;
17 }
18 $skip = TRUE;
19 $path = array();
20 // Now collect all versions > $v1 and <= $v2
21 foreach ($versionhistory as $v)
22 {
23 if ($v == $v1)
24 {
25 $skip = FALSE;
26 continue;
27 }
28 if ($skip)
29 continue;
30 $path[] = $v;
31 if ($v == $v2)
32 break;
33 }
34 return $path;
35}
36
37// Upgrade batches are name exactly as the release where they first appear.
38// That simple, but seems sufficient for beginning.
39function executeUpgradeBatch ($batchid)
40{
41 $query = array();
ce109ff2 42 global $dbxlink;
fbbb74fb
DO
43 switch ($batchid)
44 {
45 case '0.14.5':
46 // We can't realiably distinguish between 0.14.4 and 0.14.5, but
47 // luckily the SQL statements below can be safely executed for both.
48
49
50 // This has to be checked once more to be sure IPAddress allocation
51 // conventions are correct.
52 $query[] = "delete from IPAddress where name = '' and reserved = 'no'";
53
54 // In the 0.14.4 release we had AUTO_INCREMENT low in the dictionary and auth
55 // data tables, thus causing new user's data to take primary keys equal to
56 // the values of shipped data in future releases. Let's shift user's data
57 // up and keep DB consistent.
58 $query[] = "alter table Attribute AUTO_INCREMENT = 10000";
59 $query[] = "alter table Chapter AUTO_INCREMENT = 10000";
60 $query[] = "alter table Dictionary AUTO_INCREMENT = 10000";
61 $query[] = "alter table UserAccount AUTO_INCREMENT = 10000";
62 $query[] = "update UserAccount set user_id = user_id + 10000 where user_id between 2 and 10000";
63 $query[] = "update UserPermission set user_id = user_id + 10000 where user_id between 2 and 10000";
64 $query[] = "update Attribute set attr_id = attr_id + 10000 where attr_id between 25 and 10000";
65 $query[] = "update AttributeMap set attr_id = attr_id + 10000 where attr_id between 25 and 10000";
66 $query[] = "update Chapter set chapter_no = chapter_no + 10000 where chapter_no between 21 and 10000";
67 $query[] = "update AttributeMap set chapter_no = chapter_no + 10000 where chapter_no between 21 and 10000";
68 break; // --------------------------------------------
69 case '0.14.6':
70 // This version features new dictionary entries, the correction above should allow us
71 // inject them w/o a problem.
72 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,25,'FreeBSD 1.x')";
73 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,26,'FreeBSD 2.x')";
74 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,27,'FreeBSD 3.x')";
75 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,28,'FreeBSD 4.x')";
76 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,29,'FreeBSD 5.x')";
77 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,30,'FreeBSD 6.x')";
78 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,31,'RHFC8')";
79 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (13,32,'ALTLinux Master 4.0')";
80 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (20,20)";
81 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (21,21)";
82 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (22,22)";
83 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (23,23)";
84 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (24,24)";
85 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (25,25)";
86 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (26,26)";
87 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (27,27)";
88 $query[] = "INSERT INTO `PortCompat` (`type1`, `type2`) VALUES (28,28)";
89 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,20,'KVM')";
90 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,21,'1000Base-ZX')";
91 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,22,'10GBase-ER')";
92 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,23,'10GBase-LR')";
93 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,24,'10GBase-LRM')";
94 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,25,'10GBase-ZR')";
95 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,26,'10GBase-LX4')";
96 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,27,'10GBase-CX4')";
97 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (2,28,'10GBase-Kx')";
98 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (12,114,'Cisco Catalyst 2970G-24T')";
99 $query[] = "INSERT INTO `Dictionary` (`chapter_no`, `dict_key`, `dict_value`) VALUES (12,115,'Cisco Catalyst 2970G-24TS')";
100 $query[] = "INSERT INTO `UserPermission` (`user_id`, `page`, `tab`, `access`) VALUES (0,'help','%','yes')";
101 // And 0.14.6 is the first release, which features Config table. Let's create
102 // and fill it with default values.
103 $query[] = "
104CREATE TABLE `Config` (
105 `varname` char(32) NOT NULL,
106 `varvalue` char(64) NOT NULL,
107 `vartype` enum('string','uint') NOT NULL default 'string',
108 `emptyok` enum('yes','no') NOT NULL default 'no',
109 `is_hidden` enum('yes','no') NOT NULL default 'yes',
110 `description` text,
111 PRIMARY KEY (`varname`)
112) ENGINE=MyISAM DEFAULT CHARSET=latin1
113 ";
114 $query[] = "INSERT INTO `Config` VALUES ('rtwidth_0','9','uint','no','yes','')";
115 $query[] = "INSERT INTO `Config` VALUES ('rtwidth_1','21','uint','no','yes','')";
116 $query[] = "INSERT INTO `Config` VALUES ('rtwidth_2','9','uint','no','yes','')";
117 $query[] = "INSERT INTO `Config` VALUES ('color_F','8fbfbf','string','no','no','HSV: 180-25-75. Free atoms, they are available for allocation to objects.')";
118 $query[] = "INSERT INTO `Config` VALUES ('color_A','bfbfbf','string','no','no','HSV: 0-0-75. Absent atoms.')";
119 $query[] = "INSERT INTO `Config` VALUES ('color_U','bf8f8f','string','no','no','HSV: 0-25-75. Unusable atoms. Some problems keep them from being free.')";
120 $query[] = "INSERT INTO `Config` VALUES ('color_T','408080','string','no','no','HSV: 180-50-50. Taken atoms, object_id should be set for such.')";
121 $query[] = "INSERT INTO `Config` VALUES ('color_Th','80ffff','string','no','no','HSV: 180-50-100. Taken atoms with highlight. They are not stored in the database and are only used for highlighting.')";
122 $query[] = "INSERT INTO `Config` VALUES ('color_Tw','804040','string','no','no','HSV: 0-50-50. Taken atoms with object problem. This is detected at runtime.')";
123 $query[] = "INSERT INTO `Config` VALUES ('color_Thw','ff8080','string','no','no','HSV: 0-50-100. An object can be both current and problematic. We run highlightObject() first and markupObjectProblems() second.')";
124 $query[] = "INSERT INTO `Config` VALUES ('default_port_type','11','uint','no','no','Default value for port type selects.')";
125 $query[] = "INSERT INTO `Config` VALUES ('MASSCOUNT','15','uint','no','no','Number of lines in object mass-adding form.')";
126 $query[] = "INSERT INTO `Config` VALUES ('MAXSELSIZE','30','uint','no','no','Maximum size of a SELECT HTML element.')";
127 $query[] = "INSERT INTO `Config` VALUES ('enterprise','MyCompanyName','string','no','no','Fit to your needs.')";
128 $query[] = "INSERT INTO `Config` VALUES ('NAMEFUL_OBJTYPES','4,7,8','string','yes','no','These are the object types, which assume a common name to be normally configured. If a name is absent for an object of one of such types, HTML output is corrected to accent this misconfiguration.')";
129 $query[] = "INSERT INTO `Config` VALUES ('ROW_SCALE','2','uint','no','no','Row-scope picture scale factor.')";
130 $query[] = "INSERT INTO `Config` VALUES ('PORTS_PER_ROW','12','uint','no','yes','Max switch port per one row on the switchvlans dynamic tab.')";
131 $query[] = "INSERT INTO `Config` VALUES ('DB_VERSION','0.14.6','string','no','yes','Database version.')";
132 break; // --------------------------------------------
803338c1 133 case '0.14.7':
ce109ff2 134 // IPAddress is hopefully fixed now finally.
803338c1 135 $query[] = "delete from IPAddress where name = '' and reserved != 'yes'";
ce109ff2
DO
136
137 // Now rebuild the dictionary into a new table with the same data,
138 // but proper indexing. We are going to convert compound index
139 // into 1-field one to employ AUTO_INCREMENT properly. This means
140 // renumbering lots of records in Dictionary and adjusting records
141 // in related tables. After that we can safely swap the tables.
142 $query[] = 'create table Dictionary_0_14_7_new (chapter_no int(10) unsigned NOT NULL, dict_key int(10) unsigned NOT NULL auto_increment PRIMARY KEY, dict_value char(128) default NULL)';
143 // Convert the fields to unsigned on occasion.
144 $query[] = 'create table PortCompat_0_14_7_new (type1 int(10) unsigned NOT NULL, type2 int(10) unsigned NOT NULL)';
145
146 // Find all chapter numbers, which will require AttributeValue adjustment.
147 $q2 = 'select distinct chapter_no from AttributeMap where chapter_no != 0';
148 $r2 = $dbxlink->query ($q2);
149 $chaplist = $r2->fetchAll (PDO::FETCH_NUM);
150 $r2->closeCursor();
151 $chaplist = $chaplist[0];
152
153 $stock = array();
154 // Below I list the records, which are known to be the stock
155 // dictionary records of 0.14.6 release.
156 $stock[1] = array
157 (
158 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16
159 );
160 $stock[2] = array
161 (
162 3, 4, 5, 6, 7, 8, 9,
163 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
164 20, 21, 22, 23, 24, 25, 26, 27, 28
165 );
166 $stock[11] = array
167 (
168 1, 3, 4, 5, 6, 7, 8, 9,
169 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
170 21, 22, 24, 25, 26, 27, 28, 29,
171 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
172 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
173 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
174 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
175 70, 71, 72, 73, 74, 75, 76
176 );
177 $stock[12] = array
178 (
179 1, 11, 13, 14, 15, 16, 17, 18, 19, 20, 26, 29,
180 31, 32, 33, 34, 35, 36, 37, 38, 39,
181 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
182 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
183 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
184 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
185 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
186 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
187 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
188 110, 111, 112, 113
189 );
190 $stock[13] = array
191 (
192 1, 2, 3, 4, 5, 6, 7, 8, 9,
193 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
194 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
195 30, 31, 32
196 );
197 $stock[14] = array
198 (
199 1, 2, 9, 11, 13, 15, 19, 20, 21, 22
200 );
201 $stock[16] = array
202 (
203 1, 2, 3, 4, 5, 6, 7, 8
204 );
205 $stock[17] = array
206 (
207 1, 2, 3, 4, 5, 6, 7, 8, 9,
208 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
209 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
210 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
211 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
212 50
213 );
214 $stock[18] = array
215 (
216 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
217 );
218 $stock[19] = array
219 (
220 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
221 );
222 $stock[20] = array
223 (
224 1, 2
225 );
226
227 // Load dictionary and transform into two tree structures for
228 // stock and user record sets.
229 $dict = array();
230 $q3 = 'select chapter_no, dict_key, dict_value from Dictionary';
231 $r3 = $dbxlink->query ($q3);
232
233 while ($row = $r3->fetch (PDO::FETCH_ASSOC)
234 {
235 $tree = 'user';
236 $dict_key = $row['dict_key'];
237 $chapter_no = $row['chapter_no'];
238 switch ($chapter_no)
239 {
240 case 1: // RackObjectType
241 case 2: // PortType
242 case 11: // server models
243 case 12: // network switch models
244 case 13: // server OS type
245 case 14: // network switch OS type
246 case 16: // router OS type
247 case 17: // router models
248 case 18: // disk array models
249 case 19: // tape library models
250 case 20: // Protocols
251 if (in_array ($dict_key, $stock[$chapter_no]))
252 $tree = 'stock';
253 break;
254 }
255 $dict[$tree][$chapter_no][$dict_key]['value'] = $row['dict_value'];
256 }
257 $r3->closeCursor();
258
259
260 // Now we store stock dataset first, bump up key value and store
261 // user's data. After that we will know the new dict_key for all
262 // records.
263 // The result of both datasets processing is saved in $new_dict.
264 // Save on calling LAST_ISERT_ID() each time by keeping own key.
265 $newkey = 1;
266 $new_dict = array();
267 foreach ($dict['stock'] as $chapter_no => $words)
268 {
269 $new_dict[$chapter_no] = array();
270 foreach ($words as $dict_key => $entry)
271 {
272 $query[] = "insert into Dictionary_0_14_7_new " .
273 "values (${chapter_no}, ${newkey}, '${entry['value']}')";
274 $new_dict[$chapter_no][$dict_key] = $entry;
275 $new_dict[$chapter_no][$dict_key]['newkey'] = $newkey;
276 $newkey++;
277 }
278 }
279 $newkey = 10000;
280 foreach ($dict['user'] as $chapter_no => $words)
281 {
282 // Some chapters may appear on the user dataset only.
283 if (!isset ($new_dict[$chapter_no]))
284 $new_dict[$chapter_no] = array();
285 foreach ($words as $dict_key => $entry)
286 {
287 $query[] = "insert into Dictionary_0_14_7_new " .
288 "values (${chapter_no}, ${newkey}, '${entry['value']}')";
289 $new_dict[$chapter_no][$dict_key] = $entry;
290 $new_dict[$chapter_no][$dict_key]['newkey'] = $newkey;
291 $newkey++;
292 }
293 }
294 // The new table should now have adequate AUTO_INCREMENT w/o our care.
295
296 // Now we iterate over the joint dataset, picking some chapters and
297 // performing additional processing:
298 // 1 (RackObjectType) --- adjust RackObject and regenerate AttributeMap
299 // 2 (PortType) --- regenerate PortCompat and adjust Port
300 // 3 (RackRow) --- adjust Rack
301 // 20 (Protocols) --- adjust PortForwarding
302 // All other chapters listed in $chaplist --- adjust AttributeValue
303
304 $query[] = "delete from AttributeMap";
305 foreach ($new_dict as $chapter_no => $words)
306 {
307 foreach ($words as $oldkey => $data)
308 {
309 $value = $data['value'];
310 $newkey = $data['newkey'];
311 if ($chapter_no == 1)
312 {
313 $q4 = "select id from RackObject where objtype_id = ${oldkey}";
314 $r4 = $dbxlink->query ($q4);
315 while ($row = $r4->fetch (PDO::FETCH_ASSOC))
316 $query[] = "update RackObject set objtype_id = ${newkey} where id = ${row['id']} limit 1";
317 $r4->closeCursor();
318
319 $q4 = "select attr_id, chapter_no from AttributeMap where objtype_id = ${oldkey}";
320 $r4 = $dbxlink->query ($q4);
321 while ($row = $r4->fetch (PDO::FETCH_ASSOC))
322 $query[] = "insert into AttributeMap (objtype_id, attr_id, chapter_no) values (${newkey}, ${row['attr_id']}, ${row['chapter_no']})";
323 $r4->closeCursor();
324 }
325 elseif ($chapter_no == 3)
326 {
327 $q4 = "select id from Rack where row_id = ${oldkey}";
328 $r4 = $dbxlink->query ($q4);
329 while ($row = $r4->fetch (PDO::FETCH_ASSOC))
330 $query[] = "update Rack set row_id = ${newkey} where id = ${row['id']} limit 1";
331 $r4->closeCursor();
332 }
333 }
334 }
335
336 // We are done.
337 $query[] = "update Config set varvalue = '0.14.7' where varname = 'DB_VERSION'";
803338c1 338 break; // --------------------------------------------
fbbb74fb
DO
339 default:
340 showError ("executeUpgradeBatch () failed, because batch '${batchid}' isn't defined");
341 die;
342 break;
343 }
fbbb74fb 344 $failures = array();
ce109ff2 345 $ndots = 0;
fbbb74fb
DO
346 echo "<pre>Executing database upgrade batch '${batchid}: ";
347 foreach ($query as $q)
348 {
349 $result = $dbxlink->query ($q);
350 if ($result != NULL)
351 {
352 echo '.';
ce109ff2
DO
353 if (++$ndots == 50)
354 {
355 echo "\n";
356 $ndots = 0;
357 }
fbbb74fb
DO
358 continue;
359 }
360 echo '!';
361 $errorInfo = $dbxlink->errorInfo();
362 $failures[] = array ($q, $errorInfo[2]);
363 }
364 echo '<br>';
365 if (!count ($failures))
366 echo "No errors!\n";
367 else
368 {
369 echo "The following queries failed:\n";
370 foreach ($failures as $f)
371 {
372 list ($q, $i) = $f;
373 echo "${q} // ${i}\n";
374 }
375 }
376 echo '</pre>';
377}
378
379// ******************************************************************
380//
381// Execution starts here
382//
383// ******************************************************************
384
385$root = (empty($_SERVER['HTTPS'])?'http':'https').
386 '://'.
387 (isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:($_SERVER['SERVER_NAME'].($_SERVER['SERVER_PORT']=='80'?'':$_SERVER['SERVER_PORT']))).
388 dirname($_SERVER['PHP_SELF']).'/';
389
390// The below will be necessary as long as we rely on showError()
391require_once 'inc/interface.php';
392
393require_once 'inc/config.php';
394require_once 'inc/database.php';
395if (file_exists ('inc/secret.php'))
396 require_once 'inc/secret.php';
397else
398 die ("Database connection parameters are read from inc/secret.php file, " .
399 "which cannot be found.\nCopy provided inc/secret-sample.php to " .
400 "inc/secret.php and modify to your setup.\n\nThen reload the page.");
401
402try
403{
404 $dbxlink = new PDO ($pdo_dsn, $db_username, $db_password);
405}
406catch (PDOException $e)
407{
408 die ("Database connection failed:\n\n" . $e->getMessage());
409}
410
411if (isset ($_SERVER['PHP_AUTH_USER']))
412 $_SERVER['PHP_AUTH_USER'] = escapeString ($_SERVER['PHP_AUTH_USER']);
413if (isset ($_SERVER['PHP_AUTH_PW']))
414 $_SERVER['PHP_AUTH_PW'] = escapeString ($_SERVER['PHP_AUTH_PW']);
415
416// Now we need to be sure that the current user is the administrator.
417// The rest doesn't matter within this context.
418// We still continue to use the current authenticator though, but this will
419// last only till the UserAccounts remains the same. After that this file
420// will have to dig into the DB for the user accounts.
421require_once 'inc/auth.php';
422
423// This will not fail sanely, because getUserAccounts() depends on showError()
424$accounts = getUserAccounts();
425
426// Auth prompt risk being a little broken here due to config cache absence.
427$configCache = array();
428authenticate();
429if ($accounts[$_SERVER['PHP_AUTH_USER']]['user_id'] != 1)
430 die ('You are not allowed to upgrade the database. Ask your RackTables administrator to do this.');
431
432$dbver = getDatabaseVersion();
433echo 'Code version == ' . CODE_VERSION;
434echo '<br>Database version == ' . $dbver;
435if ($dbver == CODE_VERSION)
436 die ("<p align=justify>Your database seems to be up-to-date. " .
437 "Now the best thing to do would be to follow to the <a href='${root}'>main page</a> " .
438 "and explore your data. Have a nice day.</p>");
439
440foreach (getDBUpgradePath ($dbver, CODE_VERSION) as $batchid)
441 executeUpgradeBatch ($batchid);
442
443echo '<br>Database version == ' . getDatabaseVersion();
444echo "<p align=justify>Your database seems to be up-to-date. " .
445 "Now the best thing to do would be to follow to the <a href='${root}'>main page</a> " .
446 "and explore your data. Have a nice day.</p>";
447
448?>