Difference between revisions of "RackTablesDevelGuide"
Infrastation (talk | contribs) |
Infrastation (talk | contribs) m (updated SVN URLs and syntax, round 1) |
||
Line 1: | Line 1: | ||
= Working with SVN repository = | = Working with SVN repository = | ||
== getting a working copy of maintenance line == | == getting a working copy of maintenance line == | ||
− | + | <pre> | |
# Anyone can read from the repository, but writing requires an account on the server. | # Anyone can read from the repository, but writing requires an account on the server. | ||
− | svn co https://svn.racktables | + | svn co https://racktables.svn.sourceforge.net/svnroot/racktables/branches/RackTables/maintenance-0.17.x/ |
# Above command is for the maintenance line. For the trunk (see roadmap picture for exact | # Above command is for the maintenance line. For the trunk (see roadmap picture for exact | ||
# difference between two) respective command is: | # difference between two) respective command is: | ||
− | svn co https://svn.racktables | + | svn co https://racktables.svn.sourceforge.net/svnroot/racktables/trunk/RackTables/ |
− | + | </pre> | |
== editing files == | == editing files == | ||
Use your favourite editor. Consider existing code style (tabbed indentation, K&R-styled curly braces). | Use your favourite editor. Consider existing code style (tabbed indentation, K&R-styled curly braces). | ||
== viewing exact changes == | == viewing exact changes == | ||
− | + | <pre> | |
svn stat | svn stat | ||
svn diff | svn diff | ||
− | + | </pre> | |
== submitting work == | == submitting work == | ||
− | + | <pre> | |
# if you have an account | # if you have an account | ||
svn commit | svn commit | ||
Line 24: | Line 24: | ||
svn diff > my-cool-feature.patch | svn diff > my-cool-feature.patch | ||
# send the patch file by e-mail | # send the patch file by e-mail | ||
− | + | </pre> | |
− | |||
= Cutting a release = | = Cutting a release = | ||
== pre-release checklist == | == pre-release checklist == | ||
First make sure, that all necessary changes are already committed into the repository: | First make sure, that all necessary changes are already committed into the repository: | ||
− | + | * Update <tt>inc/config.php:$max_dict_key</tt>. | |
− | + | * Function <tt>database.php:getDictStats()</tt> must indicate all chapters, which belong to dictionary in tarball. Otherwise the stats are shown wrong. | |
− | + | * All release notes, if any, must appear both in upgrade script and in the README. | |
− | + | * Add current date to the version line in !ChangeLog. | |
− | + | * Make sure, that <tt>upgrade.php</tt> has new version listed in <tt>$versionhistory</tt> and <tt>executeUpgradeBatch()</tt>. It is normal to accumulate updates in </tt>executeUpgradeBatch()</tt> long before the release, this way on the release day you will have nothing to do in <tt>upgrade.php</tt>. But if you had no changes to DB since the last release, you are likely to see there changes missing. Just make sure the new version is in both places anyway. | |
− | + | * Bump up CODE_VERSION in <tt>inc/config.php</tt> and DB_VERSION in <tt>install/init-dictbase.sql</tt>. DON'T do this unless you really intend to make a release right now. | |
− | + | * svn stat; svn commit | |
− | + | Then export from the trunk (or maintenance branch) and test the codebase. | |
− | + | * The source must be installable from the web. Click next-next-next and you're done. Dump the DB. | |
− | + | * Demo data must also be loadable and functional. | |
− | + | * The source must detect and upgrade the previous release correctly. Load the last release with its demo data, then upgrade it and dump the DB. Now compare to the previous dump, there must be no meaningful differences. | |
− | + | <pre> | |
mysqldump --extended-insert=FALSE --order-by-primary racktables > ~/tmp/dump-upgraded.sql | mysqldump --extended-insert=FALSE --order-by-primary racktables > ~/tmp/dump-upgraded.sql | ||
mysqldump --extended-insert=FALSE --order-by-primary racktables > ~/tmp/dump-fresh.sql | mysqldump --extended-insert=FALSE --order-by-primary racktables > ~/tmp/dump-fresh.sql | ||
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql | diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql | ||
− | + | </pre> | |
− | + | * Now click a couple of links. | |
− | + | * Look into the error log of the server you used for the tests. There shouldn't be any messages produced by any test. | |
The above may happen repeated more, than once, until everything is fixed consistently. | The above may happen repeated more, than once, until everything is fixed consistently. | ||
== the release itself == | == the release itself == | ||
− | + | <pre> | |
# from trunk (like early 0.16.x): | # from trunk (like early 0.16.x): | ||
− | svn cp https://svn.racktables | + | svn cp https://racktables.svn.sourceforge.net/svnroot/racktables/trunk/RackTables https://racktables.svn.sourceforge.net/svnroot/racktables/tags/RackTables-X.Y.Z |
# from maintenance (like 0.17.x): | # from maintenance (like 0.17.x): | ||
− | svn cp https://svn.racktables | + | svn cp https://racktables.svn.sourceforge.net/svnroot/racktables/branches/RackTables/maintenance-X.Y.x https://racktables.svn.sourceforge.net/svnroot/racktables/tags/RackTables-X.Y.z |
− | + | </pre> | |
== rolling out == | == rolling out == | ||
− | + | * Make a tarball: | |
− | + | <pre> | |
− | svn export https://svn.racktables | + | svn export https://racktables.svn.sourceforge.net/svnroot/racktables/tags/RackTables-X.Y.z |
tar czf RackTables-X.Y.z.tar.gz RackTables-X.Y.z | tar czf RackTables-X.Y.z.tar.gz RackTables-X.Y.z | ||
− | + | </pre> | |
− | + | * Upload the tarball onto the distibution site. | |
− | + | * Update the live demo page: create a new database, export source tree, add a crontab line. Let old demo run for a couple of days more. | |
− | + | * Update Trac records (roadmap is updated from web): | |
− | + | <pre> | |
[root@racktables ~]# trac-admin /srv/trac/ | [root@racktables ~]# trac-admin /srv/trac/ | ||
Welcome to trac-admin 0.10.5 | Welcome to trac-admin 0.10.5 | ||
Line 75: | Line 74: | ||
Trac [/srv/trac]> version add X.Y.Z YYYY-MM-DD | Trac [/srv/trac]> version add X.Y.Z YYYY-MM-DD | ||
− | + | </pre> | |
− | + | * Edit <tt>/var/www/vhosts/racktables.org/header.php</tt> to update <tt>$lastrelease</tt>. Make sure "download" and "demo" do work. | |
− | + | * Write a Freshmeat release announce. | |
− | + | * Send a letter to the racktables-users list, if necessary. | |
= Config options = | = Config options = | ||
Variables are stored in the Config SQL table: | Variables are stored in the Config SQL table: | ||
− | + | <pre> | |
mysql> describe Config; | mysql> describe Config; | ||
+-------------+-----------------------+------+-----+---------+-------+ | +-------------+-----------------------+------+-----+---------+-------+ | ||
Line 95: | Line 94: | ||
+-------------+-----------------------+------+-----+---------+-------+ | +-------------+-----------------------+------+-----+---------+-------+ | ||
6 rows in set (0.00 sec) | 6 rows in set (0.00 sec) | ||
− | + | </pre> | |
Options are read and written with getConfigVar() and setConfigVar() functions respectively. The current naming convention for new options is to use descriptive expressions in ALL_CAPITAL_LETTERS with spaces REPLACED_WITH_UNDERSCORES. When adding a new option, check the following places: | Options are read and written with getConfigVar() and setConfigVar() functions respectively. The current naming convention for new options is to use descriptive expressions in ALL_CAPITAL_LETTERS with spaces REPLACED_WITH_UNDERSCORES. When adding a new option, check the following places: | ||
− | + | # upgrade.php | |
− | + | # install/init-dictbase.sql | |
− | + | # inc/ophandlers.php:resetUIConfig() | |
Default values must match regardless of the way they were set: initial setup, upgrade or UI reset. | Default values must match regardless of the way they were set: initial setup, upgrade or UI reset. | ||
= Exceptions and error handling (in development) = | = Exceptions and error handling (in development) = | ||
Error handling now can be done by exceptions mechanism. index.php and process.php have been wrapped in | Error handling now can be done by exceptions mechanism. index.php and process.php have been wrapped in | ||
− | + | <pre> | |
ob_start(); | ob_start(); | ||
try{ | try{ | ||
Line 112: | Line 111: | ||
print_error_nicely(); | print_error_nicely(); | ||
} | } | ||
− | + | </pre> | |
kind of code, so every exception you miss and not catch will be printed instead of the page that had to be printed. | kind of code, so every exception you miss and not catch will be printed instead of the page that had to be printed. | ||
− | This saves a lot of code, take a look at a function before | + | This saves a lot of code, take a look at a function |
− | + | <pre> | |
+ | // before | ||
+ | |||
function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '') | function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '') | ||
{ | { | ||
Line 136: | Line 137: | ||
return TRUE; | return TRUE; | ||
} | } | ||
− | + | ||
− | and after | + | // and after |
− | + | ||
function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '') | function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '') | ||
{ | { | ||
Line 146: | Line 147: | ||
return TRUE; | return TRUE; | ||
} | } | ||
− | + | </pre> | |
Function Database::updateWhere throws an exception if query is bad, and you can catch it if you want and handle it as necessary, or you can just ignore it and application will display error message with s stacktrace and such. | Function Database::updateWhere throws an exception if query is bad, and you can catch it if you want and handle it as necessary, or you can just ignore it and application will display error message with s stacktrace and such. | ||
Line 152: | Line 153: | ||
Exceptions registered so far: | Exceptions registered so far: | ||
− | | | + | {| border=1 |
− | | | + | ! class name !! by !! when |
− | | | + | |- |
− | | | + | | NotAuthorizedException || || Authentication failed or user is not authorized to view current page |
− | | | + | |- |
− | | | + | | CodeCompilationError || || RackCode compilcation failed |
− | | | + | |- |
− | | | + | | RealmNotFoundException || || Realm not found |
− | | | + | |- |
− | + | | EntityNotFoundException || || when an entity cannot be found in the database | |
+ | |- | ||
+ | | NotUniqueException || || when a record cannot be INSERTed into SQL table due to UNIQUE KEY constraint | ||
+ | |- | ||
+ | | InvalidArgException || lots of places || when at least one of the arguments provided to a function has invalid value | ||
+ | |- | ||
+ | | InvalidRequestArgException || usually thrown by assert*Arg() functions || wrong argument in Request | ||
+ | |- | ||
+ | | Exception || || When it's not worth to create a special exception class | ||
+ | |} | ||
---- | ---- |
Revision as of 13:55, 17 December 2009
Contents
Working with SVN repository
getting a working copy of maintenance line
# Anyone can read from the repository, but writing requires an account on the server. svn co https://racktables.svn.sourceforge.net/svnroot/racktables/branches/RackTables/maintenance-0.17.x/ # Above command is for the maintenance line. For the trunk (see roadmap picture for exact # difference between two) respective command is: svn co https://racktables.svn.sourceforge.net/svnroot/racktables/trunk/RackTables/
editing files
Use your favourite editor. Consider existing code style (tabbed indentation, K&R-styled curly braces).
viewing exact changes
svn stat svn diff
submitting work
# if you have an account svn commit # and if you don't svn diff > my-cool-feature.patch # send the patch file by e-mail
Cutting a release
pre-release checklist
First make sure, that all necessary changes are already committed into the repository:
- Update inc/config.php:$max_dict_key.
- Function database.php:getDictStats() must indicate all chapters, which belong to dictionary in tarball. Otherwise the stats are shown wrong.
- All release notes, if any, must appear both in upgrade script and in the README.
- Add current date to the version line in !ChangeLog.
- Make sure, that upgrade.php has new version listed in $versionhistory and executeUpgradeBatch(). It is normal to accumulate updates in executeUpgradeBatch() long before the release, this way on the release day you will have nothing to do in upgrade.php. But if you had no changes to DB since the last release, you are likely to see there changes missing. Just make sure the new version is in both places anyway.
- Bump up CODE_VERSION in inc/config.php and DB_VERSION in install/init-dictbase.sql. DON'T do this unless you really intend to make a release right now.
- svn stat; svn commit
Then export from the trunk (or maintenance branch) and test the codebase.
- The source must be installable from the web. Click next-next-next and you're done. Dump the DB.
- Demo data must also be loadable and functional.
- The source must detect and upgrade the previous release correctly. Load the last release with its demo data, then upgrade it and dump the DB. Now compare to the previous dump, there must be no meaningful differences.
mysqldump --extended-insert=FALSE --order-by-primary racktables > ~/tmp/dump-upgraded.sql mysqldump --extended-insert=FALSE --order-by-primary racktables > ~/tmp/dump-fresh.sql diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql
- Now click a couple of links.
- Look into the error log of the server you used for the tests. There shouldn't be any messages produced by any test.
The above may happen repeated more, than once, until everything is fixed consistently.
the release itself
# from trunk (like early 0.16.x): svn cp https://racktables.svn.sourceforge.net/svnroot/racktables/trunk/RackTables https://racktables.svn.sourceforge.net/svnroot/racktables/tags/RackTables-X.Y.Z # from maintenance (like 0.17.x): svn cp https://racktables.svn.sourceforge.net/svnroot/racktables/branches/RackTables/maintenance-X.Y.x https://racktables.svn.sourceforge.net/svnroot/racktables/tags/RackTables-X.Y.z
rolling out
- Make a tarball:
svn export https://racktables.svn.sourceforge.net/svnroot/racktables/tags/RackTables-X.Y.z tar czf RackTables-X.Y.z.tar.gz RackTables-X.Y.z
- Upload the tarball onto the distibution site.
- Update the live demo page: create a new database, export source tree, add a crontab line. Let old demo run for a couple of days more.
- Update Trac records (roadmap is updated from web):
[root@racktables ~]# trac-admin /srv/trac/ Welcome to trac-admin 0.10.5 Interactive Trac administration console. Copyright (c) 2003-2006 Edgewall Software Type: '?' or 'help' for help on commands. Trac [/srv/trac]> version add X.Y.Z YYYY-MM-DD
- Edit /var/www/vhosts/racktables.org/header.php to update $lastrelease. Make sure "download" and "demo" do work.
- Write a Freshmeat release announce.
- Send a letter to the racktables-users list, if necessary.
Config options
Variables are stored in the Config SQL table:
mysql> describe Config; +-------------+-----------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+-----------------------+------+-----+---------+-------+ | varname | varchar(32) | NO | PRI | | | | varvalue | varchar(255) | NO | | | | | vartype | enum('string','uint') | NO | | string | | | emptyok | enum('yes','no') | NO | | no | | | is_hidden | enum('yes','no') | NO | | yes | | | description | text | YES | | NULL | | +-------------+-----------------------+------+-----+---------+-------+ 6 rows in set (0.00 sec)
Options are read and written with getConfigVar() and setConfigVar() functions respectively. The current naming convention for new options is to use descriptive expressions in ALL_CAPITAL_LETTERS with spaces REPLACED_WITH_UNDERSCORES. When adding a new option, check the following places:
- upgrade.php
- install/init-dictbase.sql
- inc/ophandlers.php:resetUIConfig()
Default values must match regardless of the way they were set: initial setup, upgrade or UI reset.
Exceptions and error handling (in development)
Error handling now can be done by exceptions mechanism. index.php and process.php have been wrapped in
ob_start(); try{ ... ob_end_flush(); } catch { print_error_nicely(); }
kind of code, so every exception you miss and not catch will be printed instead of the page that had to be printed.
This saves a lot of code, take a look at a function
// before function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '') { if ($chapter_no <= 0) { showError ('Invalid args', __FUNCTION__); die; } global $dbxlink; $query = "update Dictionary set dict_value = '${dict_value}' where chapter_id=${chapter_no} " . "and dict_key=${dict_key} limit 1"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ('SQL query failed', __FUNCTION__); die; } return TRUE; } // and after function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '') { if ($chapter_no <= 0) throw InvalidArgException('$chapter_no', $chapter_no); Database::updateWhere(array('dict_value'=>$dict_value), 'Dictionary', array('chapter_id'=>$chapter_no, 'dict_key'=>$dict_key)); return TRUE; }
Function Database::updateWhere throws an exception if query is bad, and you can catch it if you want and handle it as necessary, or you can just ignore it and application will display error message with s stacktrace and such.
Exceptions registered so far:
class name | by | when |
---|---|---|
NotAuthorizedException | Authentication failed or user is not authorized to view current page | |
CodeCompilationError | RackCode compilcation failed | |
RealmNotFoundException | Realm not found | |
EntityNotFoundException | when an entity cannot be found in the database | |
NotUniqueException | when a record cannot be INSERTed into SQL table due to UNIQUE KEY constraint | |
InvalidArgException | lots of places | when at least one of the arguments provided to a function has invalid value |
InvalidRequestArgException | usually thrown by assert*Arg() functions | wrong argument in Request |
Exception | When it's not worth to create a special exception class |
!RackCode
|| BNF/regexp || description || || ID ::= {{{^alnum:([\. _~-]?alnum:)*$}}} || identifier || || PREDICATE ::= [ID] || identifier in brackets || || DEFINE ::= define PREDICATE || || || TAG ::= {ID} || identifier in curly braces || || AUTOTAG ::= {$ID} || identifier in curly braces, starting with dollar sign || || CTXMOD ::= clear | insert TAG | remove TAG || context modifier || || CTXMODLIST ::= CTXMODLIST CTXMOD | CTXMOD || context modifier list || || UNARY_EXPRESSION ::= true | false | TAG | AUTOTAG | PREDICATE | (EXPRESSION) | not UNARY_EXPRESSION || || || AND_EXPRESSION ::= AND_EXPRESSION and UNARY_EXPRESSION | UNARY_EXPRESSION || logical AND || || EXPRESSION ::= EXPRESSION or AND_EXPRESSION | AND_EXPRESSION || logical OR || || GRANT ::= allow EXPRESSION | deny EXPRESSION || || || DEFINITION ::= DEFINE EXPRESSION || || || ADJUSTMENT ::= context CTXMODLIST on EXPRESSION || || || CODETEXT ::= CODETEXT GRANT | CODETEXT DEFINITION | CODETEXT ADJUSTMENT | GRANT | DEFINITION | ADJUSTMENT || !RackCode permission text ||
Comments
Comments last from the first # to the end of the line and are filtered out automatically.
API
Hello, World!
To enable all !RackTables functions and load all necessary system data, it is enough to include one file. {{{ <?php
include ('inc/init.php'); // do something
?> }}}
There is a special parameter, which tells, if current file is intended to be run by web-server or from a command line. In the latter case neither authentication nor authorization is performed. {{{ <?php
$script_mode = TRUE; include ('inc/init.php');
echo "I am a crontab script!\n"; // do something
?> }}}
Realms
There are several principal realms in !RackTables database. Any realm contains zero, one or more records. Each such record is addressed by its ID (primary key).
object:: All servers, switches, UPSes, wireless, cable management and any other stuff, which is viewed and managed on the main "Objects" page. ipv4net:: All IPv4 networks. user:: All local accounts. There is at least one local account in any RackTables system (admin). There may be more. rack:: All racks. file:: All files. ipv4vs:: All IPv4 virtual services. ipv4rspool:: All IPv4 real server pools.
function scanRealmByText ($realm, $ftext = "")
realm:: String equal to one of the realms shown above. ftext:: Optional filter text. If this text is empty, all records of the realm are returned. If it is not empty, it is interpreted as a !RackCode expression. This expression is then evaluated against each record of the realm and only those records, for which the filter returned TRUE, are returned.
{{{ <?php
include ('inc/init.php');
$allusers = scanRealmByText ('user'); $smallnetworks = scanRealmByText ('ipv4net', '{small network}'); $unmounted_objects = scanRealmByText ('object', '{$unmounted}');
?> }}}
function spotEntity ($realm, $id)
This is the right function to get information about some record, for which you know where it belongs to and what its key value is. If this information isn't available, it has to be discovered somehow first.
realm:: Realm name. id:: Record number (key value, ID...).
{{{ <?php
include ('inc/init.php');
$adminuser = spotEntity ('user', 1); // Admin account is always number 1. $myspecialfile = spotEntity ('file', 12345); $serverinfo = spotEntity ('object', 67890);
?> }}}
function amplifyCell (&$record, $dummy = NULL)
It is assumed, that spotEntity() loads only basic data about the record requested. "Basic" means enough for renderCell() to do its job or to render a row in a table. To get detailed information about the "cell" it is necessary to execute amplifyCell() on it: {{{
include ('inc/init.php');
$adminuser = spotEntity ('user', 1); // Admin account is always number 1. amplifyCell ($adminuser);
$unmounted_objects = scanRealmByText ('object', '{$unmounted}'); array_walk ($unmounted_objects, 'amplifyCell');
}}}
function renderCell ($cell)
Render cell structure as a rectangular block with icon, name, tags and other information. Available since 0.17.2.
Dictionary
Since release 0.17.2 all records are stored in file {{{inc/dictionary.php}}}: {{{ $dictionary = array ( 1 => array ('chapter_id' => 1, 'dict_value' => 'BlackBox'), 2 => array ('chapter_id' => 1, 'dict_value' => 'PDU'), 3 => array ('chapter_id' => 1, 'dict_value' => 'Shelf'), 4 => array ('chapter_id' => 1, 'dict_value' => 'Server'), 5 => array ('chapter_id' => 1, 'dict_value' => 'DiskArray'), 6 => array ('chapter_id' => 1, 'dict_value' => 'TapeLibrary'), 7 => array ('chapter_id' => 1, 'dict_value' => 'Router'), 8 => array ('chapter_id' => 1, 'dict_value' => 'Network switch'), 9 => array ('chapter_id' => 1, 'dict_value' => 'PatchPanel'), 10 => array ('chapter_id' => 1, 'dict_value' => 'CableOrganizer'), 11 => array ('chapter_id' => 1, 'dict_value' => 'spacer'), 12 => array ('chapter_id' => 1, 'dict_value' => 'UPS'), [...] }}}
meaning of chapter ID
id
wikilinks
It is possible to render a record as an URL: {{{ http://somewhere/ }}}. However, it is up to the editor, if to include URL into description or not. URLs can be added, changed and removed from particular records later, if it makes sense.
G-markers
Text before the marker is always used for OPTGROUP in SELECT element. The difference is in the way the text is rendered as a plain text or a clickable URL. GSKIP makes OPTGROUP name to be skipped. GPASS passes OPTGROUP name on the text: {{{
719 => array ('chapter_id' => 24, 'dict_value' => ' http://cisco.com/en/US/products/ps9438/index.html'), 720 => array ('chapter_id' => 13, 'dict_value' => ' http://openbsd.org/33.html'),
}}}