<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.racktables.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Infrastation</id>
	<title>RackTables Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.racktables.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Infrastation"/>
	<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php/Special:Contributions/Infrastation"/>
	<updated>2026-04-14T17:44:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.34.1</generator>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=957</id>
		<title>ProjectInfrastructure</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=957"/>
		<updated>2024-02-25T18:53:31Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* git mirrors (code) */ git.racktables.org replaces code.racktables.org&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Mapping of assets==&lt;br /&gt;
=== DNS ===&lt;br /&gt;
racktables.org domain is currently registered through GoDaddy, which also provides 2 free nameservers. Aaron manages the domain.&lt;br /&gt;
&lt;br /&gt;
=== racktables.org mail ===&lt;br /&gt;
racktables.org mail is handled by zohomail.com.  Zoho Mail offers free service for up to three e-mail addresses.  Two mailing lists exist, &amp;quot;devteam&amp;quot; and &amp;quot;info&amp;quot;, which are setup to forward all messages to every development team member.&lt;br /&gt;
&lt;br /&gt;
=== racktables-users mail ===&lt;br /&gt;
FreeLists is a great free service without accompanying advertisements. The only drawback about it is that it is non-profit and can go down some day, hence '''make sure the list of subscribers is present in a recent backup tarball'''.&lt;br /&gt;
&lt;br /&gt;
To manage the mailing list, go to [http://www.freelists.org/login.html this page] and use the form to the left. Input your email subscribed to the list. If you don't remember your password, log in with an empty password, in this case the system will email you a one-time token to input on the next screen. Once logged in, you will see a very long SELECT with all lists of FreeLists. Click on the SELECT and press &amp;quot;r&amp;quot; to get to items starting with R. Select racktables-users and press &amp;quot;Select list&amp;quot; below. On the next screen press &amp;quot;Admin Menu&amp;quot;. To manage individual subscribers, select one on the &amp;quot;Current List Users&amp;quot; SELECT and press &amp;quot;User Options&amp;quot;. VACATION, MODPOST and NOPOST are the most frequently used flags, their meaning is explained inline.&lt;br /&gt;
&lt;br /&gt;
Please think twice before changing any of the list-wide settings on the &amp;quot;Edit List Config&amp;quot; menu, these settings influence all subscribers of racktables-users.&lt;br /&gt;
&lt;br /&gt;
=== Most web-sites (demo, bugs, wiki, www) ===&lt;br /&gt;
Arnaud Launay is the server administrator. To get SSH access there, be a team member and find someone who already has access.&lt;br /&gt;
&lt;br /&gt;
=== git mirrors (code) ===&lt;br /&gt;
The [https://git.racktables.org/ git mirror server] exists to provide backups in case github.com goes kaput. The most up to date copies are available through both a web-interface (gitweb) and git clone URLs, for example:&lt;br /&gt;
 git clone https://git.racktables.org/racktables&lt;br /&gt;
 Cloning into 'racktables'...&lt;br /&gt;
The server synchronizes each repository both hourly and after a new commit appears on github.com (via a webhook). It also keeps up to 90 days of backups for every repository.&lt;br /&gt;
&lt;br /&gt;
This cannot be hosted with the other servers because the git mirror server notifies the MantisBT server so the source control integration fetches new changes into its database. However, the TCP connection does not establish within the same network, it needs to originate from outside.&lt;br /&gt;
&lt;br /&gt;
=== Statistics ===&lt;br /&gt;
SF download stats work for the downloads. Google analytics was used in the past for racktables.org and demo.racktables.org, but was removed in 2019.&lt;br /&gt;
&lt;br /&gt;
=== downloads ===&lt;br /&gt;
SourceForge project FRS (File Release System, a CDN of sorts).&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==Backup procedure==&lt;br /&gt;
===the backup directory===&lt;br /&gt;
 mkdir RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
 cd RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
&lt;br /&gt;
===racktables-users mailing list===&lt;br /&gt;
Send an empty email with subject &amp;quot;who racktables-users&amp;quot; to ecartis@freelists.org and you will get a list of all subscribers. Save it to the backup directory as &amp;quot;racktables-users.txt&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Git repositories===&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables.git racktables.git&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables-contribs.git racktables-contribs.git&lt;br /&gt;
&lt;br /&gt;
===Bug tracker, wiki data, www===&lt;br /&gt;
SSH into the host and create a new backup directory.&lt;br /&gt;
 mkdir ~/RackTables-apps-backup&lt;br /&gt;
 cd ~/RackTables-apps-backup&lt;br /&gt;
&lt;br /&gt;
Copy the applications into the backup directory on the server. This is important because they contain uploaded files and configuration settings.&lt;br /&gt;
 tar czf bugs.tar.gz ~/www/bugs&lt;br /&gt;
 tar czf wiki.tar.gz ~/www/wiki&lt;br /&gt;
 tar czf www.tar.gz ~/www/www&lt;br /&gt;
&lt;br /&gt;
Export the database for each application.&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; bugs.sql&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; wiki.sql&lt;br /&gt;
&lt;br /&gt;
SCP the remote server directory to the local backup directory, then remove it from the server.&lt;br /&gt;
&lt;br /&gt;
===Final check===&lt;br /&gt;
Now the backup directory must have these files/directories:&lt;br /&gt;
* racktables.git&lt;br /&gt;
* racktables-contribs.git&lt;br /&gt;
* racktables-users.txt&lt;br /&gt;
* RackTables-apps-backup&lt;br /&gt;
** wiki.tar.gz&lt;br /&gt;
** wiki.sql&lt;br /&gt;
** bugs.tar.gz&lt;br /&gt;
** bugs.sql&lt;br /&gt;
** www.tar.gz&lt;br /&gt;
'''SAVE THE BACKUP FOLDER TO A SAFE PLACE'''&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=955</id>
		<title>ProjectInfrastructure</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=955"/>
		<updated>2022-11-13T19:03:58Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Statistics */ Google Analytics was retired three years ago.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Mapping of assets==&lt;br /&gt;
=== DNS ===&lt;br /&gt;
racktables.org domain is currently registered through GoDaddy, which also provides 2 free nameservers. Aaron manages the domain.&lt;br /&gt;
&lt;br /&gt;
=== racktables.org mail ===&lt;br /&gt;
racktables.org mail is handled by zohomail.com.  Zoho Mail offers free service for up to three e-mail addresses.  Two mailing lists exist, &amp;quot;devteam&amp;quot; and &amp;quot;info&amp;quot;, which are setup to forward all messages to every development team member.&lt;br /&gt;
&lt;br /&gt;
=== racktables-users mail ===&lt;br /&gt;
FreeLists is a great free service without accompanying advertisements. The only drawback about it is that it is non-profit and can go down some day, hence '''make sure the list of subscribers is present in a recent backup tarball'''.&lt;br /&gt;
&lt;br /&gt;
To manage the mailing list, go to [http://www.freelists.org/login.html this page] and use the form to the left. Input your email subscribed to the list. If you don't remember your password, log in with an empty password, in this case the system will email you a one-time token to input on the next screen. Once logged in, you will see a very long SELECT with all lists of FreeLists. Click on the SELECT and press &amp;quot;r&amp;quot; to get to items starting with R. Select racktables-users and press &amp;quot;Select list&amp;quot; below. On the next screen press &amp;quot;Admin Menu&amp;quot;. To manage individual subscribers, select one on the &amp;quot;Current List Users&amp;quot; SELECT and press &amp;quot;User Options&amp;quot;. VACATION, MODPOST and NOPOST are the most frequently used flags, their meaning is explained inline.&lt;br /&gt;
&lt;br /&gt;
Please think twice before changing any of the list-wide settings on the &amp;quot;Edit List Config&amp;quot; menu, these settings influence all subscribers of racktables-users.&lt;br /&gt;
&lt;br /&gt;
=== Most web-sites (demo, bugs, wiki, www) ===&lt;br /&gt;
Arnaud Launay is the server administrator. To get SSH access there, be a team member and find someone who already has access.&lt;br /&gt;
&lt;br /&gt;
=== git mirrors (code) ===&lt;br /&gt;
The [https://code.racktables.org/ git mirror server] exists to provide backups in case github.com goes kaput. The most up to date copies are available through both a web-interface (gitweb) and git clone URLs, for example:&lt;br /&gt;
 git clone https://code.racktables.org/racktables&lt;br /&gt;
 Cloning into 'racktables'...&lt;br /&gt;
The server synchronizes each repository both hourly and after a new commit appears on github.com (via a webhook). It also keeps up to 90 days of backups for every repository.&lt;br /&gt;
&lt;br /&gt;
This cannot be hosted with the other servers because the git mirror server notifies the MantisBT server so the source control integration fetches new changes into its database. However, the TCP connection does not establish within the same network, it needs to originate from outside.&lt;br /&gt;
&lt;br /&gt;
=== Statistics ===&lt;br /&gt;
SF download stats work for the downloads. Google analytics was used in the past for racktables.org and demo.racktables.org, but was removed in 2019.&lt;br /&gt;
&lt;br /&gt;
=== downloads ===&lt;br /&gt;
SourceForge project FRS (File Release System, a CDN of sorts).&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==Backup procedure==&lt;br /&gt;
===the backup directory===&lt;br /&gt;
 mkdir RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
 cd RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
&lt;br /&gt;
===racktables-users mailing list===&lt;br /&gt;
Send an empty email with subject &amp;quot;who racktables-users&amp;quot; to ecartis@freelists.org and you will get a list of all subscribers. Save it to the backup directory as &amp;quot;racktables-users.txt&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Git repositories===&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables.git racktables.git&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables-contribs.git racktables-contribs.git&lt;br /&gt;
&lt;br /&gt;
===Bug tracker, wiki data, www===&lt;br /&gt;
SSH into the host and create a new backup directory.&lt;br /&gt;
 mkdir ~/RackTables-apps-backup&lt;br /&gt;
 cd ~/RackTables-apps-backup&lt;br /&gt;
&lt;br /&gt;
Copy the applications into the backup directory on the server. This is important because they contain uploaded files and configuration settings.&lt;br /&gt;
 tar czf bugs.tar.gz ~/www/bugs&lt;br /&gt;
 tar czf wiki.tar.gz ~/www/wiki&lt;br /&gt;
 tar czf www.tar.gz ~/www/www&lt;br /&gt;
&lt;br /&gt;
Export the database for each application.&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; bugs.sql&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; wiki.sql&lt;br /&gt;
&lt;br /&gt;
SCP the remote server directory to the local backup directory, then remove it from the server.&lt;br /&gt;
&lt;br /&gt;
===Final check===&lt;br /&gt;
Now the backup directory must have these files/directories:&lt;br /&gt;
* racktables.git&lt;br /&gt;
* racktables-contribs.git&lt;br /&gt;
* racktables-users.txt&lt;br /&gt;
* RackTables-apps-backup&lt;br /&gt;
** wiki.tar.gz&lt;br /&gt;
** wiki.sql&lt;br /&gt;
** bugs.tar.gz&lt;br /&gt;
** bugs.sql&lt;br /&gt;
** www.tar.gz&lt;br /&gt;
'''SAVE THE BACKUP FOLDER TO A SAFE PLACE'''&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=953</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=953"/>
		<updated>2022-02-16T13:06:39Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* updating demo */ use &amp;quot;git add -p&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -p&amp;lt;/tt&amp;gt; the new file again (note the flag). Add relevant chunks only. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo yes&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=951</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=951"/>
		<updated>2022-02-15T20:30:27Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: The CI was Travis, but it isn't anymore.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo yes&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=949</id>
		<title>ProjectInfrastructure</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=949"/>
		<updated>2021-08-20T18:46:24Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: describe code.racktables.org&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Mapping of assets==&lt;br /&gt;
=== DNS ===&lt;br /&gt;
racktables.org domain is currently registered through GoDaddy, which also provides 2 free nameservers. Aaron manages the domain.&lt;br /&gt;
&lt;br /&gt;
=== racktables.org mail ===&lt;br /&gt;
racktables.org mail is handled by zohomail.com.  Zoho Mail offers free service for up to three e-mail addresses.  Two mailing lists exist, &amp;quot;devteam&amp;quot; and &amp;quot;info&amp;quot;, which are setup to forward all messages to every development team member.&lt;br /&gt;
&lt;br /&gt;
=== racktables-users mail ===&lt;br /&gt;
FreeLists is a great free service without accompanying advertisements. The only drawback about it is that it is non-profit and can go down some day, hence '''make sure the list of subscribers is present in a recent backup tarball'''.&lt;br /&gt;
&lt;br /&gt;
To manage the mailing list, go to [http://www.freelists.org/login.html this page] and use the form to the left. Input your email subscribed to the list. If you don't remember your password, log in with an empty password, in this case the system will email you a one-time token to input on the next screen. Once logged in, you will see a very long SELECT with all lists of FreeLists. Click on the SELECT and press &amp;quot;r&amp;quot; to get to items starting with R. Select racktables-users and press &amp;quot;Select list&amp;quot; below. On the next screen press &amp;quot;Admin Menu&amp;quot;. To manage individual subscribers, select one on the &amp;quot;Current List Users&amp;quot; SELECT and press &amp;quot;User Options&amp;quot;. VACATION, MODPOST and NOPOST are the most frequently used flags, their meaning is explained inline.&lt;br /&gt;
&lt;br /&gt;
Please think twice before changing any of the list-wide settings on the &amp;quot;Edit List Config&amp;quot; menu, these settings influence all subscribers of racktables-users.&lt;br /&gt;
&lt;br /&gt;
=== Most web-sites (demo, bugs, wiki, www) ===&lt;br /&gt;
Arnaud Launay is the server administrator. To get SSH access there, be a team member and find someone who already has access.&lt;br /&gt;
&lt;br /&gt;
=== git mirrors (code) ===&lt;br /&gt;
The [https://code.racktables.org/ git mirror server] exists to provide backups in case github.com goes kaput. The most up to date copies are available through both a web-interface (gitweb) and git clone URLs, for example:&lt;br /&gt;
 git clone https://code.racktables.org/racktables&lt;br /&gt;
 Cloning into 'racktables'...&lt;br /&gt;
The server synchronizes each repository both hourly and after a new commit appears on github.com (via a webhook). It also keeps up to 90 days of backups for every repository.&lt;br /&gt;
&lt;br /&gt;
This cannot be hosted with the other servers because the git mirror server notifies the MantisBT server so the source control integration fetches new changes into its database. However, the TCP connection does not establish within the same network, it needs to originate from outside.&lt;br /&gt;
&lt;br /&gt;
=== Statistics ===&lt;br /&gt;
SF download stats work for the downloads. Google analytics works for the content of racktables.org and demo.racktables.org (it does not currently display its admin interface in every browser).&lt;br /&gt;
&lt;br /&gt;
=== downloads ===&lt;br /&gt;
SourceForge project FRS (File Release System, a CDN of sorts).&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==Backup procedure==&lt;br /&gt;
===the backup directory===&lt;br /&gt;
 mkdir RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
 cd RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
&lt;br /&gt;
===racktables-users mailing list===&lt;br /&gt;
Send an empty email with subject &amp;quot;who racktables-users&amp;quot; to ecartis@freelists.org and you will get a list of all subscribers. Save it to the backup directory as &amp;quot;racktables-users.txt&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Git repositories===&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables.git racktables.git&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables-contribs.git racktables-contribs.git&lt;br /&gt;
&lt;br /&gt;
===Bug tracker, wiki data, www===&lt;br /&gt;
SSH into the host and create a new backup directory.&lt;br /&gt;
 mkdir ~/RackTables-apps-backup&lt;br /&gt;
 cd ~/RackTables-apps-backup&lt;br /&gt;
&lt;br /&gt;
Copy the applications into the backup directory on the server. This is important because they contain uploaded files and configuration settings.&lt;br /&gt;
 tar czf bugs.tar.gz ~/www/bugs&lt;br /&gt;
 tar czf wiki.tar.gz ~/www/wiki&lt;br /&gt;
 tar czf www.tar.gz ~/www/www&lt;br /&gt;
&lt;br /&gt;
Export the database for each application.&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; bugs.sql&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; wiki.sql&lt;br /&gt;
&lt;br /&gt;
SCP the remote server directory to the local backup directory, then remove it from the server.&lt;br /&gt;
&lt;br /&gt;
===Final check===&lt;br /&gt;
Now the backup directory must have these files/directories:&lt;br /&gt;
* racktables.git&lt;br /&gt;
* racktables-contribs.git&lt;br /&gt;
* racktables-users.txt&lt;br /&gt;
* RackTables-apps-backup&lt;br /&gt;
** wiki.tar.gz&lt;br /&gt;
** wiki.sql&lt;br /&gt;
** bugs.tar.gz&lt;br /&gt;
** bugs.sql&lt;br /&gt;
** www.tar.gz&lt;br /&gt;
'''SAVE THE BACKUP FOLDER TO A SAFE PLACE'''&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=947</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=947"/>
		<updated>2021-07-23T12:47:06Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: Reformat the tables for a more modern visual style.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Breed Code&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Command Code&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table (for all ports) as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table (for a specific port only) as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
! &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
! &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &amp;amp;#10003;&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Plugins&amp;diff=945</id>
		<title>Plugins</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Plugins&amp;diff=945"/>
		<updated>2021-07-23T11:58:59Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: Use fixed-width style for pathnames and PHP code, lose some passive voice, use m-dashes and arrows.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Plugins provide the ability to add functionality to RackTables and, in some cases, override existing behavior.  See the [https://github.com/RackTables/racktables-contribs racktables-contribs] repository for user-submitted plugins.&lt;br /&gt;
&lt;br /&gt;
=== States ===&lt;br /&gt;
A plugin may be in one of three states.&lt;br /&gt;
* Not installed &amp;amp;mdash; it exists in the plugins directory, but hasn't been installed yet&lt;br /&gt;
* Enabled &amp;amp;mdash; it has been installed and is active&lt;br /&gt;
* Disabled &amp;amp;mdash; it has been installed and was subsequently disabled&lt;br /&gt;
&lt;br /&gt;
=== Administration ===&lt;br /&gt;
To install a plugin, first copy it to the &amp;lt;code&amp;gt;plugins&amp;lt;/code&amp;gt; directory.  Then visit the Configuration &amp;amp;rarr; Plugins page and click the Edit tab.  The new plugin should be listed with a state of &amp;quot;Not installed&amp;quot;.  Click the Install icon.&lt;br /&gt;
&lt;br /&gt;
To upgrade a plugin, first backup the appropriate &amp;lt;code&amp;gt;plugins&amp;lt;/code&amp;gt; sub-directory.  To be safe, it's also a good idea to backup your entire database.  Afterwards, copy over the new version into the &amp;lt;code&amp;gt;plugins&amp;lt;/code&amp;gt; directory.  When you visit the Edit tab of the Configuration &amp;amp;rarr; Plugins page, you should see a variance in the Code Version and DB Version columns.  Click the Upgrade icon.&lt;br /&gt;
&lt;br /&gt;
== Writing a Plugin ==&lt;br /&gt;
The old procedure involves placing a PHP file in the &amp;lt;code&amp;gt;plugins&amp;lt;/code&amp;gt; directory of your RackTables installation.  That method has limited functionality and may be deprecated at a later date.&lt;br /&gt;
&lt;br /&gt;
RackTables release 0.21.0 had introduced a new plugin architecture, which includes the ability to install, uninstall, enable and disable plugins from the web interface.  It is assumed that each plugin resides in its own directory within the main &amp;lt;code&amp;gt;plugins&amp;lt;/code&amp;gt; directory, and that each plugin contains a &amp;lt;code&amp;gt;plugin.php&amp;lt;/code&amp;gt; file which includes certain functions.  The design was inspired by [http://docs.cacti.net/plugins Cacti's] plugin architecture.&lt;br /&gt;
&lt;br /&gt;
=== Files ===&lt;br /&gt;
Each plugin is expected to contain certain files.  While the only required file is &amp;lt;code&amp;gt;plugin.php&amp;lt;/code&amp;gt;, it is recommended that you include all mentioned files, especially if you intend to publish it for others.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;README&amp;lt;/code&amp;gt; &amp;amp;mdash; Provide a general description.  If you expect the plugin to be accepted into the racktables-contribs repo, use the [https://help.github.com/articles/github-flavored-markdown/ Markdown] format.&lt;br /&gt;
* &amp;lt;code&amp;gt;ChangeLog&amp;lt;/code&amp;gt; &amp;amp;mdash; Document changes made to each revision.&lt;br /&gt;
* &amp;lt;code&amp;gt;LICENSE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Name&lt;br /&gt;
!Returns&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;plugin_PLUGINNAME_info()&amp;lt;/code&amp;gt;&lt;br /&gt;
|array&lt;br /&gt;
|Array keys are &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;longname&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;home_url&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;plugin_PLUGINNAME_init()&amp;lt;/code&amp;gt;&lt;br /&gt;
|void&lt;br /&gt;
|Initialize the plugin.  Register pages, tabs, triggers, handlers, etc.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;plugin_PLUGINNAME_install()&amp;lt;/code&amp;gt;&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (create tables, create Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;plugin_PLUGINNAME_uninstall()&amp;lt;/code&amp;gt;&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (drop tables, delete Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;plugin_PLUGINNAME_upgrade()&amp;lt;/code&amp;gt;&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (alter tables, modify Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;plugin_PLUGINNAME_HOOKNAME()&amp;lt;/code&amp;gt;&lt;br /&gt;
|varies&lt;br /&gt;
|Run when called by a system-level function (described below in the Hooks section)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
These are used in cases where custom functions defined by a plugin should be called at specific times.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* When resetting or deleting an object&lt;br /&gt;
* Resetting the Config settings (Configuration &amp;amp;rarr; User interface)&lt;br /&gt;
* Viewing the Data Integrity report&lt;br /&gt;
&lt;br /&gt;
Hooks are already supported in some functions, such as &amp;lt;code&amp;gt;commitResetObject()&amp;lt;/code&amp;gt;.  If you need it supported by another function, create a GitHub pull request and it will probably be accepted.&lt;br /&gt;
&lt;br /&gt;
=== Upgrades ===&lt;br /&gt;
The &amp;lt;code&amp;gt;plugin_PLUGINNAME_upgrade()&amp;lt;/code&amp;gt; function is mandatory.  If this is the first version, or if no upgrade steps are required, simply have the function return &amp;lt;code&amp;gt;TRUE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
There are many cases where upgrades involve adding/modifying/deleting tables, Config settings or other pieces of information.  These transitions should be handled by the upgrade function.&lt;br /&gt;
&lt;br /&gt;
Example modeled after the core RackTables upgrader:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function plugin_myplugin_upgrade ()&lt;br /&gt;
{&lt;br /&gt;
	$db_info = getPlugin ('myplugin');&lt;br /&gt;
	$v1 = $db_info['db_version'];&lt;br /&gt;
	$code_info = plugin_plugin_info ();&lt;br /&gt;
	$v2 = $code_info['version'];&lt;br /&gt;
	&lt;br /&gt;
	if ($v1 == $v2)&lt;br /&gt;
		throw new RackTablesError ('Versions are identical', RackTablesError::INTERNAL);&lt;br /&gt;
&lt;br /&gt;
	// find the upgrade path to be taken&lt;br /&gt;
	$versionhistory = array&lt;br /&gt;
	(&lt;br /&gt;
		'1.0',&lt;br /&gt;
		'2.0',&lt;br /&gt;
		'3.0'&lt;br /&gt;
	);&lt;br /&gt;
	$skip = TRUE;&lt;br /&gt;
	$path = NULL;&lt;br /&gt;
	foreach ($versionhistory as $v)&lt;br /&gt;
	{&lt;br /&gt;
		if ($skip and $v == $v1)&lt;br /&gt;
		{&lt;br /&gt;
			$skip = FALSE;&lt;br /&gt;
			$path = array();&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		if ($skip)&lt;br /&gt;
			continue;&lt;br /&gt;
		$path[] = $v;&lt;br /&gt;
		if ($v == $v2)&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
	if ($path === NULL or ! count ($path))&lt;br /&gt;
		throw new RackTablesError ('Unable to determine upgrade path', RackTablesError::INTERNAL);&lt;br /&gt;
&lt;br /&gt;
	// build the list of queries to execute&lt;br /&gt;
	$queries = array ();&lt;br /&gt;
	foreach ($path as $batchid)&lt;br /&gt;
	{&lt;br /&gt;
		switch ($batchid)&lt;br /&gt;
		{&lt;br /&gt;
			case '2.0':&lt;br /&gt;
				// perform some upgrade step here&lt;br /&gt;
				$queries[] = &amp;quot;UPDATE Plugin SET version = '2.0' WHERE name = 'myplugin'&amp;quot;;&lt;br /&gt;
				break;&lt;br /&gt;
			case '3.0':&lt;br /&gt;
				// perform some upgrade step here&lt;br /&gt;
				$queries[] = &amp;quot;UPDATE Plugin SET version = '3.0' WHERE name = 'myplugin'&amp;quot;;&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
				throw new RackTablesError (&amp;quot;Preparing to upgrade to $batchid failed&amp;quot;, RackTablesError::INTERNAL);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// execute the queries&lt;br /&gt;
	global $dbxlink;&lt;br /&gt;
	foreach ($queries as $q)&lt;br /&gt;
	{&lt;br /&gt;
		try&lt;br /&gt;
		{&lt;br /&gt;
			$result = $dbxlink-&amp;gt;query ($q);&lt;br /&gt;
		}&lt;br /&gt;
		catch (PDOException $e)&lt;br /&gt;
		{&lt;br /&gt;
			$errorInfo = $dbxlink-&amp;gt;errorInfo();&lt;br /&gt;
			throw new RackTablesError (&amp;quot;Query: ${errorInfo[2]}&amp;quot;, RackTablesError::INTERNAL);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=943</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=943"/>
		<updated>2020-10-05T18:03:18Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* updating demo */ I had retired Google Analytics from this job a while ago.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo yes&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=941</id>
		<title>SourceCode</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=941"/>
		<updated>2020-09-03T12:10:45Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: squash two PHP version sections into one&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== the source code repository==&lt;br /&gt;
RackTables project uses git as version control system for the source code ([https://github.com/RackTables/racktables GitHub], [https://code.racktables.org/ read-only mirror]). The repository includes the following notable branches:&lt;br /&gt;
;master&lt;br /&gt;
:The main development branch, that is, the default branch for all new changes.&lt;br /&gt;
;maintenance-0.21.x&lt;br /&gt;
:The maintenance branch for 0.21.x stable releases (see the roadmap).&lt;br /&gt;
;maintenance-0.20.x&lt;br /&gt;
;maintenance-0.19.x&lt;br /&gt;
;maintenance-0.18.x&lt;br /&gt;
;maintenance-0.17.x&lt;br /&gt;
;maintenance-0.16.x&lt;br /&gt;
:Archived maintenance branches, which do not accept any new changes.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to get a working copy of the repository:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# from the RackTables project repository&lt;br /&gt;
git clone git://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:RackTables/racktables.git&lt;br /&gt;
&lt;br /&gt;
# from a GitHub fork (of your own or not)&lt;br /&gt;
git clone git://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:username/racktables.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the first two schemas (&amp;lt;tt&amp;gt;git://&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;https://&amp;lt;/tt&amp;gt;) are always '''read-only''' and the third (&amp;lt;tt&amp;gt;ssh://&amp;lt;/tt&amp;gt;) may be '''read-only or read-write'''.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to examine local unsaved changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The recommended way of contributing the work is to:&lt;br /&gt;
# fork RackTables on GitHub,&lt;br /&gt;
# push finished commits to the fork, and&lt;br /&gt;
# create a pull request.&lt;br /&gt;
&lt;br /&gt;
In the case of a private repository use a [http://git-scm.com/docs/git-format-patch git format-patch] command to produce a patch and send it to the developers. When sending a patch, please make sure your branch is [http://git-scm.com/book/en/Git-Branching-Rebasing rebased] onto the latest respective (master or maintenance) branch.&lt;br /&gt;
&lt;br /&gt;
== PHP code style guide ==&lt;br /&gt;
Almost all PHP source code in RackTables is original and the following style guide applies:&lt;br /&gt;
==== use [http://en.wikipedia.org/wiki/Indent_style#Allman_style Allman] indent style. ====&lt;br /&gt;
'''Why:''' Allman style is very diff-friendly when it comes to control structures. In Allman style expansion of a single inner operator into a larger code block (and vice versa) leaves both the inner (controlled) and the outer (controlling) operator intact:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 for ($i = 0; $i &amp;lt; 10; $i++)&lt;br /&gt;
+{&lt;br /&gt;
 	doSomething ($i);&lt;br /&gt;
+	doSomethingElse();&lt;br /&gt;
+}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This works seamlessly at any indentation level and for all control structures (&amp;lt;tt&amp;gt;if/for/foreach/while&amp;lt;/tt&amp;gt;), however deeply nested in each other. With careful indentation of &amp;lt;tt&amp;gt;case&amp;lt;/tt&amp;gt; within &amp;lt;tt&amp;gt;switch&amp;lt;/tt&amp;gt; it will work same well:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
switch ($var)&lt;br /&gt;
{&lt;br /&gt;
	case 1:&lt;br /&gt;
		doSomething();&lt;br /&gt;
		break;&lt;br /&gt;
	case 2:&lt;br /&gt;
-	{&lt;br /&gt;
-		doSomethingElse();&lt;br /&gt;
		return 3;&lt;br /&gt;
-	}&lt;br /&gt;
	case 3:&lt;br /&gt;
+	{&lt;br /&gt;
+		doSomethingCompletelyDifferent();&lt;br /&gt;
		return 4;&lt;br /&gt;
+	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Being diff-friendly means being friendly to a version control system (Subversion in the past, git at present). Neither the VCS nor the Allman style alone guarantee one's commits to be focused and clean. However, Allman style encourages commits that change only the lines that really need to be changed.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
==== use exactly one tab per indentation level ====&lt;br /&gt;
'''Why:''' different people prefer different (2, 4, 8...) amount of &amp;quot;spaces&amp;quot; per indentation level. Some projects use space indentation and force everyone to hit Space (Backspace) N times each time the developer needs to indent (unindent), regardless of their own preferred value of N. RackTables makes use of Tab and enables every developer to configure their editor for their preferred &amp;quot;tab width&amp;quot; (the screenshot below stands for tab width 2). Note that the tab width must stand for the amount of spaces the editor ''displays'', but not ''generates instead of'', that is, in the source file '''tabs must remain tabs'''.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
[[File:interface_tab2.png]]&lt;br /&gt;
&lt;br /&gt;
==== wrap lines longer than 100-120 characters ====&lt;br /&gt;
'''Why:''' readability. Prior experience tells that old-school 80-columns margin just doesn't work with descriptive naming of variables and functions (i.e. descriptive == heavily wrapped). On the other hand, avoid making a readable, 200-characters long line even if it fits your monitor. Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for function arguments ====&lt;br /&gt;
'''Why:''' the purpose is twofold. On one hand it allows to pack a lot of arguments and expressions into a single function call without losing track of what is being done and no temporary variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		usePreparedUpdateBlade&lt;br /&gt;
		(&lt;br /&gt;
			'Port',&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'name' =&amp;gt; $port_name,&lt;br /&gt;
				'type' =&amp;gt; $port_type_id,&lt;br /&gt;
				'label' =&amp;gt; $port_label,&lt;br /&gt;
				'reservation_comment' =&amp;gt; $reservation_comment,&lt;br /&gt;
				'l2address' =&amp;gt; nullEmptyStr ($db_l2address),&lt;br /&gt;
			),&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'id' =&amp;gt; $port_id,&lt;br /&gt;
				'object_id' =&amp;gt; $object_id&lt;br /&gt;
			)&lt;br /&gt;
		);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, it allows to group function arguments add/or add comments to them:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function get8021QSyncOptions&lt;br /&gt;
(&lt;br /&gt;
	$vswitch,&lt;br /&gt;
	$D, // desired config&lt;br /&gt;
	$C, // cached config&lt;br /&gt;
	$R  // running-config&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
	$default_port = array&lt;br /&gt;
	(&lt;br /&gt;
		'mode' =&amp;gt; 'access',&lt;br /&gt;
		'allowed' =&amp;gt; array (VLAN_DFL_ID),&lt;br /&gt;
		'native' =&amp;gt; VLAN_DFL_ID,&lt;br /&gt;
	);&lt;br /&gt;
	$ret = array();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for arrays ====&lt;br /&gt;
'''Why:''' due to a few reasons. First, multiline array assignment/initialization can accommodate a notable amount of supplementary expressions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ret[$pn] = array&lt;br /&gt;
(&lt;br /&gt;
	'status' =&amp;gt; 'martian_conflict',&lt;br /&gt;
	'left' =&amp;gt; array_key_exists ($pn, $C) ? $C[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
	'right' =&amp;gt; array_key_exists ($pn, $R) ? $R[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second, updates to arrays that serve as lists produce cleaner diffs, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--- a/wwwroot/inc/remote.php&lt;br /&gt;
+++ b/wwwroot/inc/remote.php&lt;br /&gt;
@@ -64,6 +64,7 @@ $breedfunc = array&lt;br /&gt;
 	'jun10-get8021q-main'      =&amp;gt; 'jun10Read8021QConfig',&lt;br /&gt;
 	'jun10-xlatepushq-main'    =&amp;gt; 'jun10TranslatePushQueue',&lt;br /&gt;
 	'jun10-getallconf-main'    =&amp;gt; 'jun10SpotConfigText',&lt;br /&gt;
+	'jun10-getlldpstatus-main' =&amp;gt; 'jun10ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-xlatepushq-main'    =&amp;gt; 'ftos8TranslatePushQueue',&lt;br /&gt;
 	'ftos8-getlldpstatus-main' =&amp;gt; 'ftos8ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-getmaclist-main'    =&amp;gt; 'ftos8ReadMacList',&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, it makes tree-like array initialization more transparent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$SQLSchema = array&lt;br /&gt;
(&lt;br /&gt;
	'object' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'table' =&amp;gt; 'RackObject',&lt;br /&gt;
		'columns' =&amp;gt; array&lt;br /&gt;
		(&lt;br /&gt;
			'id' =&amp;gt; 'id',&lt;br /&gt;
			'name' =&amp;gt; 'name',&lt;br /&gt;
			'label' =&amp;gt; 'label',&lt;br /&gt;
			'asset_no' =&amp;gt; 'asset_no',&lt;br /&gt;
			'objtype_id' =&amp;gt; 'objtype_id',&lt;br /&gt;
			'rack_id' =&amp;gt; '(SELECT MIN(rack_id) FROM RackSpace WHERE object_id = RackObject.id)',&lt;br /&gt;
			'rack_id_2' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack')&amp;quot;,&lt;br /&gt;
			'container_id' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object')&amp;quot;,&lt;br /&gt;
			'container_name' =&amp;gt; '(SELECT name FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'container_objtype_id' =&amp;gt; '(SELECT objtype_id FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'has_problems' =&amp;gt; 'has_problems',&lt;br /&gt;
			'comment' =&amp;gt; 'comment',&lt;br /&gt;
			'nports' =&amp;gt; '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',&lt;br /&gt;
			'8021q_domain_id' =&amp;gt; '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
			'8021q_template_id' =&amp;gt; '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
		),&lt;br /&gt;
		'keycolumn' =&amp;gt; 'id',&lt;br /&gt;
		'ordcolumns' =&amp;gt; array ('RackObject.name'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; near its &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
When an &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; operator includes the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; part, consider arranging the branches so that the short branch belongs to the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; and the long branch belongs to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt;. '''Why:''' it helps following the code.&lt;br /&gt;
&lt;br /&gt;
Holub, Allen (1995). Enough Rope to Shoot Yourself in the Foot. McGraw-Hill. ISBN 978-0-07-029689-3&lt;br /&gt;
&amp;quot;Section 57: Put the shortest clause of an &amp;lt;tt&amp;gt;if/else&amp;lt;/tt&amp;gt; on top.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== use the [http://en.wikipedia.org/wiki/%3F: conditional ternary operator] where it makes sense ====&lt;br /&gt;
'''Why:''' to use the language element in making the code look closer to what the code is intended to do. First, don't reinvent the conditional ternary operator with &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if ($row !== FALSE)&lt;br /&gt;
    return $row[0];&lt;br /&gt;
  else&lt;br /&gt;
    return NULL;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That block of code returns a value in either case, exactly one &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; would do the job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return $row === FALSE ? NULL : $row[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second, don't nest ternary conditionals, it is hard to read:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $ret = ($ret &amp;gt; 0 ? 1 : ($ret &amp;lt; 0 ? -1 : 0));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For that specific case use &amp;lt;tt&amp;gt;numSign()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when a standalone ternary conditional isn't sufficient or is long/difficult to read, use temporary variables or necessary amount of &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;'s.&lt;br /&gt;
&lt;br /&gt;
==== things to consider when adding a new function ====&lt;br /&gt;
* First, is it possible to use one of the existing functions or a combination of these?&lt;br /&gt;
* Carefully decide which file the function should be in (depends on its purpose and relation to other functions).&lt;br /&gt;
* Consider [http://en.wikipedia.org/wiki/CamelCase headlessCamelCase] for the function name.&lt;br /&gt;
* Try to keep functions focused, that is, small, doing just one thing but doing it very well.&lt;br /&gt;
* When adding a new function, add unit test(s) for it as well.&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;$self = __FUNCTION__;&amp;lt;/tt&amp;gt; at the beginning of a recursive function ====&lt;br /&gt;
'''Why:''' to make it easier to see a recursive function. Such a function should call itself as &amp;lt;tt&amp;gt;$self(...)&amp;lt;/tt&amp;gt; so that if the function is renamed later its code requires no changes to work.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Return a list consisting of tag ID of the given tree node and IDs of all&lt;br /&gt;
# nodes it contains.&lt;br /&gt;
function getTagIDListForNode ($treenode)&lt;br /&gt;
{&lt;br /&gt;
	$self = __FUNCTION__;&lt;br /&gt;
	$ret = array ($treenode['id']);&lt;br /&gt;
	foreach ($treenode['kids'] as $item)&lt;br /&gt;
		$ret = array_merge ($ret, $self ($item));&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== prefer &amp;lt;tt&amp;gt;//&amp;lt;/tt&amp;gt; over &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; for single-line comments ====&lt;br /&gt;
'''Why:''' it saves keystokes (double-press one key versus pressing two different keys simultaneously). However, when adding a comment to a code that already uses &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt;, be consistent with what is already there.&lt;br /&gt;
Also, the code contains ~ 2000 of //-style lines versus ~450 of #-style.&lt;br /&gt;
&lt;br /&gt;
==== don't use &amp;lt;tt&amp;gt;foreach()&amp;lt;/tt&amp;gt; by reference ====&lt;br /&gt;
'''Why:''' this valid language construct has a side effect that complicates things. Consider the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$a = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b', 3 =&amp;gt; 'c');&lt;br /&gt;
foreach ($a as &amp;amp;$tmp)&lt;br /&gt;
  $tmp = $tmp . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So far so good, but once you decide to reuse &amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; and assign a new value to it, you will [unexpectedly] modify the array (&amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; still holds the reference to the last element). [https://bugs.php.net/bug.php?id=37410 This thread] discusses the workings of this at length. The workaround would be to use &amp;lt;tt&amp;gt;unset ($tmp)&amp;lt;/tt&amp;gt; after the foreach-by-reference, but the least-surprising solution is to iterate over array keys if you need to modify array elements and &amp;lt;tt&amp;gt;array_walk()&amp;lt;/tt&amp;gt; is not sufficient:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach (array_keys ($a) as $k)&lt;br /&gt;
  $a[$k] = $a[$k] . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use double-quotes for strings only when necessary ====&lt;br /&gt;
'''Why:''' it saves the time required to tell &amp;quot;just strings&amp;quot; from strings that embed expansions. Consider the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$string1 = 'Single-quotes give a hint that this string is just a string.';&lt;br /&gt;
$string2 = &amp;quot;Double-quotes give a hint that this string not just _may_ contain &amp;quot; .&lt;br /&gt;
  &amp;quot;expansions but it actually does: here are $var1 and ${var2}&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use &amp;lt;tt&amp;gt;array_key_exists()&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; to test a key in an array ====&lt;br /&gt;
'''Why:''' with &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; it is impossible to distinguish a missing element from a typo in the array name:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$arr = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b');&lt;br /&gt;
if (array_key_exists (10, $arr)) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (array_key_exists (10, $ar)) # FALSE and a PHP warning (note the typo)&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($arr[10])) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($ar[10])) # FALSE and no warning&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== keep PHP code compatible with all supported PHP versions ====&lt;br /&gt;
'''Why:''' because newer PHP versions tend to deprecate functions and extensions, so you have to account for that.&lt;br /&gt;
&lt;br /&gt;
==== use &amp;quot;&amp;amp;&amp;amp;&amp;quot; and &amp;quot;||&amp;quot;, not &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; ====&lt;br /&gt;
'''Why:''' because (unlike, for instance, C++) those are not just alternative notations but quite different operators. From the [http://php.net/manual/en/language.operators.precedence.php PHP operator precedence table] it is possible to see that, for example, the following two lines of code work in a very different way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$c = $a or $b; // Evaluate $a and assign the value to $c. If $c was set to false, evaluate $b and discard the value.&lt;br /&gt;
$c = $a || $b; // Evaluate $a. If the value is true, set $c to true. Otherwise evaluate $b and assign the value to $c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Not everybody remembers about this difference and eventually interprets the code not as PHP actually executes it. As the gain of &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; in RackTables is negligible, the simplest solution is not to use them at all.&lt;br /&gt;
&lt;br /&gt;
==== test for an empty string in the simplest way possible ====&lt;br /&gt;
Just compare with an empty string. Only use &amp;lt;tt&amp;gt;strlen()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;mb_strlen()&amp;lt;/tt&amp;gt; when those are really required, i.e. when it is necessary to know the exact length of a string or a multibyte string.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($s == '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if ($s != '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if (strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use uppercase for NULL, TRUE and FALSE ====&lt;br /&gt;
'''Why:''' because PHP documentation always spells it this way, so let's keep it readbale (PHP interpreter itself is case-insensitive in this regard).&lt;br /&gt;
&lt;br /&gt;
== SQL code style guide ==&lt;br /&gt;
==== use uppercase for SQL keywords and wrap long lines ====&lt;br /&gt;
'''Why:''' readability&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$result = usePreparedSelectBlade&lt;br /&gt;
	(&lt;br /&gt;
		'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' .&lt;br /&gt;
		'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' .&lt;br /&gt;
		'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name',&lt;br /&gt;
		array ($entity_type, $entity_id)&lt;br /&gt;
	);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use SELECT with an explicit list of columns, don't use &amp;lt;tt&amp;gt;SELECT *&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
'''Why:''' this is very well explained in many sources, for instance, [http://stackoverflow.com/questions/3639861/why-is-select-considered-harmful here] and [http://use-the-index-luke.com/blog/2013-08/its-not-about-the-star-stupid here].&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=939</id>
		<title>SourceCode</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=939"/>
		<updated>2020-09-03T12:08:13Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* make conscise use of the conditional ternary operator */ reword the section title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== the source code repository==&lt;br /&gt;
RackTables project uses git as version control system for the source code ([https://github.com/RackTables/racktables GitHub], [https://code.racktables.org/ read-only mirror]). The repository includes the following notable branches:&lt;br /&gt;
;master&lt;br /&gt;
:The main development branch, that is, the default branch for all new changes.&lt;br /&gt;
;maintenance-0.21.x&lt;br /&gt;
:The maintenance branch for 0.21.x stable releases (see the roadmap).&lt;br /&gt;
;maintenance-0.20.x&lt;br /&gt;
;maintenance-0.19.x&lt;br /&gt;
;maintenance-0.18.x&lt;br /&gt;
;maintenance-0.17.x&lt;br /&gt;
;maintenance-0.16.x&lt;br /&gt;
:Archived maintenance branches, which do not accept any new changes.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to get a working copy of the repository:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# from the RackTables project repository&lt;br /&gt;
git clone git://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:RackTables/racktables.git&lt;br /&gt;
&lt;br /&gt;
# from a GitHub fork (of your own or not)&lt;br /&gt;
git clone git://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:username/racktables.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the first two schemas (&amp;lt;tt&amp;gt;git://&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;https://&amp;lt;/tt&amp;gt;) are always '''read-only''' and the third (&amp;lt;tt&amp;gt;ssh://&amp;lt;/tt&amp;gt;) may be '''read-only or read-write'''.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to examine local unsaved changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The recommended way of contributing the work is to:&lt;br /&gt;
# fork RackTables on GitHub,&lt;br /&gt;
# push finished commits to the fork, and&lt;br /&gt;
# create a pull request.&lt;br /&gt;
&lt;br /&gt;
In the case of a private repository use a [http://git-scm.com/docs/git-format-patch git format-patch] command to produce a patch and send it to the developers. When sending a patch, please make sure your branch is [http://git-scm.com/book/en/Git-Branching-Rebasing rebased] onto the latest respective (master or maintenance) branch.&lt;br /&gt;
&lt;br /&gt;
== PHP code style guide ==&lt;br /&gt;
Almost all PHP source code in RackTables is original and the following style guide applies:&lt;br /&gt;
==== use [http://en.wikipedia.org/wiki/Indent_style#Allman_style Allman] indent style. ====&lt;br /&gt;
'''Why:''' Allman style is very diff-friendly when it comes to control structures. In Allman style expansion of a single inner operator into a larger code block (and vice versa) leaves both the inner (controlled) and the outer (controlling) operator intact:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 for ($i = 0; $i &amp;lt; 10; $i++)&lt;br /&gt;
+{&lt;br /&gt;
 	doSomething ($i);&lt;br /&gt;
+	doSomethingElse();&lt;br /&gt;
+}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This works seamlessly at any indentation level and for all control structures (&amp;lt;tt&amp;gt;if/for/foreach/while&amp;lt;/tt&amp;gt;), however deeply nested in each other. With careful indentation of &amp;lt;tt&amp;gt;case&amp;lt;/tt&amp;gt; within &amp;lt;tt&amp;gt;switch&amp;lt;/tt&amp;gt; it will work same well:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
switch ($var)&lt;br /&gt;
{&lt;br /&gt;
	case 1:&lt;br /&gt;
		doSomething();&lt;br /&gt;
		break;&lt;br /&gt;
	case 2:&lt;br /&gt;
-	{&lt;br /&gt;
-		doSomethingElse();&lt;br /&gt;
		return 3;&lt;br /&gt;
-	}&lt;br /&gt;
	case 3:&lt;br /&gt;
+	{&lt;br /&gt;
+		doSomethingCompletelyDifferent();&lt;br /&gt;
		return 4;&lt;br /&gt;
+	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Being diff-friendly means being friendly to a version control system (Subversion in the past, git at present). Neither the VCS nor the Allman style alone guarantee one's commits to be focused and clean. However, Allman style encourages commits that change only the lines that really need to be changed.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
==== use exactly one tab per indentation level ====&lt;br /&gt;
'''Why:''' different people prefer different (2, 4, 8...) amount of &amp;quot;spaces&amp;quot; per indentation level. Some projects use space indentation and force everyone to hit Space (Backspace) N times each time the developer needs to indent (unindent), regardless of their own preferred value of N. RackTables makes use of Tab and enables every developer to configure their editor for their preferred &amp;quot;tab width&amp;quot; (the screenshot below stands for tab width 2). Note that the tab width must stand for the amount of spaces the editor ''displays'', but not ''generates instead of'', that is, in the source file '''tabs must remain tabs'''.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
[[File:interface_tab2.png]]&lt;br /&gt;
&lt;br /&gt;
==== wrap lines longer than 100-120 characters ====&lt;br /&gt;
'''Why:''' readability. Prior experience tells that old-school 80-columns margin just doesn't work with descriptive naming of variables and functions (i.e. descriptive == heavily wrapped). On the other hand, avoid making a readable, 200-characters long line even if it fits your monitor. Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for function arguments ====&lt;br /&gt;
'''Why:''' the purpose is twofold. On one hand it allows to pack a lot of arguments and expressions into a single function call without losing track of what is being done and no temporary variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		usePreparedUpdateBlade&lt;br /&gt;
		(&lt;br /&gt;
			'Port',&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'name' =&amp;gt; $port_name,&lt;br /&gt;
				'type' =&amp;gt; $port_type_id,&lt;br /&gt;
				'label' =&amp;gt; $port_label,&lt;br /&gt;
				'reservation_comment' =&amp;gt; $reservation_comment,&lt;br /&gt;
				'l2address' =&amp;gt; nullEmptyStr ($db_l2address),&lt;br /&gt;
			),&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'id' =&amp;gt; $port_id,&lt;br /&gt;
				'object_id' =&amp;gt; $object_id&lt;br /&gt;
			)&lt;br /&gt;
		);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, it allows to group function arguments add/or add comments to them:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function get8021QSyncOptions&lt;br /&gt;
(&lt;br /&gt;
	$vswitch,&lt;br /&gt;
	$D, // desired config&lt;br /&gt;
	$C, // cached config&lt;br /&gt;
	$R  // running-config&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
	$default_port = array&lt;br /&gt;
	(&lt;br /&gt;
		'mode' =&amp;gt; 'access',&lt;br /&gt;
		'allowed' =&amp;gt; array (VLAN_DFL_ID),&lt;br /&gt;
		'native' =&amp;gt; VLAN_DFL_ID,&lt;br /&gt;
	);&lt;br /&gt;
	$ret = array();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for arrays ====&lt;br /&gt;
'''Why:''' due to a few reasons. First, multiline array assignment/initialization can accommodate a notable amount of supplementary expressions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ret[$pn] = array&lt;br /&gt;
(&lt;br /&gt;
	'status' =&amp;gt; 'martian_conflict',&lt;br /&gt;
	'left' =&amp;gt; array_key_exists ($pn, $C) ? $C[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
	'right' =&amp;gt; array_key_exists ($pn, $R) ? $R[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second, updates to arrays that serve as lists produce cleaner diffs, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--- a/wwwroot/inc/remote.php&lt;br /&gt;
+++ b/wwwroot/inc/remote.php&lt;br /&gt;
@@ -64,6 +64,7 @@ $breedfunc = array&lt;br /&gt;
 	'jun10-get8021q-main'      =&amp;gt; 'jun10Read8021QConfig',&lt;br /&gt;
 	'jun10-xlatepushq-main'    =&amp;gt; 'jun10TranslatePushQueue',&lt;br /&gt;
 	'jun10-getallconf-main'    =&amp;gt; 'jun10SpotConfigText',&lt;br /&gt;
+	'jun10-getlldpstatus-main' =&amp;gt; 'jun10ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-xlatepushq-main'    =&amp;gt; 'ftos8TranslatePushQueue',&lt;br /&gt;
 	'ftos8-getlldpstatus-main' =&amp;gt; 'ftos8ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-getmaclist-main'    =&amp;gt; 'ftos8ReadMacList',&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, it makes tree-like array initialization more transparent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$SQLSchema = array&lt;br /&gt;
(&lt;br /&gt;
	'object' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'table' =&amp;gt; 'RackObject',&lt;br /&gt;
		'columns' =&amp;gt; array&lt;br /&gt;
		(&lt;br /&gt;
			'id' =&amp;gt; 'id',&lt;br /&gt;
			'name' =&amp;gt; 'name',&lt;br /&gt;
			'label' =&amp;gt; 'label',&lt;br /&gt;
			'asset_no' =&amp;gt; 'asset_no',&lt;br /&gt;
			'objtype_id' =&amp;gt; 'objtype_id',&lt;br /&gt;
			'rack_id' =&amp;gt; '(SELECT MIN(rack_id) FROM RackSpace WHERE object_id = RackObject.id)',&lt;br /&gt;
			'rack_id_2' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack')&amp;quot;,&lt;br /&gt;
			'container_id' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object')&amp;quot;,&lt;br /&gt;
			'container_name' =&amp;gt; '(SELECT name FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'container_objtype_id' =&amp;gt; '(SELECT objtype_id FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'has_problems' =&amp;gt; 'has_problems',&lt;br /&gt;
			'comment' =&amp;gt; 'comment',&lt;br /&gt;
			'nports' =&amp;gt; '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',&lt;br /&gt;
			'8021q_domain_id' =&amp;gt; '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
			'8021q_template_id' =&amp;gt; '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
		),&lt;br /&gt;
		'keycolumn' =&amp;gt; 'id',&lt;br /&gt;
		'ordcolumns' =&amp;gt; array ('RackObject.name'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; near its &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
When an &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; operator includes the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; part, consider arranging the branches so that the short branch belongs to the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; and the long branch belongs to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt;. '''Why:''' it helps following the code.&lt;br /&gt;
&lt;br /&gt;
Holub, Allen (1995). Enough Rope to Shoot Yourself in the Foot. McGraw-Hill. ISBN 978-0-07-029689-3&lt;br /&gt;
&amp;quot;Section 57: Put the shortest clause of an &amp;lt;tt&amp;gt;if/else&amp;lt;/tt&amp;gt; on top.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== use the [http://en.wikipedia.org/wiki/%3F: conditional ternary operator] where it makes sense ====&lt;br /&gt;
'''Why:''' to use the language element in making the code look closer to what the code is intended to do. First, don't reinvent the conditional ternary operator with &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if ($row !== FALSE)&lt;br /&gt;
    return $row[0];&lt;br /&gt;
  else&lt;br /&gt;
    return NULL;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That block of code returns a value in either case, exactly one &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; would do the job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return $row === FALSE ? NULL : $row[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second, don't nest ternary conditionals, it is hard to read:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $ret = ($ret &amp;gt; 0 ? 1 : ($ret &amp;lt; 0 ? -1 : 0));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For that specific case use &amp;lt;tt&amp;gt;numSign()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when a standalone ternary conditional isn't sufficient or is long/difficult to read, use temporary variables or necessary amount of &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;'s.&lt;br /&gt;
&lt;br /&gt;
==== things to consider when adding a new function ====&lt;br /&gt;
* First, is it possible to use one of the existing functions or a combination of these?&lt;br /&gt;
* Carefully decide which file the function should be in (depends on its purpose and relation to other functions).&lt;br /&gt;
* Consider [http://en.wikipedia.org/wiki/CamelCase headlessCamelCase] for the function name.&lt;br /&gt;
* Try to keep functions focused, that is, small, doing just one thing but doing it very well.&lt;br /&gt;
* When adding a new function, add unit test(s) for it as well.&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;$self = __FUNCTION__;&amp;lt;/tt&amp;gt; at the beginning of a recursive function ====&lt;br /&gt;
'''Why:''' to make it easier to see a recursive function. Such a function should call itself as &amp;lt;tt&amp;gt;$self(...)&amp;lt;/tt&amp;gt; so that if the function is renamed later its code requires no changes to work.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Return a list consisting of tag ID of the given tree node and IDs of all&lt;br /&gt;
# nodes it contains.&lt;br /&gt;
function getTagIDListForNode ($treenode)&lt;br /&gt;
{&lt;br /&gt;
	$self = __FUNCTION__;&lt;br /&gt;
	$ret = array ($treenode['id']);&lt;br /&gt;
	foreach ($treenode['kids'] as $item)&lt;br /&gt;
		$ret = array_merge ($ret, $self ($item));&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== prefer &amp;lt;tt&amp;gt;//&amp;lt;/tt&amp;gt; over &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; for single-line comments ====&lt;br /&gt;
'''Why:''' it saves keystokes (double-press one key versus pressing two different keys simultaneously). However, when adding a comment to a code that already uses &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt;, be consistent with what is already there.&lt;br /&gt;
Also, the code contains ~ 2000 of //-style lines versus ~450 of #-style.&lt;br /&gt;
&lt;br /&gt;
==== don't use &amp;lt;tt&amp;gt;foreach()&amp;lt;/tt&amp;gt; by reference ====&lt;br /&gt;
'''Why:''' this valid language construct has a side effect that complicates things. Consider the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$a = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b', 3 =&amp;gt; 'c');&lt;br /&gt;
foreach ($a as &amp;amp;$tmp)&lt;br /&gt;
  $tmp = $tmp . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So far so good, but once you decide to reuse &amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; and assign a new value to it, you will [unexpectedly] modify the array (&amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; still holds the reference to the last element). [https://bugs.php.net/bug.php?id=37410 This thread] discusses the workings of this at length. The workaround would be to use &amp;lt;tt&amp;gt;unset ($tmp)&amp;lt;/tt&amp;gt; after the foreach-by-reference, but the least-surprising solution is to iterate over array keys if you need to modify array elements and &amp;lt;tt&amp;gt;array_walk()&amp;lt;/tt&amp;gt; is not sufficient:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach (array_keys ($a) as $k)&lt;br /&gt;
  $a[$k] = $a[$k] . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use double-quotes for strings only when necessary ====&lt;br /&gt;
'''Why:''' it saves the time required to tell &amp;quot;just strings&amp;quot; from strings that embed expansions. Consider the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$string1 = 'Single-quotes give a hint that this string is just a string.';&lt;br /&gt;
$string2 = &amp;quot;Double-quotes give a hint that this string not just _may_ contain &amp;quot; .&lt;br /&gt;
  &amp;quot;expansions but it actually does: here are $var1 and ${var2}&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use &amp;lt;tt&amp;gt;array_key_exists()&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; to test a key in an array ====&lt;br /&gt;
'''Why:''' with &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; it is impossible to distinguish a missing element from a typo in the array name:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$arr = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b');&lt;br /&gt;
if (array_key_exists (10, $arr)) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (array_key_exists (10, $ar)) # FALSE and a PHP warning (note the typo)&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($arr[10])) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($ar[10])) # FALSE and no warning&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP 5.5 and 5.6 ====&lt;br /&gt;
'''Why:''' because some long-term supported distributions still support these versions, and that's what the users still use in production.&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP versions 7.x ====&lt;br /&gt;
'''Why:''' because newer PHP versions tend to deprecate functions and extensions, so you have to account for that.&lt;br /&gt;
&lt;br /&gt;
==== use &amp;quot;&amp;amp;&amp;amp;&amp;quot; and &amp;quot;||&amp;quot;, not &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; ====&lt;br /&gt;
'''Why:''' because (unlike, for instance, C++) those are not just alternative notations but quite different operators. From the [http://php.net/manual/en/language.operators.precedence.php PHP operator precedence table] it is possible to see that, for example, the following two lines of code work in a very different way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$c = $a or $b; // Evaluate $a and assign the value to $c. If $c was set to false, evaluate $b and discard the value.&lt;br /&gt;
$c = $a || $b; // Evaluate $a. If the value is true, set $c to true. Otherwise evaluate $b and assign the value to $c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Not everybody remembers about this difference and eventually interprets the code not as PHP actually executes it. As the gain of &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; in RackTables is negligible, the simplest solution is not to use them at all.&lt;br /&gt;
&lt;br /&gt;
==== test for an empty string in the simplest way possible ====&lt;br /&gt;
Just compare with an empty string. Only use &amp;lt;tt&amp;gt;strlen()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;mb_strlen()&amp;lt;/tt&amp;gt; when those are really required, i.e. when it is necessary to know the exact length of a string or a multibyte string.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($s == '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if ($s != '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if (strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use uppercase for NULL, TRUE and FALSE ====&lt;br /&gt;
'''Why:''' because PHP documentation always spells it this way, so let's keep it readbale (PHP interpreter itself is case-insensitive in this regard).&lt;br /&gt;
&lt;br /&gt;
== SQL code style guide ==&lt;br /&gt;
==== use uppercase for SQL keywords and wrap long lines ====&lt;br /&gt;
'''Why:''' readability&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$result = usePreparedSelectBlade&lt;br /&gt;
	(&lt;br /&gt;
		'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' .&lt;br /&gt;
		'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' .&lt;br /&gt;
		'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name',&lt;br /&gt;
		array ($entity_type, $entity_id)&lt;br /&gt;
	);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use SELECT with an explicit list of columns, don't use &amp;lt;tt&amp;gt;SELECT *&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
'''Why:''' this is very well explained in many sources, for instance, [http://stackoverflow.com/questions/3639861/why-is-select-considered-harmful here] and [http://use-the-index-luke.com/blog/2013-08/its-not-about-the-star-stupid here].&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=937</id>
		<title>SourceCode</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=937"/>
		<updated>2020-05-09T18:20:14Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: use HTTPS in a hyperlink&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== the source code repository==&lt;br /&gt;
RackTables project uses git as version control system for the source code ([https://github.com/RackTables/racktables GitHub], [https://code.racktables.org/ read-only mirror]). The repository includes the following notable branches:&lt;br /&gt;
;master&lt;br /&gt;
:The main development branch, that is, the default branch for all new changes.&lt;br /&gt;
;maintenance-0.21.x&lt;br /&gt;
:The maintenance branch for 0.21.x stable releases (see the roadmap).&lt;br /&gt;
;maintenance-0.20.x&lt;br /&gt;
;maintenance-0.19.x&lt;br /&gt;
;maintenance-0.18.x&lt;br /&gt;
;maintenance-0.17.x&lt;br /&gt;
;maintenance-0.16.x&lt;br /&gt;
:Archived maintenance branches, which do not accept any new changes.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to get a working copy of the repository:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# from the RackTables project repository&lt;br /&gt;
git clone git://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:RackTables/racktables.git&lt;br /&gt;
&lt;br /&gt;
# from a GitHub fork (of your own or not)&lt;br /&gt;
git clone git://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:username/racktables.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the first two schemas (&amp;lt;tt&amp;gt;git://&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;https://&amp;lt;/tt&amp;gt;) are always '''read-only''' and the third (&amp;lt;tt&amp;gt;ssh://&amp;lt;/tt&amp;gt;) may be '''read-only or read-write'''.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to examine local unsaved changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The recommended way of contributing the work is to:&lt;br /&gt;
# fork RackTables on GitHub,&lt;br /&gt;
# push finished commits to the fork, and&lt;br /&gt;
# create a pull request.&lt;br /&gt;
&lt;br /&gt;
In the case of a private repository use a [http://git-scm.com/docs/git-format-patch git format-patch] command to produce a patch and send it to the developers. When sending a patch, please make sure your branch is [http://git-scm.com/book/en/Git-Branching-Rebasing rebased] onto the latest respective (master or maintenance) branch.&lt;br /&gt;
&lt;br /&gt;
== PHP code style guide ==&lt;br /&gt;
Almost all PHP source code in RackTables is original and the following style guide applies:&lt;br /&gt;
==== use [http://en.wikipedia.org/wiki/Indent_style#Allman_style Allman] indent style. ====&lt;br /&gt;
'''Why:''' Allman style is very diff-friendly when it comes to control structures. In Allman style expansion of a single inner operator into a larger code block (and vice versa) leaves both the inner (controlled) and the outer (controlling) operator intact:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 for ($i = 0; $i &amp;lt; 10; $i++)&lt;br /&gt;
+{&lt;br /&gt;
 	doSomething ($i);&lt;br /&gt;
+	doSomethingElse();&lt;br /&gt;
+}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This works seamlessly at any indentation level and for all control structures (&amp;lt;tt&amp;gt;if/for/foreach/while&amp;lt;/tt&amp;gt;), however deeply nested in each other. With careful indentation of &amp;lt;tt&amp;gt;case&amp;lt;/tt&amp;gt; within &amp;lt;tt&amp;gt;switch&amp;lt;/tt&amp;gt; it will work same well:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
switch ($var)&lt;br /&gt;
{&lt;br /&gt;
	case 1:&lt;br /&gt;
		doSomething();&lt;br /&gt;
		break;&lt;br /&gt;
	case 2:&lt;br /&gt;
-	{&lt;br /&gt;
-		doSomethingElse();&lt;br /&gt;
		return 3;&lt;br /&gt;
-	}&lt;br /&gt;
	case 3:&lt;br /&gt;
+	{&lt;br /&gt;
+		doSomethingCompletelyDifferent();&lt;br /&gt;
		return 4;&lt;br /&gt;
+	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Being diff-friendly means being friendly to a version control system (Subversion in the past, git at present). Neither the VCS nor the Allman style alone guarantee one's commits to be focused and clean. However, Allman style encourages commits that change only the lines that really need to be changed.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
==== use exactly one tab per indentation level ====&lt;br /&gt;
'''Why:''' different people prefer different (2, 4, 8...) amount of &amp;quot;spaces&amp;quot; per indentation level. Some projects use space indentation and force everyone to hit Space (Backspace) N times each time the developer needs to indent (unindent), regardless of their own preferred value of N. RackTables makes use of Tab and enables every developer to configure their editor for their preferred &amp;quot;tab width&amp;quot; (the screenshot below stands for tab width 2). Note that the tab width must stand for the amount of spaces the editor ''displays'', but not ''generates instead of'', that is, in the source file '''tabs must remain tabs'''.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
[[File:interface_tab2.png]]&lt;br /&gt;
&lt;br /&gt;
==== wrap lines longer than 100-120 characters ====&lt;br /&gt;
'''Why:''' readability. Prior experience tells that old-school 80-columns margin just doesn't work with descriptive naming of variables and functions (i.e. descriptive == heavily wrapped). On the other hand, avoid making a readable, 200-characters long line even if it fits your monitor. Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for function arguments ====&lt;br /&gt;
'''Why:''' the purpose is twofold. On one hand it allows to pack a lot of arguments and expressions into a single function call without losing track of what is being done and no temporary variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		usePreparedUpdateBlade&lt;br /&gt;
		(&lt;br /&gt;
			'Port',&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'name' =&amp;gt; $port_name,&lt;br /&gt;
				'type' =&amp;gt; $port_type_id,&lt;br /&gt;
				'label' =&amp;gt; $port_label,&lt;br /&gt;
				'reservation_comment' =&amp;gt; $reservation_comment,&lt;br /&gt;
				'l2address' =&amp;gt; nullEmptyStr ($db_l2address),&lt;br /&gt;
			),&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'id' =&amp;gt; $port_id,&lt;br /&gt;
				'object_id' =&amp;gt; $object_id&lt;br /&gt;
			)&lt;br /&gt;
		);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, it allows to group function arguments add/or add comments to them:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function get8021QSyncOptions&lt;br /&gt;
(&lt;br /&gt;
	$vswitch,&lt;br /&gt;
	$D, // desired config&lt;br /&gt;
	$C, // cached config&lt;br /&gt;
	$R  // running-config&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
	$default_port = array&lt;br /&gt;
	(&lt;br /&gt;
		'mode' =&amp;gt; 'access',&lt;br /&gt;
		'allowed' =&amp;gt; array (VLAN_DFL_ID),&lt;br /&gt;
		'native' =&amp;gt; VLAN_DFL_ID,&lt;br /&gt;
	);&lt;br /&gt;
	$ret = array();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for arrays ====&lt;br /&gt;
'''Why:''' due to a few reasons. First, multiline array assignment/initialization can accommodate a notable amount of supplementary expressions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ret[$pn] = array&lt;br /&gt;
(&lt;br /&gt;
	'status' =&amp;gt; 'martian_conflict',&lt;br /&gt;
	'left' =&amp;gt; array_key_exists ($pn, $C) ? $C[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
	'right' =&amp;gt; array_key_exists ($pn, $R) ? $R[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second, updates to arrays that serve as lists produce cleaner diffs, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--- a/wwwroot/inc/remote.php&lt;br /&gt;
+++ b/wwwroot/inc/remote.php&lt;br /&gt;
@@ -64,6 +64,7 @@ $breedfunc = array&lt;br /&gt;
 	'jun10-get8021q-main'      =&amp;gt; 'jun10Read8021QConfig',&lt;br /&gt;
 	'jun10-xlatepushq-main'    =&amp;gt; 'jun10TranslatePushQueue',&lt;br /&gt;
 	'jun10-getallconf-main'    =&amp;gt; 'jun10SpotConfigText',&lt;br /&gt;
+	'jun10-getlldpstatus-main' =&amp;gt; 'jun10ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-xlatepushq-main'    =&amp;gt; 'ftos8TranslatePushQueue',&lt;br /&gt;
 	'ftos8-getlldpstatus-main' =&amp;gt; 'ftos8ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-getmaclist-main'    =&amp;gt; 'ftos8ReadMacList',&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, it makes tree-like array initialization more transparent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$SQLSchema = array&lt;br /&gt;
(&lt;br /&gt;
	'object' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'table' =&amp;gt; 'RackObject',&lt;br /&gt;
		'columns' =&amp;gt; array&lt;br /&gt;
		(&lt;br /&gt;
			'id' =&amp;gt; 'id',&lt;br /&gt;
			'name' =&amp;gt; 'name',&lt;br /&gt;
			'label' =&amp;gt; 'label',&lt;br /&gt;
			'asset_no' =&amp;gt; 'asset_no',&lt;br /&gt;
			'objtype_id' =&amp;gt; 'objtype_id',&lt;br /&gt;
			'rack_id' =&amp;gt; '(SELECT MIN(rack_id) FROM RackSpace WHERE object_id = RackObject.id)',&lt;br /&gt;
			'rack_id_2' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack')&amp;quot;,&lt;br /&gt;
			'container_id' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object')&amp;quot;,&lt;br /&gt;
			'container_name' =&amp;gt; '(SELECT name FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'container_objtype_id' =&amp;gt; '(SELECT objtype_id FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'has_problems' =&amp;gt; 'has_problems',&lt;br /&gt;
			'comment' =&amp;gt; 'comment',&lt;br /&gt;
			'nports' =&amp;gt; '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',&lt;br /&gt;
			'8021q_domain_id' =&amp;gt; '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
			'8021q_template_id' =&amp;gt; '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
		),&lt;br /&gt;
		'keycolumn' =&amp;gt; 'id',&lt;br /&gt;
		'ordcolumns' =&amp;gt; array ('RackObject.name'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; near its &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
When an &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; operator includes the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; part, consider arranging the branches so that the short branch belongs to the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; and the long branch belongs to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt;. '''Why:''' it helps following the code.&lt;br /&gt;
&lt;br /&gt;
Holub, Allen (1995). Enough Rope to Shoot Yourself in the Foot. McGraw-Hill. ISBN 978-0-07-029689-3&lt;br /&gt;
&amp;quot;Section 57: Put the shortest clause of an &amp;lt;tt&amp;gt;if/else&amp;lt;/tt&amp;gt; on top.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== make conscise use of the [http://en.wikipedia.org/wiki/%3F: conditional ternary operator] ====&lt;br /&gt;
'''Why:''' to use the language element in making the code look closer to what the code is intended to do. First, don't reinvent the conditional ternary operator with &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if ($row !== FALSE)&lt;br /&gt;
    return $row[0];&lt;br /&gt;
  else&lt;br /&gt;
    return NULL;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That block of code returns a value in either case, exactly one &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; would do the job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return $row === FALSE ? NULL : $row[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second, don't nest ternary conditionals, it is hard to read:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $ret = ($ret &amp;gt; 0 ? 1 : ($ret &amp;lt; 0 ? -1 : 0));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For that specific case use &amp;lt;tt&amp;gt;numSign()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when a standalone ternary conditional isn't sufficient or is long/difficult to read, use temporary variables or necessary amount of &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;'s.&lt;br /&gt;
&lt;br /&gt;
==== things to consider when adding a new function ====&lt;br /&gt;
* First, is it possible to use one of the existing functions or a combination of these?&lt;br /&gt;
* Carefully decide which file the function should be in (depends on its purpose and relation to other functions).&lt;br /&gt;
* Consider [http://en.wikipedia.org/wiki/CamelCase headlessCamelCase] for the function name.&lt;br /&gt;
* Try to keep functions focused, that is, small, doing just one thing but doing it very well.&lt;br /&gt;
* When adding a new function, add unit test(s) for it as well.&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;$self = __FUNCTION__;&amp;lt;/tt&amp;gt; at the beginning of a recursive function ====&lt;br /&gt;
'''Why:''' to make it easier to see a recursive function. Such a function should call itself as &amp;lt;tt&amp;gt;$self(...)&amp;lt;/tt&amp;gt; so that if the function is renamed later its code requires no changes to work.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Return a list consisting of tag ID of the given tree node and IDs of all&lt;br /&gt;
# nodes it contains.&lt;br /&gt;
function getTagIDListForNode ($treenode)&lt;br /&gt;
{&lt;br /&gt;
	$self = __FUNCTION__;&lt;br /&gt;
	$ret = array ($treenode['id']);&lt;br /&gt;
	foreach ($treenode['kids'] as $item)&lt;br /&gt;
		$ret = array_merge ($ret, $self ($item));&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== prefer &amp;lt;tt&amp;gt;//&amp;lt;/tt&amp;gt; over &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; for single-line comments ====&lt;br /&gt;
'''Why:''' it saves keystokes (double-press one key versus pressing two different keys simultaneously). However, when adding a comment to a code that already uses &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt;, be consistent with what is already there.&lt;br /&gt;
Also, the code contains ~ 2000 of //-style lines versus ~450 of #-style.&lt;br /&gt;
&lt;br /&gt;
==== don't use &amp;lt;tt&amp;gt;foreach()&amp;lt;/tt&amp;gt; by reference ====&lt;br /&gt;
'''Why:''' this valid language construct has a side effect that complicates things. Consider the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$a = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b', 3 =&amp;gt; 'c');&lt;br /&gt;
foreach ($a as &amp;amp;$tmp)&lt;br /&gt;
  $tmp = $tmp . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So far so good, but once you decide to reuse &amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; and assign a new value to it, you will [unexpectedly] modify the array (&amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; still holds the reference to the last element). [https://bugs.php.net/bug.php?id=37410 This thread] discusses the workings of this at length. The workaround would be to use &amp;lt;tt&amp;gt;unset ($tmp)&amp;lt;/tt&amp;gt; after the foreach-by-reference, but the least-surprising solution is to iterate over array keys if you need to modify array elements and &amp;lt;tt&amp;gt;array_walk()&amp;lt;/tt&amp;gt; is not sufficient:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach (array_keys ($a) as $k)&lt;br /&gt;
  $a[$k] = $a[$k] . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use double-quotes for strings only when necessary ====&lt;br /&gt;
'''Why:''' it saves the time required to tell &amp;quot;just strings&amp;quot; from strings that embed expansions. Consider the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$string1 = 'Single-quotes give a hint that this string is just a string.';&lt;br /&gt;
$string2 = &amp;quot;Double-quotes give a hint that this string not just _may_ contain &amp;quot; .&lt;br /&gt;
  &amp;quot;expansions but it actually does: here are $var1 and ${var2}&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use &amp;lt;tt&amp;gt;array_key_exists()&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; to test a key in an array ====&lt;br /&gt;
'''Why:''' with &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; it is impossible to distinguish a missing element from a typo in the array name:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$arr = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b');&lt;br /&gt;
if (array_key_exists (10, $arr)) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (array_key_exists (10, $ar)) # FALSE and a PHP warning (note the typo)&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($arr[10])) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($ar[10])) # FALSE and no warning&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP 5.5 and 5.6 ====&lt;br /&gt;
'''Why:''' because some long-term supported distributions still support these versions, and that's what the users still use in production.&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP versions 7.x ====&lt;br /&gt;
'''Why:''' because newer PHP versions tend to deprecate functions and extensions, so you have to account for that.&lt;br /&gt;
&lt;br /&gt;
==== use &amp;quot;&amp;amp;&amp;amp;&amp;quot; and &amp;quot;||&amp;quot;, not &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; ====&lt;br /&gt;
'''Why:''' because (unlike, for instance, C++) those are not just alternative notations but quite different operators. From the [http://php.net/manual/en/language.operators.precedence.php PHP operator precedence table] it is possible to see that, for example, the following two lines of code work in a very different way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$c = $a or $b; // Evaluate $a and assign the value to $c. If $c was set to false, evaluate $b and discard the value.&lt;br /&gt;
$c = $a || $b; // Evaluate $a. If the value is true, set $c to true. Otherwise evaluate $b and assign the value to $c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Not everybody remembers about this difference and eventually interprets the code not as PHP actually executes it. As the gain of &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; in RackTables is negligible, the simplest solution is not to use them at all.&lt;br /&gt;
&lt;br /&gt;
==== test for an empty string in the simplest way possible ====&lt;br /&gt;
Just compare with an empty string. Only use &amp;lt;tt&amp;gt;strlen()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;mb_strlen()&amp;lt;/tt&amp;gt; when those are really required, i.e. when it is necessary to know the exact length of a string or a multibyte string.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($s == '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if ($s != '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if (strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use uppercase for NULL, TRUE and FALSE ====&lt;br /&gt;
'''Why:''' because PHP documentation always spells it this way, so let's keep it readbale (PHP interpreter itself is case-insensitive in this regard).&lt;br /&gt;
&lt;br /&gt;
== SQL code style guide ==&lt;br /&gt;
==== use uppercase for SQL keywords and wrap long lines ====&lt;br /&gt;
'''Why:''' readability&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$result = usePreparedSelectBlade&lt;br /&gt;
	(&lt;br /&gt;
		'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' .&lt;br /&gt;
		'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' .&lt;br /&gt;
		'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name',&lt;br /&gt;
		array ($entity_type, $entity_id)&lt;br /&gt;
	);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use SELECT with an explicit list of columns, don't use &amp;lt;tt&amp;gt;SELECT *&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
'''Why:''' this is very well explained in many sources, for instance, [http://stackoverflow.com/questions/3639861/why-is-select-considered-harmful here] and [http://use-the-index-luke.com/blog/2013-08/its-not-about-the-star-stupid here].&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=RackTablesDevelGuide&amp;diff=931</id>
		<title>RackTablesDevelGuide</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=RackTablesDevelGuide&amp;diff=931"/>
		<updated>2020-04-17T18:50:33Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: use the current roadmap&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2019Q4.png|311px|thumb|right]]&lt;br /&gt;
= Config options =&lt;br /&gt;
Variables are stored in the Config SQL table:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql&amp;gt; DESCRIBE Config;&lt;br /&gt;
+----------------+-----------------------+------+-----+---------+-------+&lt;br /&gt;
| Field          | Type                  | Null | Key | Default | Extra |&lt;br /&gt;
+----------------+-----------------------+------+-----+---------+-------+&lt;br /&gt;
| varname        | char(32)              | NO   | PRI | NULL    |       |&lt;br /&gt;
| varvalue       | char(255)             | NO   |     | NULL    |       |&lt;br /&gt;
| vartype        | enum('string','uint') | NO   |     | string  |       |&lt;br /&gt;
| emptyok        | enum('yes','no')      | NO   |     | no      |       |&lt;br /&gt;
| is_hidden      | enum('yes','no')      | NO   |     | yes     |       |&lt;br /&gt;
| is_userdefined | enum('yes','no')      | NO   |     | no      |       |&lt;br /&gt;
| description    | text                  | YES  |     | NULL    |       |&lt;br /&gt;
+----------------+-----------------------+------+-----+---------+-------+&lt;br /&gt;
7 rows in set (0.00 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
# upgrade.php&lt;br /&gt;
# install/init-dictbase.sql&lt;br /&gt;
# inc/ophandlers.php:resetUIConfig()&lt;br /&gt;
Default values must match regardless of the way they were set: initial setup, upgrade or UI reset.&lt;br /&gt;
&lt;br /&gt;
= Exceptions and error handling =&lt;br /&gt;
Error handling now can be done by exceptions mechanism. index.php and process.php have been wrapped in &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ob_start(); &lt;br /&gt;
try{&lt;br /&gt;
  ...&lt;br /&gt;
  ob_end_flush();&lt;br /&gt;
} catch {&lt;br /&gt;
  print_error_nicely();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
kind of code, so every exception you miss and not catch will be printed instead of the page that had to be printed.&lt;br /&gt;
&lt;br /&gt;
This saves a lot of code, take a look at a function&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// before&lt;br /&gt;
&lt;br /&gt;
function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '')&lt;br /&gt;
{&lt;br /&gt;
        if ($chapter_no &amp;lt;= 0)&lt;br /&gt;
        {&lt;br /&gt;
                showError ('Invalid args', __FUNCTION__);&lt;br /&gt;
                die;&lt;br /&gt;
        }&lt;br /&gt;
        global $dbxlink;&lt;br /&gt;
        $query =&lt;br /&gt;
                &amp;quot;update Dictionary set dict_value = '${dict_value}' where chapter_id=${chapter_no} &amp;quot; .&lt;br /&gt;
                &amp;quot;and dict_key=${dict_key} limit 1&amp;quot;;&lt;br /&gt;
        $result = $dbxlink-&amp;gt;query ($query);&lt;br /&gt;
        if ($result == NULL)&lt;br /&gt;
        {&lt;br /&gt;
                showError ('SQL query failed', __FUNCTION__);&lt;br /&gt;
                die;&lt;br /&gt;
        }&lt;br /&gt;
        return TRUE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// and after&lt;br /&gt;
&lt;br /&gt;
function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '')&lt;br /&gt;
{&lt;br /&gt;
        if ($chapter_no &amp;lt;= 0) &lt;br /&gt;
                   throw InvalidArgException('$chapter_no', $chapter_no);&lt;br /&gt;
// the function below does not exist&lt;br /&gt;
        Database::updateWhere(array('dict_value'=&amp;gt;$dict_value), 'Dictionary', array('chapter_id'=&amp;gt;$chapter_no, 'dict_key'=&amp;gt;$dict_key));&lt;br /&gt;
        return TRUE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 a stacktrace and such.&lt;br /&gt;
&lt;br /&gt;
Exceptions registered so far:&lt;br /&gt;
&lt;br /&gt;
;RackTablesError&lt;br /&gt;
:Fatal, final error, usually annotated.&lt;br /&gt;
&lt;br /&gt;
;RTDatabaseError&lt;br /&gt;
:&amp;quot;Soft&amp;quot; database error (UNIQUE/FOREIGN KEY constraint violation or timeout).&lt;br /&gt;
&lt;br /&gt;
;RTGatewayError&lt;br /&gt;
:An external gateway raised an error or the data it returned was corrupt or otherwise invalid.&lt;br /&gt;
&lt;br /&gt;
;EntityNotFoundException&lt;br /&gt;
:Requested record could not be found in the database.&lt;br /&gt;
&lt;br /&gt;
;InvalidArgException&lt;br /&gt;
:At least one of the arguments provided to a function had invalid value, and that value did NOT come from user's input. This means a programming error and cannot be worked around.&lt;br /&gt;
&lt;br /&gt;
;InvalidRequestArgException&lt;br /&gt;
:Ditto, but the value was supplied by the user, and the error should be handled with an &amp;quot;inline&amp;quot; error message.&lt;br /&gt;
&lt;br /&gt;
;RTBuildLVSConfigError&lt;br /&gt;
:LVS keepalived text compilation failed.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= RackCode =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# identifier&lt;br /&gt;
ID ::= ^[[:alnum:]]([\. _~-]?[[:alnum:]])*$&lt;br /&gt;
# identifier in brackets&lt;br /&gt;
PREDICATE ::= [ID]&lt;br /&gt;
DEFINE ::= define PREDICATE&lt;br /&gt;
# identifier in curly braces&lt;br /&gt;
TAG ::= {ID}&lt;br /&gt;
# identifier in curly braces, starting with dollar sign&lt;br /&gt;
AUTOTAG ::= {$ID}&lt;br /&gt;
# context modifier&lt;br /&gt;
CTXMOD ::= clear | insert TAG | remove TAG&lt;br /&gt;
# context modifier list&lt;br /&gt;
CTXMODLIST ::= CTXMODLIST CTXMOD | CTXMOD&lt;br /&gt;
UNARY_EXPRESSION ::= true | false | TAG | AUTOTAG | PREDICATE | (EXPRESSION) | not UNARY_EXPRESSION&lt;br /&gt;
# logical AND&lt;br /&gt;
AND_EXPRESSION ::= AND_EXPRESSION and UNARY_EXPRESSION | UNARY_EXPRESSION&lt;br /&gt;
# logical OR&lt;br /&gt;
EXPRESSION ::= EXPRESSION or AND_EXPRESSION | AND_EXPRESSION&lt;br /&gt;
GRANT ::= allow EXPRESSION | deny EXPRESSION&lt;br /&gt;
DEFINITION ::= DEFINE EXPRESSION&lt;br /&gt;
ADJUSTMENT ::= context CTXMODLIST on EXPRESSION&lt;br /&gt;
# RackCode permission text&lt;br /&gt;
CODETEXT ::= CODETEXT GRANT | CODETEXT DEFINITION | CODETEXT ADJUSTMENT | GRANT | DEFINITION | ADJUSTMENT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Comments in RackCode last from the first &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; character to the end of current line and are filtered out automatically:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {tech support} # or {admins} &amp;lt;--- note where comment starts&lt;br /&gt;
and {assets} # &amp;lt;--- this is still a part of &amp;quot;allow&amp;quot; statement&lt;br /&gt;
&lt;br /&gt;
deny {guests}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= API =&lt;br /&gt;
== Hello, World! ==&lt;br /&gt;
To enable all RackTables functions and load all necessary system data, it is enough to include one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
include ('inc/init.php');&lt;br /&gt;
// do something&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$script_mode = TRUE;&lt;br /&gt;
include ('inc/init.php');&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;I am a crontab script!\n&amp;quot;;&lt;br /&gt;
// do something&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Searching for the appropriate library function ==&lt;br /&gt;
The easy way to determine which function does what you need to is analyze how the corresponding web interface request is processed.&lt;br /&gt;
&lt;br /&gt;
Each modification through web causes HTTP POST request being sent with at least these base parameters: 'page', 'tab', 'op'.&lt;br /&gt;
There is the $ophandler array in wwwroot/inc/navigation.php filled with function names for each triplet of base web operation parameters.&lt;br /&gt;
&lt;br /&gt;
Then, when you determine the handler function name for you operation (like object creating), you could examine its code in wwwroot/inc/ophandlers.php. It usually calls appropriate functions from database.php.&lt;br /&gt;
&lt;br /&gt;
Also, there are some trivial operations which have no special ophandler function. Then the universal database wrapper called tableHandler takes place. It makes direct SQL queries, building them by $opspec_list array data.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Realms ==&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
;object&lt;br /&gt;
:All servers, switches, UPSes, wireless, cable management and any other stuff, which is viewed and managed on the main &amp;quot;Objects&amp;quot; page.&lt;br /&gt;
;ipv4net&lt;br /&gt;
:all IPv4 networks&lt;br /&gt;
;ipv6net&lt;br /&gt;
:all IPv6 networks&lt;br /&gt;
;user&lt;br /&gt;
:All local accounts. There is at least one local account in any RackTables system (admin). There may be more.&lt;br /&gt;
;rack&lt;br /&gt;
:All racks.&lt;br /&gt;
;file&lt;br /&gt;
:All files.&lt;br /&gt;
;ipv4vs&lt;br /&gt;
:All IPv4 virtual services.&lt;br /&gt;
;ipv4rspool&lt;br /&gt;
:All IPv4 real server pools.&lt;br /&gt;
&lt;br /&gt;
== function scanRealmByText ($realm, $ftext = &amp;quot;&amp;quot;) ==&lt;br /&gt;
;realm&lt;br /&gt;
:String equal to one of the realms shown above.&lt;br /&gt;
;ftext&lt;br /&gt;
: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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
include ('inc/init.php');&lt;br /&gt;
&lt;br /&gt;
$allusers = scanRealmByText ('user');&lt;br /&gt;
$smallnetworks = scanRealmByText ('ipv4net', '{small network}');&lt;br /&gt;
$unmounted_objects = scanRealmByText ('object', '{$unmounted}');&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== function spotEntity ($realm, $id) ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
;realm&lt;br /&gt;
:Realm name.&lt;br /&gt;
;id&lt;br /&gt;
:Record number (key value, ID...).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
include ('inc/init.php');&lt;br /&gt;
&lt;br /&gt;
$adminuser = spotEntity ('user', 1); // Admin account is always number 1.&lt;br /&gt;
$myspecialfile = spotEntity ('file', 12345);&lt;br /&gt;
$serverinfo = spotEntity ('object', 67890);&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== function amplifyCell (&amp;amp;$record, $dummy = NULL) ==&lt;br /&gt;
It is assumed, that spotEntity() loads only basic data about the record requested. &amp;quot;Basic&amp;quot; means enough for renderCell() to do its job or to render a row in a table. To get detailed information about the &amp;quot;cell&amp;quot; it is necessary to execute amplifyCell() on it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include ('inc/init.php');&lt;br /&gt;
&lt;br /&gt;
$adminuser = spotEntity ('user', 1); // Admin account is always number 1.&lt;br /&gt;
amplifyCell ($adminuser);&lt;br /&gt;
&lt;br /&gt;
$unmounted_objects = scanRealmByText ('object', '{$unmounted}');&lt;br /&gt;
array_walk ($unmounted_objects, 'amplifyCell');&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== function renderCell ($cell) ==&lt;br /&gt;
Render cell structure as a rectangular block with icon, name, tags and other information. Available since 0.17.2.&lt;br /&gt;
&lt;br /&gt;
= Dictionary =&lt;br /&gt;
== Where the data is ==&lt;br /&gt;
Since release 0.17.2 all records are stored in file &amp;lt;tt&amp;gt;inc/dictionary.php&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$dictionary = array&lt;br /&gt;
(&lt;br /&gt;
	1 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'BlackBox'),&lt;br /&gt;
	2 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'PDU'),&lt;br /&gt;
	3 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'Shelf'),&lt;br /&gt;
	4 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'Server'),&lt;br /&gt;
	5 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'DiskArray'),&lt;br /&gt;
	6 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'TapeLibrary'),&lt;br /&gt;
	7 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'Router'),&lt;br /&gt;
	8 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'Network switch'),&lt;br /&gt;
	9 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'PatchPanel'),&lt;br /&gt;
	10 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'CableOrganizer'),&lt;br /&gt;
	11 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'spacer'),&lt;br /&gt;
	12 =&amp;gt; array ('chapter_id' =&amp;gt; 1, 'dict_value' =&amp;gt; 'UPS'),&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== meaning of chapter ID ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql&amp;gt; SELECT * FROM Chapter;&lt;br /&gt;
+----+--------+-----------------------------+&lt;br /&gt;
| id | sticky | name                        |&lt;br /&gt;
+----+--------+-----------------------------+&lt;br /&gt;
|  1 | yes    | RackObjectType              | &lt;br /&gt;
|  2 | yes    | PortType                    | &lt;br /&gt;
| 11 | no     | server models               | &lt;br /&gt;
| 12 | no     | network switch models       | &lt;br /&gt;
| 13 | no     | server OS type              | &lt;br /&gt;
| 14 | no     | switch OS type              | &lt;br /&gt;
| 16 | no     | router OS type              | &lt;br /&gt;
| 17 | no     | router models               | &lt;br /&gt;
| 18 | no     | disk array models           | &lt;br /&gt;
| 19 | no     | tape library models         | &lt;br /&gt;
| 21 | no     | KVM switch models           | &lt;br /&gt;
| 22 | no     | multiplexer models          | &lt;br /&gt;
| 23 | no     | console models              | &lt;br /&gt;
| 24 | no     | network security models     | &lt;br /&gt;
| 25 | no     | wireless models             | &lt;br /&gt;
| 26 | no     | fibre channel switch models | &lt;br /&gt;
| 27 | no     | PDU models                  | &lt;br /&gt;
+----+--------+-----------------------------+&lt;br /&gt;
17 rows in set (0.00 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== wikilinks ==&lt;br /&gt;
It is possible to render a record as an URL:&lt;br /&gt;
&amp;lt;pre&amp;gt;[[ something | http://somewhere/ ]]&amp;lt;/pre&amp;gt;&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== G-markers ==&lt;br /&gt;
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:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    719 =&amp;gt; array ('chapter_id' =&amp;gt; 24, 'dict_value' =&amp;gt; '[[Cisco%GPASS%ASR 1006 | http://cisco.com/en/US/products/ps9438/index.html]]'),&lt;br /&gt;
    720 =&amp;gt; array ('chapter_id' =&amp;gt; 13, 'dict_value' =&amp;gt; '[[BSD%GSKIP%OpenBSD 3.3 | http://openbsd.org/33.html]]'),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:dictionary-G-markers.png]]&lt;br /&gt;
&lt;br /&gt;
== How to pick dictionary changes up between the releases ==&lt;br /&gt;
This is sometimes needed to keep git working copies up to date. There is no GUI for this to avoid confusion to the users of normal releases.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$script_mode = TRUE;&lt;br /&gt;
require_once '/path/to/racktables/wwwroot/inc/init.php';&lt;br /&gt;
require_once '/path/to/racktables/wwwroot/inc/dictionary.php';&lt;br /&gt;
require_once '/path/to/racktables/wwwroot/inc/upgrade.php';&lt;br /&gt;
&lt;br /&gt;
executeUpgradeBatch ('dictionary');&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Customizing =&lt;br /&gt;
== Overriding the search procedure ==&lt;br /&gt;
&lt;br /&gt;
RackTables contains API allowing to customize search. Two ways of customizing &lt;br /&gt;
are available:&lt;br /&gt;
&lt;br /&gt;
a) You could change text request (search terms) before passing it to standard  search procedure;&lt;br /&gt;
&lt;br /&gt;
b) You could either modify the search results list returned by the standard search procedure, or fill that list completely by yourself.&lt;br /&gt;
&lt;br /&gt;
Of course, you can combine the methods above.&lt;br /&gt;
&lt;br /&gt;
First, you need Racktables to include your code. Racktables tries to include &lt;br /&gt;
the user's php lib called &amp;quot;wwwroot/inc/local.php&amp;quot;.  To be honest, the actual&lt;br /&gt;
include path is &amp;quot;$path_to_local_php&amp;quot;, which by default equals to &lt;br /&gt;
&amp;quot;$racktables_confdir . '/local.php'&amp;quot;. $racktables_confdir, consequently, by default&lt;br /&gt;
is the dir where the library files are stored (wwwroot/inc). You could override&lt;br /&gt;
either of these paths by making separate entry point (custom index.php), or in &lt;br /&gt;
secret.php. If you want to actively use plugin model, you may want to write a&lt;br /&gt;
generic local.php scanning your custom plugins directory and including files from&lt;br /&gt;
it. This technique will allow you install plugins by simply putting them into this dir,&lt;br /&gt;
and to easily exchange plugins with the community.&lt;br /&gt;
&lt;br /&gt;
So, you've decided to store your local.php in the default location and override&lt;br /&gt;
the search procedure. Lets take an example. We want to take a URL in search box,&lt;br /&gt;
extract hostname from it, resolve it into an IP address and than display the search&lt;br /&gt;
results for this address. Proper local.php is below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
//step 1&lt;br /&gt;
$page['search']['handler'] = 'searchHandler_Local';&lt;br /&gt;
&lt;br /&gt;
function searchHandler_Local ()&lt;br /&gt;
{&lt;br /&gt;
	// step 2&lt;br /&gt;
	$terms = trim ($_REQUEST['q']);&lt;br /&gt;
&lt;br /&gt;
	// step 3&lt;br /&gt;
	if (preg_match(&amp;quot;/^(http:\/\/)/&amp;quot;, $terms))&lt;br /&gt;
	{ // Search by IP if URL was given&lt;br /&gt;
		preg_match(&amp;quot;/^(http:\/\/)?([^\/]+)/i&amp;quot;, $terms, $matches);&lt;br /&gt;
		$terms = gethostbyname($matches[2]);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// step 4	&lt;br /&gt;
	$results = searchEntitiesByText ($terms);&lt;br /&gt;
&lt;br /&gt;
	// step 5&lt;br /&gt;
	// modify the $results you need to&lt;br /&gt;
&lt;br /&gt;
	// step 6&lt;br /&gt;
	renderSearchResults ($terms, $results);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's describe this simple script step-by step.&lt;br /&gt;
&lt;br /&gt;
Step 1. First we overwrite the search handler procedure name, replacing the standard one&lt;br /&gt;
by the custom 'searchHandler_Local'. Please note that you can not chain overrides - &lt;br /&gt;
as soon you set procedure name in $page['search']['handler'] (as well as any other &lt;br /&gt;
handler) the old handler is forgotten, and the new one is responsible for calling it,&lt;br /&gt;
if it decides to.&lt;br /&gt;
&lt;br /&gt;
Step 2. We need to retrieve the search request. It is passed in 'q' HTTP GET parameter to&lt;br /&gt;
the search handler.&lt;br /&gt;
&lt;br /&gt;
Step 3. We rewrite the search request text replacing URL to the corresponding IP address.&lt;br /&gt;
It is an example of using the way (a) of customizing RackTables search (see above)&lt;br /&gt;
&lt;br /&gt;
Step 4. Let the standard search procedure do its job. We call searchEntitiesByText for that.&lt;br /&gt;
&lt;br /&gt;
Step 5. Here you could change the results returned by standard function. This array structure&lt;br /&gt;
is pretty complex, you may want to examine it by calling var_dump. The array is indexed by&lt;br /&gt;
search method (like 'object' or 'ipv6addressbydescr'). The rest structure depends of the first&lt;br /&gt;
key value. If you want to display your very own search results section, you could add the unknown&lt;br /&gt;
key into this array which will be used as a header for this results section. The HTML code for its&lt;br /&gt;
body will be the value of $results by the given key.&lt;br /&gt;
&lt;br /&gt;
Step 6. Display the search results using renderSearchResults standard function.&lt;br /&gt;
&lt;br /&gt;
== Adding a custom report ==&lt;br /&gt;
There is a simple way to write custom reports and embed them into the RackTables user interface.&lt;br /&gt;
&lt;br /&gt;
All you need to do is write a report rendering function, apparently using such RackTables API functions&lt;br /&gt;
like scanRealmByText, spotEntity and renderCell, and register this function as a report rendering handler.&lt;br /&gt;
This example should make it clear:&lt;br /&gt;
&lt;br /&gt;
1. Save the code below into the plugins/test-report.php file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$tabhandler['reports']['test'] = 'renderTestReport'; // register a report rendering function&lt;br /&gt;
$tab['reports']['test'] = 'Test Report'; // title of the report tab&lt;br /&gt;
&lt;br /&gt;
function renderTestReport()&lt;br /&gt;
{&lt;br /&gt;
	// fill the HW type stat array&lt;br /&gt;
	$stat = array();&lt;br /&gt;
	$total = 0;&lt;br /&gt;
	$filter = '{switch} and {Moscow}';&lt;br /&gt;
	foreach (scanRealmByText ('object', $filter) as $switch)&lt;br /&gt;
	{&lt;br /&gt;
		$attributes = getAttrValues ($switch['id']);&lt;br /&gt;
		if (isset ($attributes[2]))&lt;br /&gt;
		{&lt;br /&gt;
			$attr = $attributes[2];&lt;br /&gt;
			if (! isset ($stat[$attr['key']]))&lt;br /&gt;
				$stat[$attr['key']] = array&lt;br /&gt;
				(&lt;br /&gt;
					'value' =&amp;gt; $attr['a_value'],&lt;br /&gt;
					'count' =&amp;gt; 0,&lt;br /&gt;
				);&lt;br /&gt;
			++$stat[$attr['key']]['count'];&lt;br /&gt;
			++$total;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	// display the stat array&lt;br /&gt;
	echo &amp;quot;&amp;lt;h2&amp;gt;Moscow switches HW types report ($total)&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;;&lt;br /&gt;
	foreach ($stat as $type_id =&amp;gt; $type)&lt;br /&gt;
	{&lt;br /&gt;
		$type_filter = $filter . ' and {$attr_2_' . $type_id . '}';&lt;br /&gt;
		$link = '&amp;lt;a href=&amp;quot;' . makeHref (array ('page' =&amp;gt; 'depot', 'cfe' =&amp;gt; $type_filter)) . '&amp;quot;&amp;gt;' . $type['count'] . ' devices&amp;lt;/a&amp;gt;';&lt;br /&gt;
		echo &amp;quot;&amp;lt;li&amp;gt;${type['value']} - $link&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	echo '&amp;lt;/ul&amp;gt;';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. To install your report into RackTables, place the file in the plugins directory.&lt;br /&gt;
&lt;br /&gt;
This report displays switch devices located in Moscow, grouped by their hardware model types. Each line of output contains the device count of corresponding model and a link to view the device list, like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Moscow switches HW types report (2)&lt;br /&gt;
 *   Cisco Catalyst 2960G-24PC - &amp;lt;a&amp;gt;1 devices&amp;lt;/a&amp;gt;&lt;br /&gt;
 *   Cisco Catalyst 2960G-24TC - &amp;lt;a&amp;gt;1 devices&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= 802.1Q internals =&lt;br /&gt;
== execution of &amp;quot;pull-only&amp;quot; and &amp;quot;pull+push&amp;quot; sync requests ==&lt;br /&gt;
[[Image:RackTables-8021Q-sync.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= SNMP sync =&lt;br /&gt;
== Overview ==&lt;br /&gt;
The feature evolved as a procedure intended to be run once for any given device. The main procedure is built around the ifTable SNMP tree, which belongs to IF-MIB (very old and ubiquitous one). With regard to network switches, everything is done in the doSwitchSNMPmining() function, located in inc/snmp.php.&lt;br /&gt;
&lt;br /&gt;
The device is polled using the FQDN (hostname or IP/IPv6) attribute, if set.  If not, assigned IP addresses will be polled.&lt;br /&gt;
&lt;br /&gt;
First, subsets of ifTable are taken and combined in a way to produce a PHP array of all interfaces (with MAC address info for each interface).&lt;br /&gt;
&lt;br /&gt;
Then, for each item in the interface list, a &amp;quot;processor&amp;quot; is tried from the list of processors (which is known for each supported product number, that is, SNMP OID). This way, there is a list of &amp;quot;processor&amp;quot; items and a list of known switches, where each item uses one or more processors to process the list of interfaces. There is also a vendor-specific procedure for things like serial number or console port type.&lt;br /&gt;
&lt;br /&gt;
Each processor is built as follows:&lt;br /&gt;
* 'pattern' stands for PCRE pattern, which is tried against interface name (ifDescr in SNMP).&lt;br /&gt;
* 'replacement' is a PCRE replacement for it (most often interface names need to be compressed into something of reasonable length, e.g. 'GigabitEthernet1/2/3' -&amp;gt; 'gi1/2/3').&lt;br /&gt;
* 'label' stands for port's visible label ('replacement' was for port's interface name, i.e. the one which switch's operating system uses) in SQL it is Port.label.&lt;br /&gt;
* 'dict_key' stands for a special value which can be defined in one of two forms: either &amp;quot;N2&amp;quot; or &amp;quot;N1-N2&amp;quot; (in the former case N1 is taken to be equal to 1).&lt;br /&gt;
&lt;br /&gt;
N1 stands for IIF ID, its valid values are stored in the PortInnerInterface table.&lt;br /&gt;
N2 stands for OIF ID, its values are stored in Dictionary chapter 2.&lt;br /&gt;
&lt;br /&gt;
For example, the most popular interface, &amp;quot;hardwired 1000Base-T&amp;quot; may be written as either 24 or '1-24'.&lt;br /&gt;
For an &amp;quot;empty XENPAK&amp;quot; port, use '5-1079'.&lt;br /&gt;
IIF stands for &amp;quot;Inner InterFace&amp;quot; and OIF stands for &amp;quot;Outer InterFace&amp;quot;&lt;br /&gt;
In the Port table, IIF ID is stored in the &amp;quot;iif_id&amp;quot; column and OIF ID in the &amp;quot;type&amp;quot; column.&lt;br /&gt;
this way the description of &amp;quot;dict_key&amp;quot; in a processor item&lt;br /&gt;
&lt;br /&gt;
The 'known_switches' array is used to map processors and other information to specific switch models.&lt;br /&gt;
* 'dict_key' is the dictionary ID which represents the switch model.  The list of known models is defined in dictionary.php.&lt;br /&gt;
* 'text' is a basic description of the device that is presented to the user after discovery is complete.&lt;br /&gt;
* 'processors' lists which processors are applied to the device.&lt;br /&gt;
* 'ifDescrOID' is an optional attribute used in cases where the SNMP ifDescr data does not include unique values (e.g. all interfaces are named 'Ethernet Interface').  It specifies the name of the table which does indeed contain unique interface names.&lt;br /&gt;
* 'try_next_proc' controls the way different processors are applied.  It only refers to if the PCRE pattern has matched or not. In the case of TRUE, the port is created as prescribed and processing of the current interface goes on with the next processor item.  In the case of FALSE, the port is added and no more processor items are tried for the current interface (procedure goes on to the next interface).&lt;br /&gt;
&lt;br /&gt;
When it comes to adding support for another switch, the best route is to study the device to find out which physical ports it has and how they are represented in SNMP. In particular, 'try_next_proc' is used for combo ports (ones allowing either copper or SFP media under the same OS interface).  The work is much easier when some other device from the same product line already exists in snmp.php. Some product lines only have different OIDs and dict_key values, while everything else is the same (e.g. a switch which has PoE ports and one which does not).&lt;br /&gt;
&lt;br /&gt;
=== Environmental requirements ===&lt;br /&gt;
This function uses symbolic OIDs to access the devices which in turn requires base MIBs to be installed and configured properly. If they're not you'll get ''&amp;quot;Fatal SNMP error&amp;quot;'' on the webpage and ''&amp;quot;PHP Warning: snmp2_get(): Invalid object identifier: sysObjectID.0 in /var/www/rt/inc/snmp.php&amp;quot;'' in the webserver error log.&lt;br /&gt;
&lt;br /&gt;
In case of ''Debian GNU/Linux'' (squeeze and up), for example, this means that apart from &amp;lt;tt&amp;gt;php5-snmp&amp;lt;/tt&amp;gt; there must be &amp;lt;tt&amp;gt;snmp-mibs-downloader&amp;lt;/tt&amp;gt; installed, the MIBs downloaded and &amp;lt;tt&amp;gt;/etc/snmp/snmp.conf&amp;lt;/tt&amp;gt; edited to allow their use. Do not forget to restart php (apache) after the changes.&lt;br /&gt;
&lt;br /&gt;
== Example 1: Arista 7124S ==&lt;br /&gt;
This is a switch which has 24 SFP+ ports, two management ports and one power supply.&lt;br /&gt;
&lt;br /&gt;
SNMP returns the following names for the ifDescr table:&lt;br /&gt;
* Ethernet1&lt;br /&gt;
* Ethernet2&lt;br /&gt;
* ...&lt;br /&gt;
* Ethernet24&lt;br /&gt;
* Management1&lt;br /&gt;
* Management2&lt;br /&gt;
&lt;br /&gt;
Add to dictionary.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1610 =&amp;gt; array ('chapter_id' =&amp;gt; 12, 'dict_value' =&amp;gt; 'Arista%GPASS%7124S'),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add to snmp.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$iftable_processors['arista-any-SFP+'] = array&lt;br /&gt;
(&lt;br /&gt;
	'pattern' =&amp;gt; '@^Ethernet([[:digit:]]+)$@',&lt;br /&gt;
	'replacement' =&amp;gt; '\\1',&lt;br /&gt;
	'dict_key' =&amp;gt; '9-1084',&lt;br /&gt;
	'label' =&amp;gt; '\\1',&lt;br /&gt;
	'try_next_proc' =&amp;gt; FALSE,&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
$iftable_processors['arista-management'] = array&lt;br /&gt;
(&lt;br /&gt;
	'pattern' =&amp;gt; '@^Management(1|2)$@',&lt;br /&gt;
	'replacement' =&amp;gt; 'mgmt\\1',&lt;br /&gt;
	'dict_key' =&amp;gt; '1-24',&lt;br /&gt;
	'label' =&amp;gt; 'Management',&lt;br /&gt;
	'try_next_proc' =&amp;gt; FALSE,&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	'30065.1.3011.7124.3282' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'dict_key' =&amp;gt; 1610,&lt;br /&gt;
		'text' =&amp;gt; 'DCS-7124S: 24 SFP+/10000',&lt;br /&gt;
		'processors' =&amp;gt; array ('arista-any-SFP+', 'arista-management'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=929</id>
		<title>SourceCode</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=929"/>
		<updated>2019-12-07T20:48:22Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* the source code repository */ update the list of git branches&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== the source code repository==&lt;br /&gt;
RackTables project uses git as version control system for the source code ([https://github.com/RackTables/racktables GitHub], [http://code.racktables.org/ read-only mirror]). The repository includes the following notable branches:&lt;br /&gt;
;master&lt;br /&gt;
:The main development branch, that is, the default branch for all new changes.&lt;br /&gt;
;maintenance-0.21.x&lt;br /&gt;
:The maintenance branch for 0.21.x stable releases (see the roadmap).&lt;br /&gt;
;maintenance-0.20.x&lt;br /&gt;
;maintenance-0.19.x&lt;br /&gt;
;maintenance-0.18.x&lt;br /&gt;
;maintenance-0.17.x&lt;br /&gt;
;maintenance-0.16.x&lt;br /&gt;
:Archived maintenance branches, which do not accept any new changes.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to get a working copy of the repository:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# from the RackTables project repository&lt;br /&gt;
git clone git://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:RackTables/racktables.git&lt;br /&gt;
&lt;br /&gt;
# from a GitHub fork (of your own or not)&lt;br /&gt;
git clone git://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:username/racktables.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the first two schemas (&amp;lt;tt&amp;gt;git://&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;https://&amp;lt;/tt&amp;gt;) are always '''read-only''' and the third (&amp;lt;tt&amp;gt;ssh://&amp;lt;/tt&amp;gt;) may be '''read-only or read-write'''.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to examine local unsaved changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The recommended way of contributing the work is to:&lt;br /&gt;
# fork RackTables on GitHub,&lt;br /&gt;
# push finished commits to the fork, and&lt;br /&gt;
# create a pull request.&lt;br /&gt;
&lt;br /&gt;
In the case of a private repository use a [http://git-scm.com/docs/git-format-patch git format-patch] command to produce a patch and send it to the developers. When sending a patch, please make sure your branch is [http://git-scm.com/book/en/Git-Branching-Rebasing rebased] onto the latest respective (master or maintenance) branch.&lt;br /&gt;
&lt;br /&gt;
== PHP code style guide ==&lt;br /&gt;
Almost all PHP source code in RackTables is original and the following style guide applies:&lt;br /&gt;
==== use [http://en.wikipedia.org/wiki/Indent_style#Allman_style Allman] indent style. ====&lt;br /&gt;
'''Why:''' Allman style is very diff-friendly when it comes to control structures. In Allman style expansion of a single inner operator into a larger code block (and vice versa) leaves both the inner (controlled) and the outer (controlling) operator intact:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 for ($i = 0; $i &amp;lt; 10; $i++)&lt;br /&gt;
+{&lt;br /&gt;
 	doSomething ($i);&lt;br /&gt;
+	doSomethingElse();&lt;br /&gt;
+}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This works seamlessly at any indentation level and for all control structures (&amp;lt;tt&amp;gt;if/for/foreach/while&amp;lt;/tt&amp;gt;), however deeply nested in each other. With careful indentation of &amp;lt;tt&amp;gt;case&amp;lt;/tt&amp;gt; within &amp;lt;tt&amp;gt;switch&amp;lt;/tt&amp;gt; it will work same well:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
switch ($var)&lt;br /&gt;
{&lt;br /&gt;
	case 1:&lt;br /&gt;
		doSomething();&lt;br /&gt;
		break;&lt;br /&gt;
	case 2:&lt;br /&gt;
-	{&lt;br /&gt;
-		doSomethingElse();&lt;br /&gt;
		return 3;&lt;br /&gt;
-	}&lt;br /&gt;
	case 3:&lt;br /&gt;
+	{&lt;br /&gt;
+		doSomethingCompletelyDifferent();&lt;br /&gt;
		return 4;&lt;br /&gt;
+	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Being diff-friendly means being friendly to a version control system (Subversion in the past, git at present). Neither the VCS nor the Allman style alone guarantee one's commits to be focused and clean. However, Allman style encourages commits that change only the lines that really need to be changed.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
==== use exactly one tab per indentation level ====&lt;br /&gt;
'''Why:''' different people prefer different (2, 4, 8...) amount of &amp;quot;spaces&amp;quot; per indentation level. Some projects use space indentation and force everyone to hit Space (Backspace) N times each time the developer needs to indent (unindent), regardless of their own preferred value of N. RackTables makes use of Tab and enables every developer to configure their editor for their preferred &amp;quot;tab width&amp;quot; (the screenshot below stands for tab width 2). Note that the tab width must stand for the amount of spaces the editor ''displays'', but not ''generates instead of'', that is, in the source file '''tabs must remain tabs'''.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
[[File:interface_tab2.png]]&lt;br /&gt;
&lt;br /&gt;
==== wrap lines longer than 100-120 characters ====&lt;br /&gt;
'''Why:''' readability. Prior experience tells that old-school 80-columns margin just doesn't work with descriptive naming of variables and functions (i.e. descriptive == heavily wrapped). On the other hand, avoid making a readable, 200-characters long line even if it fits your monitor. Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for function arguments ====&lt;br /&gt;
'''Why:''' the purpose is twofold. On one hand it allows to pack a lot of arguments and expressions into a single function call without losing track of what is being done and no temporary variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		usePreparedUpdateBlade&lt;br /&gt;
		(&lt;br /&gt;
			'Port',&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'name' =&amp;gt; $port_name,&lt;br /&gt;
				'type' =&amp;gt; $port_type_id,&lt;br /&gt;
				'label' =&amp;gt; $port_label,&lt;br /&gt;
				'reservation_comment' =&amp;gt; $reservation_comment,&lt;br /&gt;
				'l2address' =&amp;gt; nullEmptyStr ($db_l2address),&lt;br /&gt;
			),&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'id' =&amp;gt; $port_id,&lt;br /&gt;
				'object_id' =&amp;gt; $object_id&lt;br /&gt;
			)&lt;br /&gt;
		);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, it allows to group function arguments add/or add comments to them:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function get8021QSyncOptions&lt;br /&gt;
(&lt;br /&gt;
	$vswitch,&lt;br /&gt;
	$D, // desired config&lt;br /&gt;
	$C, // cached config&lt;br /&gt;
	$R  // running-config&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
	$default_port = array&lt;br /&gt;
	(&lt;br /&gt;
		'mode' =&amp;gt; 'access',&lt;br /&gt;
		'allowed' =&amp;gt; array (VLAN_DFL_ID),&lt;br /&gt;
		'native' =&amp;gt; VLAN_DFL_ID,&lt;br /&gt;
	);&lt;br /&gt;
	$ret = array();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for arrays ====&lt;br /&gt;
'''Why:''' due to a few reasons. First, multiline array assignment/initialization can accommodate a notable amount of supplementary expressions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ret[$pn] = array&lt;br /&gt;
(&lt;br /&gt;
	'status' =&amp;gt; 'martian_conflict',&lt;br /&gt;
	'left' =&amp;gt; array_key_exists ($pn, $C) ? $C[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
	'right' =&amp;gt; array_key_exists ($pn, $R) ? $R[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second, updates to arrays that serve as lists produce cleaner diffs, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--- a/wwwroot/inc/remote.php&lt;br /&gt;
+++ b/wwwroot/inc/remote.php&lt;br /&gt;
@@ -64,6 +64,7 @@ $breedfunc = array&lt;br /&gt;
 	'jun10-get8021q-main'      =&amp;gt; 'jun10Read8021QConfig',&lt;br /&gt;
 	'jun10-xlatepushq-main'    =&amp;gt; 'jun10TranslatePushQueue',&lt;br /&gt;
 	'jun10-getallconf-main'    =&amp;gt; 'jun10SpotConfigText',&lt;br /&gt;
+	'jun10-getlldpstatus-main' =&amp;gt; 'jun10ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-xlatepushq-main'    =&amp;gt; 'ftos8TranslatePushQueue',&lt;br /&gt;
 	'ftos8-getlldpstatus-main' =&amp;gt; 'ftos8ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-getmaclist-main'    =&amp;gt; 'ftos8ReadMacList',&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, it makes tree-like array initialization more transparent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$SQLSchema = array&lt;br /&gt;
(&lt;br /&gt;
	'object' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'table' =&amp;gt; 'RackObject',&lt;br /&gt;
		'columns' =&amp;gt; array&lt;br /&gt;
		(&lt;br /&gt;
			'id' =&amp;gt; 'id',&lt;br /&gt;
			'name' =&amp;gt; 'name',&lt;br /&gt;
			'label' =&amp;gt; 'label',&lt;br /&gt;
			'asset_no' =&amp;gt; 'asset_no',&lt;br /&gt;
			'objtype_id' =&amp;gt; 'objtype_id',&lt;br /&gt;
			'rack_id' =&amp;gt; '(SELECT MIN(rack_id) FROM RackSpace WHERE object_id = RackObject.id)',&lt;br /&gt;
			'rack_id_2' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack')&amp;quot;,&lt;br /&gt;
			'container_id' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object')&amp;quot;,&lt;br /&gt;
			'container_name' =&amp;gt; '(SELECT name FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'container_objtype_id' =&amp;gt; '(SELECT objtype_id FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'has_problems' =&amp;gt; 'has_problems',&lt;br /&gt;
			'comment' =&amp;gt; 'comment',&lt;br /&gt;
			'nports' =&amp;gt; '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',&lt;br /&gt;
			'8021q_domain_id' =&amp;gt; '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
			'8021q_template_id' =&amp;gt; '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
		),&lt;br /&gt;
		'keycolumn' =&amp;gt; 'id',&lt;br /&gt;
		'ordcolumns' =&amp;gt; array ('RackObject.name'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; near its &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
When an &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; operator includes the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; part, consider arranging the branches so that the short branch belongs to the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; and the long branch belongs to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt;. '''Why:''' it helps following the code.&lt;br /&gt;
&lt;br /&gt;
Holub, Allen (1995). Enough Rope to Shoot Yourself in the Foot. McGraw-Hill. ISBN 978-0-07-029689-3&lt;br /&gt;
&amp;quot;Section 57: Put the shortest clause of an &amp;lt;tt&amp;gt;if/else&amp;lt;/tt&amp;gt; on top.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== make conscise use of the [http://en.wikipedia.org/wiki/%3F: conditional ternary operator] ====&lt;br /&gt;
'''Why:''' to use the language element in making the code look closer to what the code is intended to do. First, don't reinvent the conditional ternary operator with &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if ($row !== FALSE)&lt;br /&gt;
    return $row[0];&lt;br /&gt;
  else&lt;br /&gt;
    return NULL;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That block of code returns a value in either case, exactly one &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; would do the job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return $row === FALSE ? NULL : $row[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second, don't nest ternary conditionals, it is hard to read:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $ret = ($ret &amp;gt; 0 ? 1 : ($ret &amp;lt; 0 ? -1 : 0));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For that specific case use &amp;lt;tt&amp;gt;numSign()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when a standalone ternary conditional isn't sufficient or is long/difficult to read, use temporary variables or necessary amount of &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;'s.&lt;br /&gt;
&lt;br /&gt;
==== things to consider when adding a new function ====&lt;br /&gt;
* First, is it possible to use one of the existing functions or a combination of these?&lt;br /&gt;
* Carefully decide which file the function should be in (depends on its purpose and relation to other functions).&lt;br /&gt;
* Consider [http://en.wikipedia.org/wiki/CamelCase headlessCamelCase] for the function name.&lt;br /&gt;
* Try to keep functions focused, that is, small, doing just one thing but doing it very well.&lt;br /&gt;
* When adding a new function, add unit test(s) for it as well.&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;$self = __FUNCTION__;&amp;lt;/tt&amp;gt; at the beginning of a recursive function ====&lt;br /&gt;
'''Why:''' to make it easier to see a recursive function. Such a function should call itself as &amp;lt;tt&amp;gt;$self(...)&amp;lt;/tt&amp;gt; so that if the function is renamed later its code requires no changes to work.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Return a list consisting of tag ID of the given tree node and IDs of all&lt;br /&gt;
# nodes it contains.&lt;br /&gt;
function getTagIDListForNode ($treenode)&lt;br /&gt;
{&lt;br /&gt;
	$self = __FUNCTION__;&lt;br /&gt;
	$ret = array ($treenode['id']);&lt;br /&gt;
	foreach ($treenode['kids'] as $item)&lt;br /&gt;
		$ret = array_merge ($ret, $self ($item));&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== prefer &amp;lt;tt&amp;gt;//&amp;lt;/tt&amp;gt; over &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; for single-line comments ====&lt;br /&gt;
'''Why:''' it saves keystokes (double-press one key versus pressing two different keys simultaneously). However, when adding a comment to a code that already uses &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt;, be consistent with what is already there.&lt;br /&gt;
Also, the code contains ~ 2000 of //-style lines versus ~450 of #-style.&lt;br /&gt;
&lt;br /&gt;
==== don't use &amp;lt;tt&amp;gt;foreach()&amp;lt;/tt&amp;gt; by reference ====&lt;br /&gt;
'''Why:''' this valid language construct has a side effect that complicates things. Consider the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$a = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b', 3 =&amp;gt; 'c');&lt;br /&gt;
foreach ($a as &amp;amp;$tmp)&lt;br /&gt;
  $tmp = $tmp . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So far so good, but once you decide to reuse &amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; and assign a new value to it, you will [unexpectedly] modify the array (&amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; still holds the reference to the last element). [https://bugs.php.net/bug.php?id=37410 This thread] discusses the workings of this at length. The workaround would be to use &amp;lt;tt&amp;gt;unset ($tmp)&amp;lt;/tt&amp;gt; after the foreach-by-reference, but the least-surprising solution is to iterate over array keys if you need to modify array elements and &amp;lt;tt&amp;gt;array_walk()&amp;lt;/tt&amp;gt; is not sufficient:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach (array_keys ($a) as $k)&lt;br /&gt;
  $a[$k] = $a[$k] . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use double-quotes for strings only when necessary ====&lt;br /&gt;
'''Why:''' it saves the time required to tell &amp;quot;just strings&amp;quot; from strings that embed expansions. Consider the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$string1 = 'Single-quotes give a hint that this string is just a string.';&lt;br /&gt;
$string2 = &amp;quot;Double-quotes give a hint that this string not just _may_ contain &amp;quot; .&lt;br /&gt;
  &amp;quot;expansions but it actually does: here are $var1 and ${var2}&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use &amp;lt;tt&amp;gt;array_key_exists()&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; to test a key in an array ====&lt;br /&gt;
'''Why:''' with &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; it is impossible to distinguish a missing element from a typo in the array name:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$arr = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b');&lt;br /&gt;
if (array_key_exists (10, $arr)) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (array_key_exists (10, $ar)) # FALSE and a PHP warning (note the typo)&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($arr[10])) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($ar[10])) # FALSE and no warning&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP 5.5 and 5.6 ====&lt;br /&gt;
'''Why:''' because some long-term supported distributions still support these versions, and that's what the users still use in production.&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP versions 7.x ====&lt;br /&gt;
'''Why:''' because newer PHP versions tend to deprecate functions and extensions, so you have to account for that.&lt;br /&gt;
&lt;br /&gt;
==== use &amp;quot;&amp;amp;&amp;amp;&amp;quot; and &amp;quot;||&amp;quot;, not &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; ====&lt;br /&gt;
'''Why:''' because (unlike, for instance, C++) those are not just alternative notations but quite different operators. From the [http://php.net/manual/en/language.operators.precedence.php PHP operator precedence table] it is possible to see that, for example, the following two lines of code work in a very different way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$c = $a or $b; // Evaluate $a and assign the value to $c. If $c was set to false, evaluate $b and discard the value.&lt;br /&gt;
$c = $a || $b; // Evaluate $a. If the value is true, set $c to true. Otherwise evaluate $b and assign the value to $c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Not everybody remembers about this difference and eventually interprets the code not as PHP actually executes it. As the gain of &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; in RackTables is negligible, the simplest solution is not to use them at all.&lt;br /&gt;
&lt;br /&gt;
==== test for an empty string in the simplest way possible ====&lt;br /&gt;
Just compare with an empty string. Only use &amp;lt;tt&amp;gt;strlen()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;mb_strlen()&amp;lt;/tt&amp;gt; when those are really required, i.e. when it is necessary to know the exact length of a string or a multibyte string.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($s == '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if ($s != '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if (strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use uppercase for NULL, TRUE and FALSE ====&lt;br /&gt;
'''Why:''' because PHP documentation always spells it this way, so let's keep it readbale (PHP interpreter itself is case-insensitive in this regard).&lt;br /&gt;
&lt;br /&gt;
== SQL code style guide ==&lt;br /&gt;
==== use uppercase for SQL keywords and wrap long lines ====&lt;br /&gt;
'''Why:''' readability&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$result = usePreparedSelectBlade&lt;br /&gt;
	(&lt;br /&gt;
		'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' .&lt;br /&gt;
		'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' .&lt;br /&gt;
		'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name',&lt;br /&gt;
		array ($entity_type, $entity_id)&lt;br /&gt;
	);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use SELECT with an explicit list of columns, don't use &amp;lt;tt&amp;gt;SELECT *&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
'''Why:''' this is very well explained in many sources, for instance, [http://stackoverflow.com/questions/3639861/why-is-select-considered-harmful here] and [http://use-the-index-luke.com/blog/2013-08/its-not-about-the-star-stupid here].&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=927</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=927"/>
		<updated>2019-12-07T20:45:47Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: update a reference to point to maintenance-0.21.x&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2019Q4.png|311px|thumb|right]]&lt;br /&gt;
* for RackTables users&lt;br /&gt;
** The [https://github.com/RackTables/racktables/blob/maintenance-0.21.x/README.md README file] now replaces the old Installation HOWTO.&lt;br /&gt;
** [[RackTablesAdminGuide | Administrator's guide]]&lt;br /&gt;
** [[RackTablesUserGuide | User's guide]]&lt;br /&gt;
** [[8021Q | 802.1Q VLAN management in RackTables]] (still work in progress)&lt;br /&gt;
** [[FAQ]]&lt;br /&gt;
** [[LDAP | LDAP authentication]]&lt;br /&gt;
** [[SAML | SAML authentication]]&lt;br /&gt;
* for RackTables hackers&lt;br /&gt;
** [[SourceCode | Working with RackTables source code]]&lt;br /&gt;
** [[RackTablesDevelGuide | Developer's guide]]&lt;br /&gt;
** [[FeatureWishlist | Feature wishlist]]&lt;br /&gt;
** [[Gateways | Extending RackTables with gateways]]&lt;br /&gt;
** [[Plugins | Extending RackTables with plugins]]&lt;br /&gt;
* for development team&lt;br /&gt;
** [[NewRelease | Checklist and procedures to make a new release]]&lt;br /&gt;
** [[ProjectInfrastructure | Project infrastructure]]&lt;br /&gt;
** [[Roadmap]]&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=925</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=925"/>
		<updated>2019-11-24T15:52:38Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: use the new roadmap&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2019Q4.png|311px|thumb|right]]&lt;br /&gt;
* for RackTables users&lt;br /&gt;
** The [https://github.com/RackTables/racktables/blob/maintenance-0.20.x/README.md README file] now replaces the old Installation HOWTO.&lt;br /&gt;
** [[RackTablesAdminGuide | Administrator's guide]]&lt;br /&gt;
** [[RackTablesUserGuide | User's guide]]&lt;br /&gt;
** [[8021Q | 802.1Q VLAN management in RackTables]] (still work in progress)&lt;br /&gt;
** [[FAQ]]&lt;br /&gt;
** [[LDAP | LDAP authentication]]&lt;br /&gt;
** [[SAML | SAML authentication]]&lt;br /&gt;
* for RackTables hackers&lt;br /&gt;
** [[SourceCode | Working with RackTables source code]]&lt;br /&gt;
** [[RackTablesDevelGuide | Developer's guide]]&lt;br /&gt;
** [[FeatureWishlist | Feature wishlist]]&lt;br /&gt;
** [[Gateways | Extending RackTables with gateways]]&lt;br /&gt;
** [[Plugins | Extending RackTables with plugins]]&lt;br /&gt;
* for development team&lt;br /&gt;
** [[NewRelease | Checklist and procedures to make a new release]]&lt;br /&gt;
** [[ProjectInfrastructure | Project infrastructure]]&lt;br /&gt;
** [[Roadmap]]&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=File:RackTables-development-roadmap-2019Q4.png&amp;diff=923</id>
		<title>File:RackTables-development-roadmap-2019Q4.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=File:RackTables-development-roadmap-2019Q4.png&amp;diff=923"/>
		<updated>2019-11-24T15:50:38Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=921</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=921"/>
		<updated>2019-11-17T20:56:30Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* updating demo */ add a missing parameter&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo yes&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=919</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=919"/>
		<updated>2019-11-17T20:52:06Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* producing and publishing deliverables */ SF downloads no longer have buttons&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=917</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=917"/>
		<updated>2019-11-17T20:28:22Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* producing and publishing deliverables */ remove unnecessary export&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=915</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=915"/>
		<updated>2019-06-12T14:12:09Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* part 1 */ simplify the git command&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add ChangeLog wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=913</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=913"/>
		<updated>2019-06-12T13:51:32Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* part 1 */ fixup some language&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
Make sure all necessary changes have been committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=911</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=911"/>
		<updated>2019-06-12T12:25:49Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: use the new roadmap&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2019Q2.png|409px|thumb|right]]&lt;br /&gt;
* for RackTables users&lt;br /&gt;
** The [https://github.com/RackTables/racktables/blob/maintenance-0.20.x/README.md README file] now replaces the old Installation HOWTO.&lt;br /&gt;
** [[RackTablesAdminGuide | Administrator's guide]]&lt;br /&gt;
** [[RackTablesUserGuide | User's guide]]&lt;br /&gt;
** [[8021Q | 802.1Q VLAN management in RackTables]] (still work in progress)&lt;br /&gt;
** [[FAQ]]&lt;br /&gt;
** [[LDAP | LDAP authentication]]&lt;br /&gt;
** [[SAML | SAML authentication]]&lt;br /&gt;
* for RackTables hackers&lt;br /&gt;
** [[SourceCode | Working with RackTables source code]]&lt;br /&gt;
** [[RackTablesDevelGuide | Developer's guide]]&lt;br /&gt;
** [[FeatureWishlist | Feature wishlist]]&lt;br /&gt;
** [[Gateways | Extending RackTables with gateways]]&lt;br /&gt;
** [[Plugins | Extending RackTables with plugins]]&lt;br /&gt;
* for development team&lt;br /&gt;
** [[NewRelease | Checklist and procedures to make a new release]]&lt;br /&gt;
** [[ProjectInfrastructure | Project infrastructure]]&lt;br /&gt;
** [[Roadmap]]&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=File:RackTables-development-roadmap-2019Q2.png&amp;diff=909</id>
		<title>File:RackTables-development-roadmap-2019Q2.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=File:RackTables-development-roadmap-2019Q2.png&amp;diff=909"/>
		<updated>2019-06-12T12:24:28Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=897</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=897"/>
		<updated>2019-03-31T23:54:36Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* List of device commands */ describe getmaclist and getportmaclist&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table (for all ports) as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table (for a specific port only) as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=895</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=895"/>
		<updated>2019-03-31T23:51:37Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Implementation matrix */ add hpprocurveN1178&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=893</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=893"/>
		<updated>2019-03-31T23:48:38Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Implementation matrix */ add ios15&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=891</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=891"/>
		<updated>2019-03-31T23:47:05Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Implementation matrix */ add vrp85&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=889</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=889"/>
		<updated>2019-03-31T23:39:22Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Implementation matrix */ update xos12&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=887</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=887"/>
		<updated>2019-03-31T23:38:04Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Implementation matrix */ update fdry5&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=885</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=885"/>
		<updated>2019-03-31T23:35:37Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Implementation matrix */ add getportmaclist&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=883</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=883"/>
		<updated>2019-03-31T23:09:17Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* List of device breeds */ add vrp85&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp85&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 8.5&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=881</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=881"/>
		<updated>2019-03-31T23:06:45Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* List of device breeds */ fixup the order of entries&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Gateways&amp;diff=879</id>
		<title>Gateways</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Gateways&amp;diff=879"/>
		<updated>2019-03-29T10:09:45Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* List of device breeds */ list the breeds added in 0.21.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==What are RackTables gateways==&lt;br /&gt;
RackTables is a PHP web application. Gateways are special executables residing on the same web-server, but not belonging to RackTables. The executables may be command-line scripts written in PHP, Perl, Python or any other language, or even binary files. Although PHP itself allows execution of arbitrary external commands, RackTables API provides helper functions to make such interaction ordered and convenient.&lt;br /&gt;
&lt;br /&gt;
RackTables has 3 gateways out of the box: '''netcat''', '''telnet''' and '''ssh'''. All of these are remote terminal clients providing the same interface: they take input commands from standard input, execute them on remote device and bring the output to standard output. Connection errors are reported to standard error stream and through exit code. The difference between telnet and netcat clients is that telnet supports telnet protocol escape sequences and can wait for previous command execution to end before pushing the next one. Netcat, on the other hand, streams all commands to TCP socket and closes its write end. Then it waits for remote device to close its write end and finishes. Not all devices support this mode, but at least Cisco IOS 12 devices do.&lt;br /&gt;
&lt;br /&gt;
RackTables has unified API function to work with these clients: '''[[#Setting_up_queryTerminal_function|queryTerminal]]'''. It decides which gateway and which connection parameters to use based on user-defined callback function results.&lt;br /&gt;
&lt;br /&gt;
==List of device breeds==&lt;br /&gt;
In RackTables source code a '''breed''' stands for a distinguished type of a managed device. There are currently the following breeds implemented:&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Aironet IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| D-Link, unknown release&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Arista EOS release 4.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Foundry Networks IronWare release 5.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Force10 FTOS release 8.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Catalyst IOS release 12.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios15&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco IOS release 15.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco XR IOS release 4.2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Juniper JunOS releases 10, 11 and 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| generic Linux&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco Nexus NX-OS releases 4.x, 5.x and 6.x&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Marvell ROS release 1.1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Cisco UCS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP release 5.3&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Huawei VRP releases 5.5 and 5.7&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Extreme Networks XOS release 12&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;hpprocurveN1178&amp;lt;/tt&amp;gt;&lt;br /&gt;
| HP N.11.78&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==List of device commands==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Translates a given array of breed-independent operations into a multiline string with breed-specific commands.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the 802.1Q configuration of the device as a list of arrays.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the device's current configuration as a string in native plain-text format (multiline).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of CDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the list of LLDP neighbors as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the MAC address table as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns the full list of network interfaces as an array.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
| Returns a list of hardware components as an array.&lt;br /&gt;
|}&lt;br /&gt;
==Implementation matrix==&lt;br /&gt;
{| border=1 cellspacing=0 cellpadding=5&lt;br /&gt;
|&lt;br /&gt;
| &amp;lt;tt&amp;gt;xlatepushq&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;get8021q&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getallconf&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getcdpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getlldpstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getmaclist&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getportstatus&amp;lt;/tt&amp;gt;&lt;br /&gt;
| &amp;lt;tt&amp;gt;getinventory&amp;lt;/tt&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;air12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;dlink&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;eos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;fdry5&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ftos8&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ios12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;iosxr4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;jun10&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;linux&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;nxos4&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ros11&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;ucs&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp53&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;vrp55&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;xos12&amp;lt;/tt&amp;gt;&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| bgcolor=green |&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==File layout==&lt;br /&gt;
The gateways part consist of two file sets:&lt;br /&gt;
* PHP files stored in wwwroot/inc/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;deviceconfig.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Contains most vendor-specific functions translating from plain text to RackTables PHP arrays/strings and back. Support for new breeds typically requires adding functions to this file.&lt;br /&gt;
*; &amp;lt;tt&amp;gt;remote.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: The current 0.20.x gateways API. This file normally requires no changes.&lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways.php&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Formerly the main source code of the old API of RackTables 0.16.x to 0.19.x. It was removed in 0.20.2&lt;br /&gt;
&lt;br /&gt;
* CLI tools stored in gateways/ dir:&lt;br /&gt;
*; &amp;lt;tt&amp;gt;netcat	ssh  sshnokey  telnet  ucssdk&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: CLI-mode clients providing a particular communication protocol. &lt;br /&gt;
*; &amp;lt;tt style=&amp;quot;text-decoration: line-through&amp;quot;&amp;gt;gateways/sendfile/  gateways/deviceconfig/  gateways/switchvlans/&amp;lt;/tt&amp;gt;&lt;br /&gt;
*: Old-style CLI tools. Removed in 0.20.2.&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
==Key API functions==&lt;br /&gt;
There are two core functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)&lt;br /&gt;
function callScript ($gwname, $params, $in, &amp;amp;$out, &amp;amp;$errors)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting up queryTerminal function ==&lt;br /&gt;
Every operation racktables performs on device (except of SNMP walk) is made by calling queryTerminal API function.&lt;br /&gt;
It takes care of the communication protocol, connection properties and credentials for each object_id.&lt;br /&gt;
To do so, it must be set up properly. It calls the user-defined callback function '''terminal_settings''' to &lt;br /&gt;
collect the parameters. This function is responsible for overriding of connection properties based on &lt;br /&gt;
local policy. Most of the re-definable parameters have reasonable default values, but username and&lt;br /&gt;
password must be specifyed in any case.&lt;br /&gt;
&lt;br /&gt;
Here is a full schema of $params array (with default values) which could be changed in '''terminal_settings''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$params = array (array&lt;br /&gt;
	(&lt;br /&gt;
		'hostname' =&amp;gt; $endpoints[0], // either hostname or IP&lt;br /&gt;
		'protocol' =&amp;gt; $protocol, // either 'telnet', 'netcat' or 'ssh'&lt;br /&gt;
		'port' =&amp;gt; NULL, // if NULL, 22 for 'ssh' proto and 23 for 'telnet' and 'netcat'&lt;br /&gt;
		'prompt' =&amp;gt; $prompt, // used only by 'telnet'. There is apropriate default values for each device breed known by RackTables&lt;br /&gt;
		'username' =&amp;gt; NULL,&lt;br /&gt;
		'password' =&amp;gt; NULL,&lt;br /&gt;
		'timeout' =&amp;gt; 15,&lt;br /&gt;
		'connect_timeout' =&amp;gt; 2,&lt;br /&gt;
		'prompt_delay' =&amp;gt; 0.001, // 1ms. Used only by 'telnet'&lt;br /&gt;
		'sudo_user' =&amp;gt; NULL, // used only by 'ssh'. If specified, ssh gateway calls itself with sudo -u&lt;br /&gt;
		'identity_file' =&amp;gt; NULL, // used only by 'ssh'. Path to secret key file.&lt;br /&gt;
	));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical implementation of this user-defined callback looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        // servers and Juniper routers use ssh, other - telnet&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 5;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
                $params[0]['username'] = 'username';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 30;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can put your definition of terminal_settings function into your secret.php file.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=877</id>
		<title>SourceCode</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=877"/>
		<updated>2019-01-22T21:05:40Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* the source code repository */ reword and reference the git mirror&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== the source code repository==&lt;br /&gt;
RackTables project uses git as version control system for the source code ([https://github.com/RackTables/racktables GitHub], [http://code.racktables.org/ read-only mirror]). The repository includes the following notable branches:&lt;br /&gt;
;master&lt;br /&gt;
:The main development branch, that is, the default branch for all new changes.&lt;br /&gt;
;maintenance-0.16.x&lt;br /&gt;
;maintenance-0.17.x&lt;br /&gt;
;maintenance-0.18.x&lt;br /&gt;
;maintenance-0.19.x&lt;br /&gt;
;maintenance-0.20.x&lt;br /&gt;
:Archived maintenance branches, which do not accept any new changes.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to get a working copy of the repository:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# from the RackTables project repository&lt;br /&gt;
git clone git://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:RackTables/racktables.git&lt;br /&gt;
&lt;br /&gt;
# from a GitHub fork (of your own or not)&lt;br /&gt;
git clone git://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:username/racktables.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the first two schemas (&amp;lt;tt&amp;gt;git://&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;https://&amp;lt;/tt&amp;gt;) are always '''read-only''' and the third (&amp;lt;tt&amp;gt;ssh://&amp;lt;/tt&amp;gt;) may be '''read-only or read-write'''.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to examine local unsaved changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The recommended way of contributing the work is to:&lt;br /&gt;
# fork RackTables on GitHub,&lt;br /&gt;
# push finished commits to the fork, and&lt;br /&gt;
# create a pull request.&lt;br /&gt;
&lt;br /&gt;
In the case of a private repository use a [http://git-scm.com/docs/git-format-patch git format-patch] command to produce a patch and send it to the developers. When sending a patch, please make sure your branch is [http://git-scm.com/book/en/Git-Branching-Rebasing rebased] onto the latest respective (master or maintenance) branch.&lt;br /&gt;
&lt;br /&gt;
== PHP code style guide ==&lt;br /&gt;
Almost all PHP source code in RackTables is original and the following style guide applies:&lt;br /&gt;
==== use [http://en.wikipedia.org/wiki/Indent_style#Allman_style Allman] indent style. ====&lt;br /&gt;
'''Why:''' Allman style is very diff-friendly when it comes to control structures. In Allman style expansion of a single inner operator into a larger code block (and vice versa) leaves both the inner (controlled) and the outer (controlling) operator intact:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 for ($i = 0; $i &amp;lt; 10; $i++)&lt;br /&gt;
+{&lt;br /&gt;
 	doSomething ($i);&lt;br /&gt;
+	doSomethingElse();&lt;br /&gt;
+}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This works seamlessly at any indentation level and for all control structures (&amp;lt;tt&amp;gt;if/for/foreach/while&amp;lt;/tt&amp;gt;), however deeply nested in each other. With careful indentation of &amp;lt;tt&amp;gt;case&amp;lt;/tt&amp;gt; within &amp;lt;tt&amp;gt;switch&amp;lt;/tt&amp;gt; it will work same well:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
switch ($var)&lt;br /&gt;
{&lt;br /&gt;
	case 1:&lt;br /&gt;
		doSomething();&lt;br /&gt;
		break;&lt;br /&gt;
	case 2:&lt;br /&gt;
-	{&lt;br /&gt;
-		doSomethingElse();&lt;br /&gt;
		return 3;&lt;br /&gt;
-	}&lt;br /&gt;
	case 3:&lt;br /&gt;
+	{&lt;br /&gt;
+		doSomethingCompletelyDifferent();&lt;br /&gt;
		return 4;&lt;br /&gt;
+	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Being diff-friendly means being friendly to a version control system (Subversion in the past, git at present). Neither the VCS nor the Allman style alone guarantee one's commits to be focused and clean. However, Allman style encourages commits that change only the lines that really need to be changed.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
==== use exactly one tab per indentation level ====&lt;br /&gt;
'''Why:''' different people prefer different (2, 4, 8...) amount of &amp;quot;spaces&amp;quot; per indentation level. Some projects use space indentation and force everyone to hit Space (Backspace) N times each time the developer needs to indent (unindent), regardless of their own preferred value of N. RackTables makes use of Tab and enables every developer to configure their editor for their preferred &amp;quot;tab width&amp;quot; (the screenshot below stands for tab width 2). Note that the tab width must stand for the amount of spaces the editor ''displays'', but not ''generates instead of'', that is, in the source file '''tabs must remain tabs'''.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
[[File:interface_tab2.png]]&lt;br /&gt;
&lt;br /&gt;
==== wrap lines longer than 100-120 characters ====&lt;br /&gt;
'''Why:''' readability. Prior experience tells that old-school 80-columns margin just doesn't work with descriptive naming of variables and functions (i.e. descriptive == heavily wrapped). On the other hand, avoid making a readable, 200-characters long line even if it fits your monitor. Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for function arguments ====&lt;br /&gt;
'''Why:''' the purpose is twofold. On one hand it allows to pack a lot of arguments and expressions into a single function call without losing track of what is being done and no temporary variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		usePreparedUpdateBlade&lt;br /&gt;
		(&lt;br /&gt;
			'Port',&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'name' =&amp;gt; $port_name,&lt;br /&gt;
				'type' =&amp;gt; $port_type_id,&lt;br /&gt;
				'label' =&amp;gt; $port_label,&lt;br /&gt;
				'reservation_comment' =&amp;gt; $reservation_comment,&lt;br /&gt;
				'l2address' =&amp;gt; nullEmptyStr ($db_l2address),&lt;br /&gt;
			),&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'id' =&amp;gt; $port_id,&lt;br /&gt;
				'object_id' =&amp;gt; $object_id&lt;br /&gt;
			)&lt;br /&gt;
		);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, it allows to group function arguments add/or add comments to them:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function get8021QSyncOptions&lt;br /&gt;
(&lt;br /&gt;
	$vswitch,&lt;br /&gt;
	$D, // desired config&lt;br /&gt;
	$C, // cached config&lt;br /&gt;
	$R  // running-config&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
	$default_port = array&lt;br /&gt;
	(&lt;br /&gt;
		'mode' =&amp;gt; 'access',&lt;br /&gt;
		'allowed' =&amp;gt; array (VLAN_DFL_ID),&lt;br /&gt;
		'native' =&amp;gt; VLAN_DFL_ID,&lt;br /&gt;
	);&lt;br /&gt;
	$ret = array();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for arrays ====&lt;br /&gt;
'''Why:''' due to a few reasons. First, multiline array assignment/initialization can accommodate a notable amount of supplementary expressions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ret[$pn] = array&lt;br /&gt;
(&lt;br /&gt;
	'status' =&amp;gt; 'martian_conflict',&lt;br /&gt;
	'left' =&amp;gt; array_key_exists ($pn, $C) ? $C[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
	'right' =&amp;gt; array_key_exists ($pn, $R) ? $R[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second, updates to arrays that serve as lists produce cleaner diffs, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--- a/wwwroot/inc/remote.php&lt;br /&gt;
+++ b/wwwroot/inc/remote.php&lt;br /&gt;
@@ -64,6 +64,7 @@ $breedfunc = array&lt;br /&gt;
 	'jun10-get8021q-main'      =&amp;gt; 'jun10Read8021QConfig',&lt;br /&gt;
 	'jun10-xlatepushq-main'    =&amp;gt; 'jun10TranslatePushQueue',&lt;br /&gt;
 	'jun10-getallconf-main'    =&amp;gt; 'jun10SpotConfigText',&lt;br /&gt;
+	'jun10-getlldpstatus-main' =&amp;gt; 'jun10ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-xlatepushq-main'    =&amp;gt; 'ftos8TranslatePushQueue',&lt;br /&gt;
 	'ftos8-getlldpstatus-main' =&amp;gt; 'ftos8ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-getmaclist-main'    =&amp;gt; 'ftos8ReadMacList',&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, it makes tree-like array initialization more transparent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$SQLSchema = array&lt;br /&gt;
(&lt;br /&gt;
	'object' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'table' =&amp;gt; 'RackObject',&lt;br /&gt;
		'columns' =&amp;gt; array&lt;br /&gt;
		(&lt;br /&gt;
			'id' =&amp;gt; 'id',&lt;br /&gt;
			'name' =&amp;gt; 'name',&lt;br /&gt;
			'label' =&amp;gt; 'label',&lt;br /&gt;
			'asset_no' =&amp;gt; 'asset_no',&lt;br /&gt;
			'objtype_id' =&amp;gt; 'objtype_id',&lt;br /&gt;
			'rack_id' =&amp;gt; '(SELECT MIN(rack_id) FROM RackSpace WHERE object_id = RackObject.id)',&lt;br /&gt;
			'rack_id_2' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack')&amp;quot;,&lt;br /&gt;
			'container_id' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object')&amp;quot;,&lt;br /&gt;
			'container_name' =&amp;gt; '(SELECT name FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'container_objtype_id' =&amp;gt; '(SELECT objtype_id FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'has_problems' =&amp;gt; 'has_problems',&lt;br /&gt;
			'comment' =&amp;gt; 'comment',&lt;br /&gt;
			'nports' =&amp;gt; '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',&lt;br /&gt;
			'8021q_domain_id' =&amp;gt; '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
			'8021q_template_id' =&amp;gt; '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
		),&lt;br /&gt;
		'keycolumn' =&amp;gt; 'id',&lt;br /&gt;
		'ordcolumns' =&amp;gt; array ('RackObject.name'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; near its &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
When an &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; operator includes the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; part, consider arranging the branches so that the short branch belongs to the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; and the long branch belongs to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt;. '''Why:''' it helps following the code.&lt;br /&gt;
&lt;br /&gt;
Holub, Allen (1995). Enough Rope to Shoot Yourself in the Foot. McGraw-Hill. ISBN 978-0-07-029689-3&lt;br /&gt;
&amp;quot;Section 57: Put the shortest clause of an &amp;lt;tt&amp;gt;if/else&amp;lt;/tt&amp;gt; on top.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== make conscise use of the [http://en.wikipedia.org/wiki/%3F: conditional ternary operator] ====&lt;br /&gt;
'''Why:''' to use the language element in making the code look closer to what the code is intended to do. First, don't reinvent the conditional ternary operator with &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if ($row !== FALSE)&lt;br /&gt;
    return $row[0];&lt;br /&gt;
  else&lt;br /&gt;
    return NULL;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That block of code returns a value in either case, exactly one &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; would do the job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return $row === FALSE ? NULL : $row[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second, don't nest ternary conditionals, it is hard to read:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $ret = ($ret &amp;gt; 0 ? 1 : ($ret &amp;lt; 0 ? -1 : 0));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For that specific case use &amp;lt;tt&amp;gt;numSign()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when a standalone ternary conditional isn't sufficient or is long/difficult to read, use temporary variables or necessary amount of &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;'s.&lt;br /&gt;
&lt;br /&gt;
==== things to consider when adding a new function ====&lt;br /&gt;
* First, is it possible to use one of the existing functions or a combination of these?&lt;br /&gt;
* Carefully decide which file the function should be in (depends on its purpose and relation to other functions).&lt;br /&gt;
* Consider [http://en.wikipedia.org/wiki/CamelCase headlessCamelCase] for the function name.&lt;br /&gt;
* Try to keep functions focused, that is, small, doing just one thing but doing it very well.&lt;br /&gt;
* When adding a new function, add unit test(s) for it as well.&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;$self = __FUNCTION__;&amp;lt;/tt&amp;gt; at the beginning of a recursive function ====&lt;br /&gt;
'''Why:''' to make it easier to see a recursive function. Such a function should call itself as &amp;lt;tt&amp;gt;$self(...)&amp;lt;/tt&amp;gt; so that if the function is renamed later its code requires no changes to work.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Return a list consisting of tag ID of the given tree node and IDs of all&lt;br /&gt;
# nodes it contains.&lt;br /&gt;
function getTagIDListForNode ($treenode)&lt;br /&gt;
{&lt;br /&gt;
	$self = __FUNCTION__;&lt;br /&gt;
	$ret = array ($treenode['id']);&lt;br /&gt;
	foreach ($treenode['kids'] as $item)&lt;br /&gt;
		$ret = array_merge ($ret, $self ($item));&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== prefer &amp;lt;tt&amp;gt;//&amp;lt;/tt&amp;gt; over &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; for single-line comments ====&lt;br /&gt;
'''Why:''' it saves keystokes (double-press one key versus pressing two different keys simultaneously). However, when adding a comment to a code that already uses &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt;, be consistent with what is already there.&lt;br /&gt;
Also, the code contains ~ 2000 of //-style lines versus ~450 of #-style.&lt;br /&gt;
&lt;br /&gt;
==== don't use &amp;lt;tt&amp;gt;foreach()&amp;lt;/tt&amp;gt; by reference ====&lt;br /&gt;
'''Why:''' this valid language construct has a side effect that complicates things. Consider the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$a = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b', 3 =&amp;gt; 'c');&lt;br /&gt;
foreach ($a as &amp;amp;$tmp)&lt;br /&gt;
  $tmp = $tmp . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So far so good, but once you decide to reuse &amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; and assign a new value to it, you will [unexpectedly] modify the array (&amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; still holds the reference to the last element). [https://bugs.php.net/bug.php?id=37410 This thread] discusses the workings of this at length. The workaround would be to use &amp;lt;tt&amp;gt;unset ($tmp)&amp;lt;/tt&amp;gt; after the foreach-by-reference, but the least-surprising solution is to iterate over array keys if you need to modify array elements and &amp;lt;tt&amp;gt;array_walk()&amp;lt;/tt&amp;gt; is not sufficient:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach (array_keys ($a) as $k)&lt;br /&gt;
  $a[$k] = $a[$k] . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use double-quotes for strings only when necessary ====&lt;br /&gt;
'''Why:''' it saves the time required to tell &amp;quot;just strings&amp;quot; from strings that embed expansions. Consider the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$string1 = 'Single-quotes give a hint that this string is just a string.';&lt;br /&gt;
$string2 = &amp;quot;Double-quotes give a hint that this string not just _may_ contain &amp;quot; .&lt;br /&gt;
  &amp;quot;expansions but it actually does: here are $var1 and ${var2}&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use &amp;lt;tt&amp;gt;array_key_exists()&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; to test a key in an array ====&lt;br /&gt;
'''Why:''' with &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; it is impossible to distinguish a missing element from a typo in the array name:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$arr = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b');&lt;br /&gt;
if (array_key_exists (10, $arr)) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (array_key_exists (10, $ar)) # FALSE and a PHP warning (note the typo)&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($arr[10])) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($ar[10])) # FALSE and no warning&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP 5.5 and 5.6 ====&lt;br /&gt;
'''Why:''' because some long-term supported distributions still support these versions, and that's what the users still use in production.&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP versions 7.x ====&lt;br /&gt;
'''Why:''' because newer PHP versions tend to deprecate functions and extensions, so you have to account for that.&lt;br /&gt;
&lt;br /&gt;
==== use &amp;quot;&amp;amp;&amp;amp;&amp;quot; and &amp;quot;||&amp;quot;, not &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; ====&lt;br /&gt;
'''Why:''' because (unlike, for instance, C++) those are not just alternative notations but quite different operators. From the [http://php.net/manual/en/language.operators.precedence.php PHP operator precedence table] it is possible to see that, for example, the following two lines of code work in a very different way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$c = $a or $b; // Evaluate $a and assign the value to $c. If $c was set to false, evaluate $b and discard the value.&lt;br /&gt;
$c = $a || $b; // Evaluate $a. If the value is true, set $c to true. Otherwise evaluate $b and assign the value to $c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Not everybody remembers about this difference and eventually interprets the code not as PHP actually executes it. As the gain of &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; in RackTables is negligible, the simplest solution is not to use them at all.&lt;br /&gt;
&lt;br /&gt;
==== test for an empty string in the simplest way possible ====&lt;br /&gt;
Just compare with an empty string. Only use &amp;lt;tt&amp;gt;strlen()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;mb_strlen()&amp;lt;/tt&amp;gt; when those are really required, i.e. when it is necessary to know the exact length of a string or a multibyte string.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($s == '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if ($s != '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if (strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use uppercase for NULL, TRUE and FALSE ====&lt;br /&gt;
'''Why:''' because PHP documentation always spells it this way, so let's keep it readbale (PHP interpreter itself is case-insensitive in this regard).&lt;br /&gt;
&lt;br /&gt;
== SQL code style guide ==&lt;br /&gt;
==== use uppercase for SQL keywords and wrap long lines ====&lt;br /&gt;
'''Why:''' readability&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$result = usePreparedSelectBlade&lt;br /&gt;
	(&lt;br /&gt;
		'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' .&lt;br /&gt;
		'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' .&lt;br /&gt;
		'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name',&lt;br /&gt;
		array ($entity_type, $entity_id)&lt;br /&gt;
	);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use SELECT with an explicit list of columns, don't use &amp;lt;tt&amp;gt;SELECT *&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
'''Why:''' this is very well explained in many sources, for instance, [http://stackoverflow.com/questions/3639861/why-is-select-considered-harmful here] and [http://use-the-index-luke.com/blog/2013-08/its-not-about-the-star-stupid here].&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=875</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=875"/>
		<updated>2018-12-18T22:15:53Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: HTTPS is forced now&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2017Q2.png|535px|thumb|right]]&lt;br /&gt;
* for RackTables users&lt;br /&gt;
** The [https://github.com/RackTables/racktables/blob/maintenance-0.20.x/README.md README file] now replaces the old Installation HOWTO.&lt;br /&gt;
** [[RackTablesAdminGuide | Administrator's guide]]&lt;br /&gt;
** [[RackTablesUserGuide | User's guide]]&lt;br /&gt;
** [[8021Q | 802.1Q VLAN management in RackTables]] (still work in progress)&lt;br /&gt;
** [[FAQ]]&lt;br /&gt;
** [[LDAP | LDAP authentication]]&lt;br /&gt;
** [[SAML | SAML authentication]]&lt;br /&gt;
* for RackTables hackers&lt;br /&gt;
** [[SourceCode | Working with RackTables source code]]&lt;br /&gt;
** [[RackTablesDevelGuide | Developer's guide]]&lt;br /&gt;
** [[FeatureWishlist | Feature wishlist]]&lt;br /&gt;
** [[Gateways | Extending RackTables with gateways]]&lt;br /&gt;
** [[Plugins | Extending RackTables with plugins]]&lt;br /&gt;
* for development team&lt;br /&gt;
** [[NewRelease | Checklist and procedures to make a new release]]&lt;br /&gt;
** [[ProjectInfrastructure | Project infrastructure]]&lt;br /&gt;
** [[Roadmap]]&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=873</id>
		<title>SourceCode</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=SourceCode&amp;diff=873"/>
		<updated>2018-11-13T10:34:50Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* don't use language features of PHP 5.3+ */ update and expand the comment about PHP versions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== the source code repository==&lt;br /&gt;
RackTables project uses git as version control system for the source code. The git repository that is used to make &amp;quot;tar.gz&amp;quot; releases is currently hosted on [https://github.com/RackTables/racktables GitHub]. It includes the following notable branches:&lt;br /&gt;
;master&lt;br /&gt;
:The main development branch, that is, the default branch for all new changes.&lt;br /&gt;
;maintenance-0.16.x&lt;br /&gt;
;maintenance-0.17.x&lt;br /&gt;
;maintenance-0.18.x&lt;br /&gt;
;maintenance-0.19.x&lt;br /&gt;
:Archived maintenance branches, which don't accept any new changes.&lt;br /&gt;
;maintenance-0.20.x&lt;br /&gt;
:Current maintenance branche, that is, the stable branch that serves as the base for normal RackTables releases.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to get a working copy of the repository:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# from the RackTables project repository&lt;br /&gt;
git clone git://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/RackTables/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:RackTables/racktables.git&lt;br /&gt;
&lt;br /&gt;
# from a GitHub fork (of your own or not)&lt;br /&gt;
git clone git://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone https://github.com/username/racktables.git&lt;br /&gt;
# or&lt;br /&gt;
git clone ssh://git@github.com:username/racktables.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the first two schemas (&amp;lt;tt&amp;gt;git://&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;https://&amp;lt;/tt&amp;gt;) are always '''read-only''' and the third (&amp;lt;tt&amp;gt;ssh://&amp;lt;/tt&amp;gt;) may be '''read-only or read-write'''.&lt;br /&gt;
&lt;br /&gt;
Use the following commands to examine local unsaved changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The recommended way of contributing the work is to:&lt;br /&gt;
# fork RackTables on GitHub,&lt;br /&gt;
# push finished commits to the fork, and&lt;br /&gt;
# create a pull request.&lt;br /&gt;
&lt;br /&gt;
In the case of a private repository use a [http://git-scm.com/docs/git-format-patch git format-patch] command to produce a patch and send it to the developers. When sending a patch, please make sure your branch is [http://git-scm.com/book/en/Git-Branching-Rebasing rebased] onto the latest respective (master or maintenance) branch.&lt;br /&gt;
&lt;br /&gt;
== PHP code style guide ==&lt;br /&gt;
Almost all PHP source code in RackTables is original and the following style guide applies:&lt;br /&gt;
==== use [http://en.wikipedia.org/wiki/Indent_style#Allman_style Allman] indent style. ====&lt;br /&gt;
'''Why:''' Allman style is very diff-friendly when it comes to control structures. In Allman style expansion of a single inner operator into a larger code block (and vice versa) leaves both the inner (controlled) and the outer (controlling) operator intact:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 for ($i = 0; $i &amp;lt; 10; $i++)&lt;br /&gt;
+{&lt;br /&gt;
 	doSomething ($i);&lt;br /&gt;
+	doSomethingElse();&lt;br /&gt;
+}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This works seamlessly at any indentation level and for all control structures (&amp;lt;tt&amp;gt;if/for/foreach/while&amp;lt;/tt&amp;gt;), however deeply nested in each other. With careful indentation of &amp;lt;tt&amp;gt;case&amp;lt;/tt&amp;gt; within &amp;lt;tt&amp;gt;switch&amp;lt;/tt&amp;gt; it will work same well:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
switch ($var)&lt;br /&gt;
{&lt;br /&gt;
	case 1:&lt;br /&gt;
		doSomething();&lt;br /&gt;
		break;&lt;br /&gt;
	case 2:&lt;br /&gt;
-	{&lt;br /&gt;
-		doSomethingElse();&lt;br /&gt;
		return 3;&lt;br /&gt;
-	}&lt;br /&gt;
	case 3:&lt;br /&gt;
+	{&lt;br /&gt;
+		doSomethingCompletelyDifferent();&lt;br /&gt;
		return 4;&lt;br /&gt;
+	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Being diff-friendly means being friendly to a version control system (Subversion in the past, git at present). Neither the VCS nor the Allman style alone guarantee one's commits to be focused and clean. However, Allman style encourages commits that change only the lines that really need to be changed.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
==== use exactly one tab per indentation level ====&lt;br /&gt;
'''Why:''' different people prefer different (2, 4, 8...) amount of &amp;quot;spaces&amp;quot; per indentation level. Some projects use space indentation and force everyone to hit Space (Backspace) N times each time the developer needs to indent (unindent), regardless of their own preferred value of N. RackTables makes use of Tab and enables every developer to configure their editor for their preferred &amp;quot;tab width&amp;quot; (the screenshot below stands for tab width 2). Note that the tab width must stand for the amount of spaces the editor ''displays'', but not ''generates instead of'', that is, in the source file '''tabs must remain tabs'''.&lt;br /&gt;
&lt;br /&gt;
'''Exception:''' inline (PHP heredoc) HTML, SQL and JS code.&lt;br /&gt;
&lt;br /&gt;
[[File:interface_tab2.png]]&lt;br /&gt;
&lt;br /&gt;
==== wrap lines longer than 100-120 characters ====&lt;br /&gt;
'''Why:''' readability. Prior experience tells that old-school 80-columns margin just doesn't work with descriptive naming of variables and functions (i.e. descriptive == heavily wrapped). On the other hand, avoid making a readable, 200-characters long line even if it fits your monitor. Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for function arguments ====&lt;br /&gt;
'''Why:''' the purpose is twofold. On one hand it allows to pack a lot of arguments and expressions into a single function call without losing track of what is being done and no temporary variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		usePreparedUpdateBlade&lt;br /&gt;
		(&lt;br /&gt;
			'Port',&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'name' =&amp;gt; $port_name,&lt;br /&gt;
				'type' =&amp;gt; $port_type_id,&lt;br /&gt;
				'label' =&amp;gt; $port_label,&lt;br /&gt;
				'reservation_comment' =&amp;gt; $reservation_comment,&lt;br /&gt;
				'l2address' =&amp;gt; nullEmptyStr ($db_l2address),&lt;br /&gt;
			),&lt;br /&gt;
			array&lt;br /&gt;
			(&lt;br /&gt;
				'id' =&amp;gt; $port_id,&lt;br /&gt;
				'object_id' =&amp;gt; $object_id&lt;br /&gt;
			)&lt;br /&gt;
		);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, it allows to group function arguments add/or add comments to them:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function get8021QSyncOptions&lt;br /&gt;
(&lt;br /&gt;
	$vswitch,&lt;br /&gt;
	$D, // desired config&lt;br /&gt;
	$C, // cached config&lt;br /&gt;
	$R  // running-config&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
	$default_port = array&lt;br /&gt;
	(&lt;br /&gt;
		'mode' =&amp;gt; 'access',&lt;br /&gt;
		'allowed' =&amp;gt; array (VLAN_DFL_ID),&lt;br /&gt;
		'native' =&amp;gt; VLAN_DFL_ID,&lt;br /&gt;
	);&lt;br /&gt;
	$ret = array();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use good judgement.&lt;br /&gt;
&lt;br /&gt;
==== consider using indentation/alignment for arrays ====&lt;br /&gt;
'''Why:''' due to a few reasons. First, multiline array assignment/initialization can accommodate a notable amount of supplementary expressions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ret[$pn] = array&lt;br /&gt;
(&lt;br /&gt;
	'status' =&amp;gt; 'martian_conflict',&lt;br /&gt;
	'left' =&amp;gt; array_key_exists ($pn, $C) ? $C[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
	'right' =&amp;gt; array_key_exists ($pn, $R) ? $R[$pn] : array ('mode' =&amp;gt; 'none'),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second, updates to arrays that serve as lists produce cleaner diffs, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--- a/wwwroot/inc/remote.php&lt;br /&gt;
+++ b/wwwroot/inc/remote.php&lt;br /&gt;
@@ -64,6 +64,7 @@ $breedfunc = array&lt;br /&gt;
 	'jun10-get8021q-main'      =&amp;gt; 'jun10Read8021QConfig',&lt;br /&gt;
 	'jun10-xlatepushq-main'    =&amp;gt; 'jun10TranslatePushQueue',&lt;br /&gt;
 	'jun10-getallconf-main'    =&amp;gt; 'jun10SpotConfigText',&lt;br /&gt;
+	'jun10-getlldpstatus-main' =&amp;gt; 'jun10ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-xlatepushq-main'    =&amp;gt; 'ftos8TranslatePushQueue',&lt;br /&gt;
 	'ftos8-getlldpstatus-main' =&amp;gt; 'ftos8ReadLLDPStatus',&lt;br /&gt;
 	'ftos8-getmaclist-main'    =&amp;gt; 'ftos8ReadMacList',&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, it makes tree-like array initialization more transparent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$SQLSchema = array&lt;br /&gt;
(&lt;br /&gt;
	'object' =&amp;gt; array&lt;br /&gt;
	(&lt;br /&gt;
		'table' =&amp;gt; 'RackObject',&lt;br /&gt;
		'columns' =&amp;gt; array&lt;br /&gt;
		(&lt;br /&gt;
			'id' =&amp;gt; 'id',&lt;br /&gt;
			'name' =&amp;gt; 'name',&lt;br /&gt;
			'label' =&amp;gt; 'label',&lt;br /&gt;
			'asset_no' =&amp;gt; 'asset_no',&lt;br /&gt;
			'objtype_id' =&amp;gt; 'objtype_id',&lt;br /&gt;
			'rack_id' =&amp;gt; '(SELECT MIN(rack_id) FROM RackSpace WHERE object_id = RackObject.id)',&lt;br /&gt;
			'rack_id_2' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack')&amp;quot;,&lt;br /&gt;
			'container_id' =&amp;gt; &amp;quot;(SELECT MIN(parent_entity_id) FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object')&amp;quot;,&lt;br /&gt;
			'container_name' =&amp;gt; '(SELECT name FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'container_objtype_id' =&amp;gt; '(SELECT objtype_id FROM RackObject WHERE id = container_id)',&lt;br /&gt;
			'has_problems' =&amp;gt; 'has_problems',&lt;br /&gt;
			'comment' =&amp;gt; 'comment',&lt;br /&gt;
			'nports' =&amp;gt; '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',&lt;br /&gt;
			'8021q_domain_id' =&amp;gt; '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
			'8021q_template_id' =&amp;gt; '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',&lt;br /&gt;
		),&lt;br /&gt;
		'keycolumn' =&amp;gt; 'id',&lt;br /&gt;
		'ordcolumns' =&amp;gt; array ('RackObject.name'),&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; near its &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
When an &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; operator includes the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; part, consider arranging the branches so that the short branch belongs to the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; and the long branch belongs to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt;. '''Why:''' it helps following the code.&lt;br /&gt;
&lt;br /&gt;
Holub, Allen (1995). Enough Rope to Shoot Yourself in the Foot. McGraw-Hill. ISBN 978-0-07-029689-3&lt;br /&gt;
&amp;quot;Section 57: Put the shortest clause of an &amp;lt;tt&amp;gt;if/else&amp;lt;/tt&amp;gt; on top.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== make conscise use of the [http://en.wikipedia.org/wiki/%3F: conditional ternary operator] ====&lt;br /&gt;
'''Why:''' to use the language element in making the code look closer to what the code is intended to do. First, don't reinvent the conditional ternary operator with &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if ($row !== FALSE)&lt;br /&gt;
    return $row[0];&lt;br /&gt;
  else&lt;br /&gt;
    return NULL;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That block of code returns a value in either case, exactly one &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; would do the job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return $row === FALSE ? NULL : $row[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second, don't nest ternary conditionals, it is hard to read:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $ret = ($ret &amp;gt; 0 ? 1 : ($ret &amp;lt; 0 ? -1 : 0));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For that specific case use &amp;lt;tt&amp;gt;numSign()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when a standalone ternary conditional isn't sufficient or is long/difficult to read, use temporary variables or necessary amount of &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;'s.&lt;br /&gt;
&lt;br /&gt;
==== things to consider when adding a new function ====&lt;br /&gt;
* First, is it possible to use one of the existing functions or a combination of these?&lt;br /&gt;
* Carefully decide which file the function should be in (depends on its purpose and relation to other functions).&lt;br /&gt;
* Consider [http://en.wikipedia.org/wiki/CamelCase headlessCamelCase] for the function name.&lt;br /&gt;
* Try to keep functions focused, that is, small, doing just one thing but doing it very well.&lt;br /&gt;
* When adding a new function, add unit test(s) for it as well.&lt;br /&gt;
&lt;br /&gt;
==== put &amp;lt;tt&amp;gt;$self = __FUNCTION__;&amp;lt;/tt&amp;gt; at the beginning of a recursive function ====&lt;br /&gt;
'''Why:''' to make it easier to see a recursive function. Such a function should call itself as &amp;lt;tt&amp;gt;$self(...)&amp;lt;/tt&amp;gt; so that if the function is renamed later its code requires no changes to work.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Return a list consisting of tag ID of the given tree node and IDs of all&lt;br /&gt;
# nodes it contains.&lt;br /&gt;
function getTagIDListForNode ($treenode)&lt;br /&gt;
{&lt;br /&gt;
	$self = __FUNCTION__;&lt;br /&gt;
	$ret = array ($treenode['id']);&lt;br /&gt;
	foreach ($treenode['kids'] as $item)&lt;br /&gt;
		$ret = array_merge ($ret, $self ($item));&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== prefer &amp;lt;tt&amp;gt;//&amp;lt;/tt&amp;gt; over &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; for single-line comments ====&lt;br /&gt;
'''Why:''' it saves keystokes (double-press one key versus pressing two different keys simultaneously). However, when adding a comment to a code that already uses &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt;, be consistent with what is already there.&lt;br /&gt;
Also, the code contains ~ 2000 of //-style lines versus ~450 of #-style.&lt;br /&gt;
&lt;br /&gt;
==== don't use &amp;lt;tt&amp;gt;foreach()&amp;lt;/tt&amp;gt; by reference ====&lt;br /&gt;
'''Why:''' this valid language construct has a side effect that complicates things. Consider the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$a = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b', 3 =&amp;gt; 'c');&lt;br /&gt;
foreach ($a as &amp;amp;$tmp)&lt;br /&gt;
  $tmp = $tmp . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So far so good, but once you decide to reuse &amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; and assign a new value to it, you will [unexpectedly] modify the array (&amp;lt;tt&amp;gt;$tmp&amp;lt;/tt&amp;gt; still holds the reference to the last element). [https://bugs.php.net/bug.php?id=37410 This thread] discusses the workings of this at length. The workaround would be to use &amp;lt;tt&amp;gt;unset ($tmp)&amp;lt;/tt&amp;gt; after the foreach-by-reference, but the least-surprising solution is to iterate over array keys if you need to modify array elements and &amp;lt;tt&amp;gt;array_walk()&amp;lt;/tt&amp;gt; is not sufficient:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach (array_keys ($a) as $k)&lt;br /&gt;
  $a[$k] = $a[$k] . '_str';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use double-quotes for strings only when necessary ====&lt;br /&gt;
'''Why:''' it saves the time required to tell &amp;quot;just strings&amp;quot; from strings that embed expansions. Consider the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$string1 = 'Single-quotes give a hint that this string is just a string.';&lt;br /&gt;
$string2 = &amp;quot;Double-quotes give a hint that this string not just _may_ contain &amp;quot; .&lt;br /&gt;
  &amp;quot;expansions but it actually does: here are $var1 and ${var2}&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use &amp;lt;tt&amp;gt;array_key_exists()&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; to test a key in an array ====&lt;br /&gt;
'''Why:''' with &amp;lt;tt&amp;gt;isset()&amp;lt;/tt&amp;gt; it is impossible to distinguish a missing element from a typo in the array name:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$arr = array (1 =&amp;gt; 'a', 2 =&amp;gt; 'b');&lt;br /&gt;
if (array_key_exists (10, $arr)) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (array_key_exists (10, $ar)) # FALSE and a PHP warning (note the typo)&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($arr[10])) # FALSE&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
if (isset ($ar[10])) # FALSE and no warning&lt;br /&gt;
  echo 'OK';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP 5.5 and 5.6 ====&lt;br /&gt;
'''Why:''' because some long-term supported distributions still support these versions, and that's what the users still use in production.&lt;br /&gt;
&lt;br /&gt;
==== keep the PHP code compatible with PHP versions 7.x ====&lt;br /&gt;
'''Why:''' because newer PHP versions tend to deprecate functions and extensions, so you have to account for that.&lt;br /&gt;
&lt;br /&gt;
==== use &amp;quot;&amp;amp;&amp;amp;&amp;quot; and &amp;quot;||&amp;quot;, not &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; ====&lt;br /&gt;
'''Why:''' because (unlike, for instance, C++) those are not just alternative notations but quite different operators. From the [http://php.net/manual/en/language.operators.precedence.php PHP operator precedence table] it is possible to see that, for example, the following two lines of code work in a very different way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$c = $a or $b; // Evaluate $a and assign the value to $c. If $c was set to false, evaluate $b and discard the value.&lt;br /&gt;
$c = $a || $b; // Evaluate $a. If the value is true, set $c to true. Otherwise evaluate $b and assign the value to $c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Not everybody remembers about this difference and eventually interprets the code not as PHP actually executes it. As the gain of &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; in RackTables is negligible, the simplest solution is not to use them at all.&lt;br /&gt;
&lt;br /&gt;
==== test for an empty string in the simplest way possible ====&lt;br /&gt;
Just compare with an empty string. Only use &amp;lt;tt&amp;gt;strlen()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;mb_strlen()&amp;lt;/tt&amp;gt; when those are really required, i.e. when it is necessary to know the exact length of a string or a multibyte string.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($s == '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if ($s != '') // OK&lt;br /&gt;
  ...&lt;br /&gt;
if (strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
if (! mb_strlen ($s)) // overkill&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use uppercase for NULL, TRUE and FALSE ====&lt;br /&gt;
'''Why:''' because PHP documentation always spells it this way, so let's keep it readbale (PHP interpreter itself is case-insensitive in this regard).&lt;br /&gt;
&lt;br /&gt;
== SQL code style guide ==&lt;br /&gt;
==== use uppercase for SQL keywords and wrap long lines ====&lt;br /&gt;
'''Why:''' readability&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	$result = usePreparedSelectBlade&lt;br /&gt;
	(&lt;br /&gt;
		'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' .&lt;br /&gt;
		'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' .&lt;br /&gt;
		'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name',&lt;br /&gt;
		array ($entity_type, $entity_id)&lt;br /&gt;
	);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== use SELECT with an explicit list of columns, don't use &amp;lt;tt&amp;gt;SELECT *&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
'''Why:''' this is very well explained in many sources, for instance, [http://stackoverflow.com/questions/3639861/why-is-select-considered-harmful here] and [http://use-the-index-luke.com/blog/2013-08/its-not-about-the-star-stupid here].&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=863</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=863"/>
		<updated>2017-10-28T15:05:12Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* part 2 (unit testing) */ also run the large group of tests&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
First make sure that all necessary changes are already committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and that no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure that &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
./express.sh&lt;br /&gt;
phpunit --group large&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=FAQ&amp;diff=861</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=FAQ&amp;diff=861"/>
		<updated>2017-10-27T13:09:54Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Write a patch yourself. */ also mention GitHub PRs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= RackTables FAQ =&lt;br /&gt;
&lt;br /&gt;
== How do I edit this wiki? ==&lt;br /&gt;
Send a message to devteam@racktables.org.&lt;br /&gt;
&lt;br /&gt;
== Why does the SNMP sync feature return Unknown OID (n.n.n.n.n) ==&lt;br /&gt;
RackTables only supports some specific switch models, and yours is not one of them. &lt;br /&gt;
&lt;br /&gt;
There are two ways to add support for new switch models.  Both involve [http://bugs.racktables.org/bug_report_page.php filing a Mantis ticket].&lt;br /&gt;
=== Write a patch yourself. ===&lt;br /&gt;
# Review [[RackTablesDevelGuide#SNMP_sync|this text]] for background information.&lt;br /&gt;
# Add necessary changes to your copy of RackTables source code and test them.&lt;br /&gt;
# Propose the changes as a GitHub pull request or attach the patch to a new ticket in MantisBT.&lt;br /&gt;
&lt;br /&gt;
=== Ask the developers to write a patch for you. ===&lt;br /&gt;
The following information is necessary.&lt;br /&gt;
* The exact model/part number of the device.&lt;br /&gt;
* For a device which features SFP (GBIC, X2 etc) pluggable ports, specify which of these pluggable ports are &amp;quot;combo&amp;quot; (IOW, alternate socket for a copper port under the same name) and which are standalone ports.&lt;br /&gt;
If possible, add the following information:&lt;br /&gt;
* Manufacturer's markup of device's ports (this can be &amp;quot;21, 22, 23, 24&amp;quot;, &amp;quot;21X, 22X, 23X, 24X&amp;quot; or even &amp;quot;21, 22, 23C, 23F, 24C, 24F&amp;quot;)&lt;br /&gt;
* Dump of SNMP info:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
snmpwalk -v 1 -c public switchname sysDescr.0&lt;br /&gt;
snmpwalk -On -v 1 -c public switchname sysObjectID.0&lt;br /&gt;
snmpwalk -v 1 -c public switchname ifTable&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If the device is modular or stackable, also walk ENTITY-MIB:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
snmpwalk -v 1 -c public switchname .1.3.6.1.2.1.47&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If the device is a Cisco stackable switch and its sysObjectID ends with .9.1.516, then enter the configuration command&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
no snmp-server sysobjectid type stack-oid&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
, reboot switch and re-collect the sysObjectID.0&lt;br /&gt;
&lt;br /&gt;
Attach the information to the Mantis ticket.  Do not post it inline, as it consumes too much screen real estate.&lt;br /&gt;
&lt;br /&gt;
== Rack thumb images are broken ==&lt;br /&gt;
There are two common reasons for this:&lt;br /&gt;
# A mis-formatted local.php extension file. For images to work correctly, every PHP file of your RackTables, which begins with '''&amp;lt;?php''' tag, CAN NOT have a newline before the tag. Every PHP file of your RackTables, which ends with '''?&amp;gt;''' tag, CAN NOT have a newline after the tag.&lt;br /&gt;
# GD library is not working (although it probably was at the time of installation). This can be confirmed by means of phpinfo() and fixed with the help of package manager of your server and RackTables README file.&lt;br /&gt;
&lt;br /&gt;
== How do I browse objects by object type? ==&lt;br /&gt;
On the &amp;quot;Objects&amp;quot; page under the &amp;quot;Tag filters&amp;quot; section there should be a text area where you can enter:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{$typeid_XXX}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Replace XXX with the Type ID of the type you are looking for. The easiest way to do that is to open object's page of an object that has the type you need and then to click the link next to the &amp;quot;Object type&amp;quot; header in the Summary block:&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:Object_type.png]]&lt;br /&gt;
&lt;br /&gt;
== How do I handle blade systems/modular switches? ==&lt;br /&gt;
You can have one object represent the chassis, and other objects represent the blades.  See the documentation on [[RackTablesUserGuide#Containers|Containers]] for details.&lt;br /&gt;
&lt;br /&gt;
== How do I re-order racks in a row? ==&lt;br /&gt;
In versions prior to 0.20, RackTables sorts in alphabetical order. A work-around is to prefix each rack's name with a number:&lt;br /&gt;
* &amp;quot;01 F14&amp;quot;&lt;br /&gt;
* &amp;quot;02 E14&amp;quot;&lt;br /&gt;
* &amp;quot;03 D14&amp;quot;&lt;br /&gt;
* &amp;quot;04 C14&amp;quot;&lt;br /&gt;
* &amp;quot;05 B14&amp;quot;&lt;br /&gt;
* &amp;quot;06 A14&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Starting with 0.20, you can manually sort racks.  View the row, then click the &amp;quot;Manage racks&amp;quot; tab.&lt;br /&gt;
&lt;br /&gt;
== How do I log out (and log in after that?) ==&lt;br /&gt;
To log out: Use the &amp;quot;Click here to logout&amp;quot; link. When presented with the username/password prompt, do not enter anything.  Instead, press &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To log in: Press your browser's &amp;quot;back&amp;quot; button to return to any of the normal pages (without &amp;quot;index.php?logout&amp;quot; in the URL) and enter your username/password.&lt;br /&gt;
&lt;br /&gt;
== How do I enable IPv4 for object type X in versions before 0.20.6? ==&lt;br /&gt;
Answered by Ray Robertson:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
Configuration --&amp;gt; User Interface --&amp;gt; Change&lt;br /&gt;
 &lt;br /&gt;
Edit the entry for 'List source: IPv4-enabled objects'&lt;br /&gt;
 &lt;br /&gt;
e.g.&lt;br /&gt;
List source: IPv4-enabled objects  {$typeid_2} or {$typeid_4} or {$typeid_7} or {$typeid_8} or {$typeid_12} or {$typeid_445} or {$typeid_447} or {$typeid_798}&lt;br /&gt;
 &lt;br /&gt;
To determine an object's typeid, hover your mouse pointer over the 'Object type' field when viewing the object.  The typeid will be revealed in the URL.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Scenario:''' &lt;br /&gt;
&lt;br /&gt;
At Company X you have a proprietary transceiver-type device that is network accessible &amp;amp; can be assigned an IP. You add a new RackObjectType called &amp;quot;Transceiver&amp;quot;. To add the IPv4 tab to new &amp;quot;Transceiver&amp;quot; objects, do the following.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
1. Navigate to Main &amp;gt; Configuration &amp;gt; Dictionary &amp;gt; RackObjectType.&lt;br /&gt;
&lt;br /&gt;
2. Mouse-over the new RackObjectType you created, in this case &amp;quot;Transceiver&amp;quot;. In this example, the Transceiver typeid is 50012.&lt;br /&gt;
&lt;br /&gt;
3. Now navigate to Main &amp;gt; Configuration &amp;gt; User Interface &amp;gt; Change.&lt;br /&gt;
&lt;br /&gt;
4. Scroll down to the item/entry &amp;quot;List source: IPv4-enabled objects&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
5. Now go to the end of the line, and enter &amp;quot;or {$typeid_50012}&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
6. Your new line looks like: {$typeid_2} or {$typeid_4} or {$typeid_7} or {$typeid_8} or {$typeid_12} or {$typeid_445} or {$typeid_447} or {$typeid_798} or {$typeid_50012}&lt;br /&gt;
&lt;br /&gt;
7. Save changes.&lt;br /&gt;
&lt;br /&gt;
8. Navigate to one of your Transceiver objects, and confirm that the IPv4 tab is now present.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==How do I decode the IPv4 addresses stored in RackTables MySQL database?==&lt;br /&gt;
RackTables stores all IPv4 addresses in their natural representation (32-bit unsigned integers like 180879936). The most reliable way is to use RackTables PHP function spotEntity(), which will return a structure filled with assorted fields, including a dotted-quad representation of IP address. If for whatever reason you decide to fetch the data directly from MySQL database, the simplest way of converting the intergers to dotted-quad (10.200.2.64) form and back is to use MySQL's functions INET_NTOA() and INET_ATON() respectively:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql&amp;gt; SELECT ip, INET_NTOA(ip), mask FROM IPv4Network;&lt;br /&gt;
+-----------+---------------+------+&lt;br /&gt;
| ip        | INET_NTOA(ip) | mask |&lt;br /&gt;
+-----------+---------------+------+&lt;br /&gt;
| 180879360 | 10.200.0.0    |   31 |&lt;br /&gt;
| 180879362 | 10.200.0.2    |   31 |&lt;br /&gt;
| 180879364 | 10.200.0.4    |   31 |&lt;br /&gt;
| 180879366 | 10.200.0.6    |   31 |&lt;br /&gt;
| 180879616 | 10.200.1.0    |   26 |&lt;br /&gt;
| 180879680 | 10.200.1.64   |   26 |&lt;br /&gt;
| 180879872 | 10.200.2.0    |   26 |&lt;br /&gt;
| 180879936 | 10.200.2.64   |   26 |&lt;br /&gt;
| 180880192 | 10.200.3.64   |   26 |&lt;br /&gt;
| 180880384 | 10.200.4.0    |   26 |&lt;br /&gt;
| 180880448 | 10.200.4.64   |   26 |&lt;br /&gt;
+-----------+---------------+------+&lt;br /&gt;
11 rows in set (0.00 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Is there a simpler way to export IP addresses?==&lt;br /&gt;
The method above has some disadvantages: IPv6 addresses are stored in different format, you aren't able to filter the objects to export using RackCode. In general, users should prefer to write PHP exporters rather than SQL ones. Here is an example of a simple exporter printing all IP allocaions for objects tagged by {debian wheezy}:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$script_mode = TRUE;&lt;br /&gt;
require 'wwwroot/inc/init.php';&lt;br /&gt;
foreach (scanRealmByText ('object', '{debian wheezy}') as $object)&lt;br /&gt;
{&lt;br /&gt;
	amplifyCell ($object);&lt;br /&gt;
	foreach ($object['ipv4'] + $object['ipv6'] as $ip_bin =&amp;gt; $alloc)&lt;br /&gt;
		echo implode (&amp;quot;\t&amp;quot;, [ $object['name'], $alloc['osif'], ip_format ($ip_bin) ]) . &amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==There is an &amp;quot;Unknown column 'is_userdefined'&amp;quot; PDO exception on upgrade from version 0.17.x to 0.19.x==&lt;br /&gt;
This is a known issue. The workaround is to upgrade from 0.17.x to 0.18.7, then to 0.19.x. Release 0.18.7 can be downloaded directly from the git repository: https://github.com/RackTables/racktables/zipball/RackTables-0.18.7&lt;br /&gt;
&lt;br /&gt;
==How do I manage tags on a series of objects (networks etc) automaticaly?==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$script_mode = TRUE;&lt;br /&gt;
include '/usr/local/racktables/wwwroot/inc/init.php';&lt;br /&gt;
&lt;br /&gt;
# add tag &amp;quot;right&amp;quot; to each object tagged &amp;quot;left&amp;quot;&lt;br /&gt;
$added = getTagByName ('right');&lt;br /&gt;
foreach (scanRealmByText ('object', '{left}') as $object)&lt;br /&gt;
        rebuildTagChainForEntity ('object', $object['id'], array ($added));&lt;br /&gt;
&lt;br /&gt;
# remove tag &amp;quot;left&amp;quot; from each object tagged &amp;quot;right&amp;quot;&lt;br /&gt;
$removed = getTagByName ('left');&lt;br /&gt;
foreach (scanRealmByText ('object', '{left}') as $object)&lt;br /&gt;
        deleteTagForEntity ('object', $object['id'], $removed['id']);&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= RackTables contributions =&lt;br /&gt;
== How can I visualise network topology? ==&lt;br /&gt;
See [[Visualisation with GraphViz]] for ideas using GraphViz.&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=859</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=859"/>
		<updated>2017-10-05T16:46:03Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* updating the main web-site */ update the steps after the switch from SF&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
First make sure that all necessary changes are already committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and that no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure that &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
./tests/express.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* In racktables-htdocs repository modify and commit &amp;lt;tt&amp;gt;header.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* On the web-server run &amp;lt;tt&amp;gt;git pull&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Open https://racktables.org and test the &amp;quot;download&amp;quot; link to work.&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=853</id>
		<title>ProjectInfrastructure</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=ProjectInfrastructure&amp;diff=853"/>
		<updated>2017-10-02T16:30:29Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: Update for the recent SF vhost cease.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Mapping of assets==&lt;br /&gt;
=== DNS ===&lt;br /&gt;
racktables.org domain is currently registered through GoDaddy, which also provides 2 free nameservers. Aaron manages the domain.&lt;br /&gt;
&lt;br /&gt;
=== racktables.org mail ===&lt;br /&gt;
racktables.org mail is handled by zohomail.com.  Zoho Mail offers free service for up to three e-mail addresses.  Two mailing lists exist, &amp;quot;devteam&amp;quot; and &amp;quot;info&amp;quot;, which are setup to forward all messages to every development team member.&lt;br /&gt;
&lt;br /&gt;
=== racktables-users mail ===&lt;br /&gt;
FreeLists is a great free service without accompanying advertisements. The only drawback about it is that it is non-profit and can go down some day, hence '''make sure the list of subscribers is present in a recent backup tarball'''.&lt;br /&gt;
&lt;br /&gt;
To manage the mailing list, go to [http://www.freelists.org/login.html this page] and use the form to the left. Input your email subscribed to the list. If you don't remember your password, log in with an empty password, in this case the system will email you a one-time token to input on the next screen. Once logged in, you will see a very long SELECT with all lists of FreeLists. Click on the SELECT and press &amp;quot;r&amp;quot; to get to items starting with R. Select racktables-users and press &amp;quot;Select list&amp;quot; below. On the next screen press &amp;quot;Admin Menu&amp;quot;. To manage individual subscribers, select one on the &amp;quot;Current List Users&amp;quot; SELECT and press &amp;quot;User Options&amp;quot;. VACATION, MODPOST and NOPOST are the most frequently used flags, their meaning is explained inline.&lt;br /&gt;
&lt;br /&gt;
Please think twice before changing any of the list-wide settings on the &amp;quot;Edit List Config&amp;quot; menu, these settings influence all subscribers of racktables-users.&lt;br /&gt;
&lt;br /&gt;
=== All web-sites (demo, bugs, wiki, www) ===&lt;br /&gt;
Arnaud Launay is the server administrator. To get SSH access there, be a team member and find someone who already has access.&lt;br /&gt;
&lt;br /&gt;
=== Statistics ===&lt;br /&gt;
SF download stats work for the downloads. Google analytics works for the content of racktables.org and demo.racktables.org (it does not currently display its admin interface in every browser).&lt;br /&gt;
&lt;br /&gt;
=== downloads ===&lt;br /&gt;
SourceForge project FRS (File Release System, a CDN of sorts).&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==Backup procedure==&lt;br /&gt;
===the backup directory===&lt;br /&gt;
 mkdir RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
 cd RackTables-project-backup-`date +%Y%m%d`&lt;br /&gt;
&lt;br /&gt;
===racktables-users mailing list===&lt;br /&gt;
Send an empty email with subject &amp;quot;who racktables-users&amp;quot; to ecartis@freelists.org and you will get a list of all subscribers. Save it to the backup directory as &amp;quot;racktables-users.txt&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Git repositories===&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables.git racktables.git&lt;br /&gt;
 git clone --mirror git://github.com/RackTables/racktables-contribs.git racktables-contribs.git&lt;br /&gt;
&lt;br /&gt;
===Bug tracker, wiki data, www===&lt;br /&gt;
SSH into the host and create a new backup directory.&lt;br /&gt;
 mkdir ~/RackTables-apps-backup&lt;br /&gt;
 cd ~/RackTables-apps-backup&lt;br /&gt;
&lt;br /&gt;
Copy the applications into the backup directory on the server. This is important because they contain uploaded files and configuration settings.&lt;br /&gt;
 tar czf bugs.tar.gz ~/www/bugs&lt;br /&gt;
 tar czf wiki.tar.gz ~/www/wiki&lt;br /&gt;
 tar czf www.tar.gz ~/www/www&lt;br /&gt;
&lt;br /&gt;
Export the database for each application.&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; bugs.sql&lt;br /&gt;
 mysqldump -u &amp;lt;username&amp;gt; -p &amp;lt;dbname&amp;gt; &amp;gt; wiki.sql&lt;br /&gt;
&lt;br /&gt;
SCP the remote server directory to the local backup directory, then remove it from the server.&lt;br /&gt;
&lt;br /&gt;
===Final check===&lt;br /&gt;
Now the backup directory must have these files/directories:&lt;br /&gt;
* racktables.git&lt;br /&gt;
* racktables-contribs.git&lt;br /&gt;
* racktables-users.txt&lt;br /&gt;
* RackTables-apps-backup&lt;br /&gt;
** wiki.tar.gz&lt;br /&gt;
** wiki.sql&lt;br /&gt;
** bugs.tar.gz&lt;br /&gt;
** bugs.sql&lt;br /&gt;
** www.tar.gz&lt;br /&gt;
'''SAVE THE BACKUP FOLDER TO A SAFE PLACE'''&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Plugins&amp;diff=851</id>
		<title>Plugins</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Plugins&amp;diff=851"/>
		<updated>2017-10-02T16:25:21Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: Remove the warning.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Plugins provide the ability to add functionality to RackTables and, in some cases, override existing behavior.  See the [https://github.com/RackTables/racktables-contribs racktables-contribs] repository for user-submitted plugins.&lt;br /&gt;
&lt;br /&gt;
Note that this guide details features that may or may not be included in a future release.&lt;br /&gt;
&lt;br /&gt;
=== States ===&lt;br /&gt;
A plugin may be in one of three states.&lt;br /&gt;
* Not installed - it exists in the plugins directory, but hasn't been installed yet&lt;br /&gt;
* Enabled - it has been installed and is active&lt;br /&gt;
* Disabled - it has been installed and was subsequently disabled&lt;br /&gt;
&lt;br /&gt;
=== Administration ===&lt;br /&gt;
To install a plugin, first copy it to the &amp;quot;plugins&amp;quot; directory.  Then visit the Configuration -&amp;gt; Plugins page and click the Edit tab.  The new plugin should be listed with a state of &amp;quot;Not installed&amp;quot;.  Click the Install icon.&lt;br /&gt;
&lt;br /&gt;
To upgrade a plugin, first backup the appropriate &amp;quot;plugins&amp;quot; sub-directory.  To be safe, it's also a good idea to backup your entire database.  Afterwards, copy over the new version into the &amp;quot;plugins&amp;quot; directory.  When you visit the Edit tab of the Configuration -&amp;gt; Plugins page, you should see a variance in the Code Version and DB Version columns.  Click the Upgrade icon.&lt;br /&gt;
&lt;br /&gt;
== Writing a Plugin ==&lt;br /&gt;
The old procedure involves placing a PHP file in the &amp;quot;plugins&amp;quot; directory of your RackTables installation.  That method has limited functionality and may be deprecated at a later date.&lt;br /&gt;
&lt;br /&gt;
A new plugin architecture was introduced in version 0.21.0.  It includes the ability to install, uninstall, enable and disable plugins from the web interface.  It is assumed that each plugin resides in its own directory within the main &amp;quot;plugins&amp;quot; directory, and that each plugin contains a &amp;quot;plugin.php&amp;quot; file which includes certain functions.  The design was inspired by [http://docs.cacti.net/plugins Cacti's] plugin architecture.&lt;br /&gt;
&lt;br /&gt;
=== Files ===&lt;br /&gt;
Each plugin is expected to contain certain files.  While the only required file is &amp;quot;plugin.php&amp;quot;, it is recommended that you include all mentioned files, especially if you intend to publish it for others.&lt;br /&gt;
&lt;br /&gt;
* README - Provide a general description.  If you expect the plugin to be accepted into the racktables-contribs repo, use the [https://help.github.com/articles/github-flavored-markdown/ Markdown] format.&lt;br /&gt;
* ChangeLog - Document changes made to each revision.&lt;br /&gt;
* LICENSE&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Name&lt;br /&gt;
!Returns&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_info&lt;br /&gt;
|array&lt;br /&gt;
|Array contains 'name', 'longname', 'version' &amp;amp; 'home_url'&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_init&lt;br /&gt;
|void&lt;br /&gt;
|Initialize the plugin.  Register pages, tabs, triggers, handlers, etc.&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_install&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (create tables, create Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_uninstall&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (drop tables, delete Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_upgrade&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (alter tables, modify Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_HOOKNAME&lt;br /&gt;
|varies&lt;br /&gt;
|Run when called by a system-level function (described below in the Hooks section)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
There are used in cases where custom functions defined by a plugin should be called at specific times.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* When resetting or deleting an object&lt;br /&gt;
* Resetting the Config settings (Configuration -&amp;gt; User interface)&lt;br /&gt;
* Viewing the Data Integrity report&lt;br /&gt;
&lt;br /&gt;
Hooks are already supported in some functions, such as commitResetObject().  If you need it supported by another function, create a GitHub pull request and it will probably be accepted.&lt;br /&gt;
&lt;br /&gt;
=== Upgrades ===&lt;br /&gt;
The plugin_PLUGINNAME_upgrade function is mandatory.  If this is the first version, or if no upgrade steps are required, simply have the function return TRUE.&lt;br /&gt;
&lt;br /&gt;
There are many cases where upgrades involve adding/modifying/deleting tables, Config settings or other pieces of information.  These transitions should be handled by the upgrade function.&lt;br /&gt;
&lt;br /&gt;
Example modeled after the core RackTables upgrader:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function plugin_myplugin_upgrade ()&lt;br /&gt;
{&lt;br /&gt;
	$db_info = getPlugin ('myplugin');&lt;br /&gt;
	$v1 = $db_info['db_version'];&lt;br /&gt;
	$code_info = plugin_plugin_info ();&lt;br /&gt;
	$v2 = $code_info['version'];&lt;br /&gt;
	&lt;br /&gt;
	if ($v1 == $v2)&lt;br /&gt;
		throw new RackTablesError ('Versions are identical', RackTablesError::INTERNAL);&lt;br /&gt;
&lt;br /&gt;
	// find the upgrade path to be taken&lt;br /&gt;
	$versionhistory = array&lt;br /&gt;
	(&lt;br /&gt;
		'1.0',&lt;br /&gt;
		'2.0',&lt;br /&gt;
		'3.0'&lt;br /&gt;
	);&lt;br /&gt;
	$skip = TRUE;&lt;br /&gt;
	$path = NULL;&lt;br /&gt;
	foreach ($versionhistory as $v)&lt;br /&gt;
	{&lt;br /&gt;
		if ($skip and $v == $v1)&lt;br /&gt;
		{&lt;br /&gt;
			$skip = FALSE;&lt;br /&gt;
			$path = array();&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		if ($skip)&lt;br /&gt;
			continue;&lt;br /&gt;
		$path[] = $v;&lt;br /&gt;
		if ($v == $v2)&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
	if ($path === NULL or ! count ($path))&lt;br /&gt;
		throw new RackTablesError ('Unable to determine upgrade path', RackTablesError::INTERNAL);&lt;br /&gt;
&lt;br /&gt;
	// build the list of queries to execute&lt;br /&gt;
	$queries = array ();&lt;br /&gt;
	foreach ($path as $batchid)&lt;br /&gt;
	{&lt;br /&gt;
		switch ($batchid)&lt;br /&gt;
		{&lt;br /&gt;
			case '2.0':&lt;br /&gt;
				// perform some upgrade step here&lt;br /&gt;
				$queries[] = &amp;quot;UPDATE Plugin SET version = '2.0' WHERE name = 'myplugin'&amp;quot;;&lt;br /&gt;
				break;&lt;br /&gt;
			case '3.0':&lt;br /&gt;
				// perform some upgrade step here&lt;br /&gt;
				$queries[] = &amp;quot;UPDATE Plugin SET version = '3.0' WHERE name = 'myplugin'&amp;quot;;&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
				throw new RackTablesError (&amp;quot;Preparing to upgrade to $batchid failed&amp;quot;, RackTablesError::INTERNAL);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// execute the queries&lt;br /&gt;
	global $dbxlink;&lt;br /&gt;
	foreach ($queries as $q)&lt;br /&gt;
	{&lt;br /&gt;
		try&lt;br /&gt;
		{&lt;br /&gt;
			$result = $dbxlink-&amp;gt;query ($q);&lt;br /&gt;
		}&lt;br /&gt;
		catch (PDOException $e)&lt;br /&gt;
		{&lt;br /&gt;
			$errorInfo = $dbxlink-&amp;gt;errorInfo();&lt;br /&gt;
			throw new RackTablesError (&amp;quot;Query: ${errorInfo[2]}&amp;quot;, RackTablesError::INTERNAL);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Plugins&amp;diff=849</id>
		<title>Plugins</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Plugins&amp;diff=849"/>
		<updated>2017-10-02T16:24:53Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* Writing a Plugin */ It was done in 0.21.0, not 0.20.11.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Warning ==&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
'''This page is a part of work in progress, it documents a feature that is not yet in RackTables source code.'''&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
Plugins provide the ability to add functionality to RackTables and, in some cases, override existing behavior.  See the [https://github.com/RackTables/racktables-contribs racktables-contribs] repository for user-submitted plugins.&lt;br /&gt;
&lt;br /&gt;
Note that this guide details features that may or may not be included in a future release.&lt;br /&gt;
&lt;br /&gt;
=== States ===&lt;br /&gt;
A plugin may be in one of three states.&lt;br /&gt;
* Not installed - it exists in the plugins directory, but hasn't been installed yet&lt;br /&gt;
* Enabled - it has been installed and is active&lt;br /&gt;
* Disabled - it has been installed and was subsequently disabled&lt;br /&gt;
&lt;br /&gt;
=== Administration ===&lt;br /&gt;
To install a plugin, first copy it to the &amp;quot;plugins&amp;quot; directory.  Then visit the Configuration -&amp;gt; Plugins page and click the Edit tab.  The new plugin should be listed with a state of &amp;quot;Not installed&amp;quot;.  Click the Install icon.&lt;br /&gt;
&lt;br /&gt;
To upgrade a plugin, first backup the appropriate &amp;quot;plugins&amp;quot; sub-directory.  To be safe, it's also a good idea to backup your entire database.  Afterwards, copy over the new version into the &amp;quot;plugins&amp;quot; directory.  When you visit the Edit tab of the Configuration -&amp;gt; Plugins page, you should see a variance in the Code Version and DB Version columns.  Click the Upgrade icon.&lt;br /&gt;
&lt;br /&gt;
== Writing a Plugin ==&lt;br /&gt;
The old procedure involves placing a PHP file in the &amp;quot;plugins&amp;quot; directory of your RackTables installation.  That method has limited functionality and may be deprecated at a later date.&lt;br /&gt;
&lt;br /&gt;
A new plugin architecture was introduced in version 0.21.0.  It includes the ability to install, uninstall, enable and disable plugins from the web interface.  It is assumed that each plugin resides in its own directory within the main &amp;quot;plugins&amp;quot; directory, and that each plugin contains a &amp;quot;plugin.php&amp;quot; file which includes certain functions.  The design was inspired by [http://docs.cacti.net/plugins Cacti's] plugin architecture.&lt;br /&gt;
&lt;br /&gt;
=== Files ===&lt;br /&gt;
Each plugin is expected to contain certain files.  While the only required file is &amp;quot;plugin.php&amp;quot;, it is recommended that you include all mentioned files, especially if you intend to publish it for others.&lt;br /&gt;
&lt;br /&gt;
* README - Provide a general description.  If you expect the plugin to be accepted into the racktables-contribs repo, use the [https://help.github.com/articles/github-flavored-markdown/ Markdown] format.&lt;br /&gt;
* ChangeLog - Document changes made to each revision.&lt;br /&gt;
* LICENSE&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Name&lt;br /&gt;
!Returns&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_info&lt;br /&gt;
|array&lt;br /&gt;
|Array contains 'name', 'longname', 'version' &amp;amp; 'home_url'&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_init&lt;br /&gt;
|void&lt;br /&gt;
|Initialize the plugin.  Register pages, tabs, triggers, handlers, etc.&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_install&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (create tables, create Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_uninstall&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (drop tables, delete Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_upgrade&lt;br /&gt;
|boolean&lt;br /&gt;
|Make any necessary modifications (alter tables, modify Config settings, etc.)&lt;br /&gt;
|-&lt;br /&gt;
|plugin_PLUGINNAME_HOOKNAME&lt;br /&gt;
|varies&lt;br /&gt;
|Run when called by a system-level function (described below in the Hooks section)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
There are used in cases where custom functions defined by a plugin should be called at specific times.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* When resetting or deleting an object&lt;br /&gt;
* Resetting the Config settings (Configuration -&amp;gt; User interface)&lt;br /&gt;
* Viewing the Data Integrity report&lt;br /&gt;
&lt;br /&gt;
Hooks are already supported in some functions, such as commitResetObject().  If you need it supported by another function, create a GitHub pull request and it will probably be accepted.&lt;br /&gt;
&lt;br /&gt;
=== Upgrades ===&lt;br /&gt;
The plugin_PLUGINNAME_upgrade function is mandatory.  If this is the first version, or if no upgrade steps are required, simply have the function return TRUE.&lt;br /&gt;
&lt;br /&gt;
There are many cases where upgrades involve adding/modifying/deleting tables, Config settings or other pieces of information.  These transitions should be handled by the upgrade function.&lt;br /&gt;
&lt;br /&gt;
Example modeled after the core RackTables upgrader:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function plugin_myplugin_upgrade ()&lt;br /&gt;
{&lt;br /&gt;
	$db_info = getPlugin ('myplugin');&lt;br /&gt;
	$v1 = $db_info['db_version'];&lt;br /&gt;
	$code_info = plugin_plugin_info ();&lt;br /&gt;
	$v2 = $code_info['version'];&lt;br /&gt;
	&lt;br /&gt;
	if ($v1 == $v2)&lt;br /&gt;
		throw new RackTablesError ('Versions are identical', RackTablesError::INTERNAL);&lt;br /&gt;
&lt;br /&gt;
	// find the upgrade path to be taken&lt;br /&gt;
	$versionhistory = array&lt;br /&gt;
	(&lt;br /&gt;
		'1.0',&lt;br /&gt;
		'2.0',&lt;br /&gt;
		'3.0'&lt;br /&gt;
	);&lt;br /&gt;
	$skip = TRUE;&lt;br /&gt;
	$path = NULL;&lt;br /&gt;
	foreach ($versionhistory as $v)&lt;br /&gt;
	{&lt;br /&gt;
		if ($skip and $v == $v1)&lt;br /&gt;
		{&lt;br /&gt;
			$skip = FALSE;&lt;br /&gt;
			$path = array();&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		if ($skip)&lt;br /&gt;
			continue;&lt;br /&gt;
		$path[] = $v;&lt;br /&gt;
		if ($v == $v2)&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
	if ($path === NULL or ! count ($path))&lt;br /&gt;
		throw new RackTablesError ('Unable to determine upgrade path', RackTablesError::INTERNAL);&lt;br /&gt;
&lt;br /&gt;
	// build the list of queries to execute&lt;br /&gt;
	$queries = array ();&lt;br /&gt;
	foreach ($path as $batchid)&lt;br /&gt;
	{&lt;br /&gt;
		switch ($batchid)&lt;br /&gt;
		{&lt;br /&gt;
			case '2.0':&lt;br /&gt;
				// perform some upgrade step here&lt;br /&gt;
				$queries[] = &amp;quot;UPDATE Plugin SET version = '2.0' WHERE name = 'myplugin'&amp;quot;;&lt;br /&gt;
				break;&lt;br /&gt;
			case '3.0':&lt;br /&gt;
				// perform some upgrade step here&lt;br /&gt;
				$queries[] = &amp;quot;UPDATE Plugin SET version = '3.0' WHERE name = 'myplugin'&amp;quot;;&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
				throw new RackTablesError (&amp;quot;Preparing to upgrade to $batchid failed&amp;quot;, RackTablesError::INTERNAL);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// execute the queries&lt;br /&gt;
	global $dbxlink;&lt;br /&gt;
	foreach ($queries as $q)&lt;br /&gt;
	{&lt;br /&gt;
		try&lt;br /&gt;
		{&lt;br /&gt;
			$result = $dbxlink-&amp;gt;query ($q);&lt;br /&gt;
		}&lt;br /&gt;
		catch (PDOException $e)&lt;br /&gt;
		{&lt;br /&gt;
			$errorInfo = $dbxlink-&amp;gt;errorInfo();&lt;br /&gt;
			throw new RackTablesError (&amp;quot;Query: ${errorInfo[2]}&amp;quot;, RackTablesError::INTERNAL);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=847</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=847"/>
		<updated>2017-10-02T16:24:13Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: add a link to the plugins page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2017Q2.png|535px|thumb|right]]&lt;br /&gt;
* for RackTables users&lt;br /&gt;
** The [https://github.com/RackTables/racktables/blob/maintenance-0.20.x/README.md README file] now replaces the old Installation HOWTO.&lt;br /&gt;
** [[RackTablesAdminGuide | Administrator's guide]]&lt;br /&gt;
** [[RackTablesUserGuide | User's guide]]&lt;br /&gt;
** [[8021Q | 802.1Q VLAN management in RackTables]] (still work in progress)&lt;br /&gt;
** [[FAQ]]&lt;br /&gt;
** [[LDAP | LDAP authentication]]&lt;br /&gt;
** [[SAML | SAML authentication]]&lt;br /&gt;
* for RackTables hackers&lt;br /&gt;
** [[SourceCode | Working with RackTables source code]]&lt;br /&gt;
** [[RackTablesDevelGuide | Developer's guide]]&lt;br /&gt;
** [[FeatureWishlist | Feature wishlist]]&lt;br /&gt;
** [[Gateways | Extending RackTables with gateways]]&lt;br /&gt;
** [[Plugins | Extending RackTables with plugins]]&lt;br /&gt;
* for development team&lt;br /&gt;
** [[NewRelease | Checklist and procedures to make a new release]]&lt;br /&gt;
** [[ProjectInfrastructure | Project infrastructure]]&lt;br /&gt;
** [[Roadmap]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To use this site over an SSL connection, visit [https://wiki.racktables.org/ https://wiki.racktables.org/]&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=845</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=845"/>
		<updated>2017-10-01T20:44:37Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* part 2 (unit testing) */ just run express.sh&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
First make sure that all necessary changes are already committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and that no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure that &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
./tests/express.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* Log into SF shell service:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat ~/.ssh/config &lt;br /&gt;
Host shell.sourceforge.net&lt;br /&gt;
	User YOUR_SF_USERNAME,racktables&lt;br /&gt;
$ ssh -t shell.sourceforge.net create&lt;br /&gt;
# Sometimes the above command doesn't drop you into the newly created session, in this case&lt;br /&gt;
# an additional &amp;quot;ssh shell.sourceforge.net&amp;quot; will put you through.&lt;br /&gt;
# upon logging in update $lastrelease&lt;br /&gt;
$ vim /home/project-web/racktables/htdocs/header.php&lt;br /&gt;
# test the &amp;quot;download&amp;quot; link at racktables.org to work&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=RackTablesAdminGuide&amp;diff=843</id>
		<title>RackTablesAdminGuide</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=RackTablesAdminGuide&amp;diff=843"/>
		<updated>2017-05-29T11:03:49Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: Live VLANs is obsolete, remove the section.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= User authentication =&lt;br /&gt;
Authentication is a way to tell that the remote user is who he or she purports to be. This can be done in three ways, with or without knowing the user's password, and is controlled through an option on the configuration page and via editing parameters in the &amp;quot;secret.php&amp;quot; file in the &amp;quot;inc&amp;quot; folder of your installation. Authentication does not automatically equate to access. All users other than the default &amp;quot;admin&amp;quot; user must be explicitly granted access, either individually, or via group permissions (see the &amp;quot;Permission configuration&amp;quot; section below), as specified under &amp;quot;Configuration: Permissions&amp;quot;. If you do not grant access, the user will not be permitted to view any pages. !RackTables traditional account locking works regardless of authentication sources, so one can always disable accounts one by one, if necessary.&lt;br /&gt;
&lt;br /&gt;
== Local authentication ==&lt;br /&gt;
This is the oldest (and default) method. Password hashes are stored in SQL database along with all other data. !RackTables checks the passwords itself.&lt;br /&gt;
&lt;br /&gt;
== LDAP authentication ==&lt;br /&gt;
Administrator user is the user with user_id 1, which by default maps to username &amp;quot;admin&amp;quot;. Administrator is '''always''' authenticated locally via the accounts database. Other user accounts are treated the same way by default, but this can be changed. See [[LDAP | here]] for detailed instructions on configuring RackTables for LDAP.&lt;br /&gt;
&lt;br /&gt;
==external authentication (since 0.17.0)==&lt;br /&gt;
In this mode !RackTables only makes sure, that the user is using a username to access. It is assumed, that Apache is configured to authenticate users, in its simplest case:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
AuthType basic&lt;br /&gt;
AuthName &amp;quot;beware ogres&amp;quot;&lt;br /&gt;
AuthUserFile /var/www/racktables/.htpasswd&lt;br /&gt;
Require valid-user&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After Apache configuration it is necessary to open !RackTables main page and make sure, that Apache really blocks unauthenticated users, whichever means it uses to validate them (htpasswd, LDAP, Kerberos and so on). Only after that the software configuration must be changed to always trust the username provided (because the web-server has already validated it). This is done in secret.php file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$user_auth_src = 'httpd';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===External Authentication with Shibboleth===&lt;br /&gt;
RackTables is compatible with [https://wiki.shibboleth.net/confluence/display/SHIB2/Home Shibboleth] authentication. It will require the installation of the Sibboleth Service Provider (SP) module for Apache, and for that to be configured to accept identity assertions from an Identity Provider (IDp), Federation, WAYF service or Directory Service.&lt;br /&gt;
&lt;br /&gt;
Detailed installation instructions for the Shibboleth SP are here: https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLinuxInstall&lt;br /&gt;
&lt;br /&gt;
Be sure that RackTables is properly installed and working with the local administrator account (&amp;lt;tt&amp;gt;admin&amp;lt;/tt&amp;gt;) before setting up Shibboleth.&lt;br /&gt;
&lt;br /&gt;
* Check that required packages are installed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ sudo apt-get install libapache2-mod-shib2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Configure &amp;lt;tt&amp;gt;shibd&amp;lt;/tt&amp;gt; for your Fedration or IDp (using the instructions above, or those provided by your IDp or Registry)&lt;br /&gt;
* Enable the &amp;lt;tt&amp;gt;shibd&amp;lt;/tt&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ sudo update-rc.d shibd enable&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Enable the Apache modules for Shibboleth and SSL:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ sudo a2enmod ssl&lt;br /&gt;
$ sudo a2enmod shib2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Create a SSL secured site for RackTables by createing &amp;lt;tt&amp;gt;/etc/apache2/sites-available/racktables-ssl&amp;lt;/tt&amp;gt; with the content:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:443&amp;gt;&lt;br /&gt;
        ServerAdmin webmaster@localhost&lt;br /&gt;
&lt;br /&gt;
        DocumentRoot /var/www&lt;br /&gt;
        SSLEngine on&lt;br /&gt;
        SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem&lt;br /&gt;
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;FilesMatch &amp;quot;\.(cgi|shtml|phtml|php)$&amp;quot;&amp;gt;&lt;br /&gt;
                SSLOptions +StdEnvVars&lt;br /&gt;
        &amp;lt;/FilesMatch&amp;gt;&lt;br /&gt;
        &amp;lt;Directory /usr/lib/cgi-bin&amp;gt;&lt;br /&gt;
                SSLOptions +StdEnvVars&lt;br /&gt;
        &amp;lt;/Directory&amp;gt;&lt;br /&gt;
        #   &amp;quot;force-response-1.0&amp;quot; for this.&lt;br /&gt;
        BrowserMatch &amp;quot;MSIE [2-6]&amp;quot; \&lt;br /&gt;
                nokeepalive ssl-unclean-shutdown \&lt;br /&gt;
                downgrade-1.0 force-response-1.0&lt;br /&gt;
        # MSIE 7 and newer should be able to use keepalive&lt;br /&gt;
        BrowserMatch &amp;quot;MSIE [17-9]&amp;quot; ssl-unclean-shutdown&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Location /racktables&amp;gt;&lt;br /&gt;
        AuthType shibboleth&lt;br /&gt;
        ShibRequireSession On&lt;br /&gt;
        require valid-user&lt;br /&gt;
    &amp;lt;/Location&amp;gt;&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Enable the RackTables site in Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ sudo a2ensite racktables-ssl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Restart Shibboleth and Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ sudo service shibd restart&lt;br /&gt;
$ sudo service apache2 restart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Edit &amp;lt;tt&amp;gt;/var/www/racktables/inc/secret.php&amp;lt;/tt&amp;gt; and add the following lines:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$user_auth_src = 'httpd';&lt;br /&gt;
$require_local_account = FALSE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Use your browser to navigate to your RackTables application using HTTPS (i.e. &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;https://your.server.com/rackspace&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;), you should now be presented with a Shibboleth login from your configured IDp, or Directory service. This should present you with a &amp;quot;Not Authorised&amp;quot; screen, copy the username that Shibboleth has handed RackTables.&lt;br /&gt;
* Comment out the lines added to &amp;lt;tt&amp;gt;/var/www/racktables/inc/secret.php&amp;lt;/tt&amp;gt; in the previous step:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# $user_auth_src = 'httpd';&lt;br /&gt;
# $require_local_account = FALSE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Return to RackSpace in your browser and reload the page, login as the local administrator (&amp;lt;tt&amp;gt;admin&amp;lt;/tt&amp;gt;)&lt;br /&gt;
* Create a tag to identify RackTables administrators e.g. 'RackTable Admin'&lt;br /&gt;
* Create a new user with the username you coped previously, and tag it with the administrator tag&lt;br /&gt;
* Edit the RackCode in the '''Configure''' -&amp;gt; '''Permissions''' tool to give your administrator tag access (this also gives non-admins viewing rights), Verify and Save:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$userid_1} or {$tab_default}&lt;br /&gt;
allow {RackTables Admin}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Log the local administrator out of the RackSpace web application&lt;br /&gt;
* Return to &amp;lt;tt&amp;gt;/var/www/racktables/inc/secret.php&amp;lt;/tt&amp;gt; and uncomment the lines:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$user_auth_src = 'httpd';&lt;br /&gt;
$require_local_account = FALSE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Return to RackSpace in your browser and reload the page. You should now be logged in as your Shibboleth user, and have administrator access.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' Replace the snakeoil certificates in the Apache configuration with certificates issued by a proper Certificate Authority, or use self signed certificates.&lt;br /&gt;
'''Note:''' The certificates in the Apache configuration are your Front End certificates presented to the user's browser, they are not the Back End certificates used by Shibboleth to encrypt back-channel communication between a SP and and IDp. '''Do not''' secure Apache with certificates found in &amp;lt;tt&amp;gt;/etc/shibboleth/&amp;lt;/tt&amp;gt;&lt;br /&gt;
'''Note:''' RackTables does not handle user names with special characters (e.g. '@'), which may prevent the use of &amp;lt;tt&amp;gt;$username=User@company.url.com&amp;lt;/tt&amp;gt; rules in RackCode.&lt;br /&gt;
&lt;br /&gt;
== logging out ==&lt;br /&gt;
When using external user authentication, special care should be taken to enable logging out of the system. According to HTTP spec, user agent (browser) among other factors uses &amp;quot;authentication realm&amp;quot; to decide, which username and password to send, when requesting each page. When the browser gets HTTP code 401 (&amp;quot;unauthorized&amp;quot;), it resets cached username/password pair for the realm, which is presented in the 401 reply. The &amp;quot;logout&amp;quot; link in !RackTables points to a special location, which always refuses to authenticate, and presents the same realm, which was used for initial authentication. That works fine for local and LDAP authentication, because in both cases the realm comes from the same source. For &amp;quot;httpd&amp;quot; authentication, however, this isn't always true (realms mismatch and cached password isn't reset), so a user often cannot log out by following &amp;quot;logout&amp;quot; link.&lt;br /&gt;
&lt;br /&gt;
To make things working again, it is necessary to make the realm, which Apache presents to the user, match the one, which is seen after following the &amp;quot;logout&amp;quot; link. Let's suppose, you see {{{Snake Oil RackTables access-500}}} after clicking &amp;quot;logout&amp;quot; (500 or other value is often added by Apache PHP module to make things more secure). The setting in htaccess file then should be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
AuthName &amp;quot;Snake Oil RackTables access-500&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After that it's easy to find, that logging out works again.&lt;br /&gt;
&lt;br /&gt;
== Resetting administrator password (and username) ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql&amp;gt; UPDATE UserAccount SET user_name = 'admin', user_password_hash = SHA1('mynewpassword') where user_id = 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Failover setup =&lt;br /&gt;
RackTables' purpose is to aid in troubleshooting, but sometimes the problem makes !RackTables system unavailable itself. A read-only backup replica of the server will help making through the accident faster. The diagram below explains necessary setup:&lt;br /&gt;
&lt;br /&gt;
[[Image:RackTables-failover.png]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Vendor sieve (since 0.17.0) =&lt;br /&gt;
The dictionary is made of many (hundreds) records, most of them being hardware models. The records are usually grouped by OEM brand or product line. It's common to stick with one particular brand and never purchase others. However, by default all records are shown in drop-down SELECT lists on &amp;quot;Properties&amp;quot; tab of each object. It is possible to hide certain option groups from being rendered by default. To do so, navigate to &amp;quot;Configuration&amp;quot;, then to &amp;quot;User interface&amp;quot; and switch to &amp;quot;Change&amp;quot;. There is an option (empty by default) named &amp;quot;Vendor sieve configuration&amp;quot;. Its format is:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Vendor1; Vendor2@X; Vendor3; Vendor4@Y; ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here VendorZ is what you see in bold in drop-down list (OPTGROUP, to be specific), and X/Y are optional numeric values of object type. If the object type isn't specified, related vendor is screened out from SELECTs for all object types. So, say, one has Sun (tm) tape libraries, but has no Sun (tm) servers. To hide &amp;quot;Sun&amp;quot; OPTGROUP from the list of server models and leave it on the list of tape libraries, he puts the following into option value:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Sun@4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This way the &amp;quot;HW type&amp;quot; SELECT gets considerably shorter. However, this feature is purely cosmetic: it doesn't remove any data from the SQL database, and doesn't change existing data. If there is a particular server marked as one of Sun (tm) boxes, the record is shown as before and upon going to &amp;quot;Properties&amp;quot; tab the whole &amp;quot;Sun&amp;quot; OPTGROUP is there (because the value is already set). It's possible to change from one Sun model to another in this situation. But once the box is changed to, say, a noname server, the &amp;quot;Sun&amp;quot; OPTGROUP is again gone, as requested by the sieve.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= AutoPorts =&lt;br /&gt;
When we create objects, we often (if not always) add some ports to them depending on the type and hardware model. Although RackTables can't yet add ports depending on hardware model (except via SNMP), it is (as of 0.14.12 release) able to automatically add ports upon object creation. This process is controlled by AutoPorts configuration string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;object type1 id&amp;gt;=&amp;lt;n1&amp;gt;*&amp;lt;port type1 id&amp;gt;*&amp;lt;format1&amp;gt;;&amp;lt;object type2 id&amp;gt;=&amp;lt;n2&amp;gt;*&amp;lt;port type2 id&amp;gt;*&amp;lt;format2&amp;gt;+&amp;lt;n3&amp;gt;*&amp;lt;port type3 id&amp;gt;*&amp;lt;format3&amp;gt;;&amp;lt;object type3 id&amp;gt;=&amp;lt;n4&amp;gt;*&amp;lt;port type4 id&amp;gt;*&amp;lt;format4&amp;gt;+&amp;lt;n5&amp;gt;*&amp;lt;port type5 id&amp;gt;*&amp;lt;format5&amp;gt;...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The default value is &amp;quot;{{{4 = 1*33*kvm + 2*24*eth%u}}}&amp;quot;, which makes each new server to have 2 Linux-style GigE ports and 1 KVM port. To make each new PDU bear 6 power plugs in addition to this, the AutoPorts config would be as follows: &amp;quot;&amp;lt;tt&amp;gt;4 = 1*33*kvm + 2*24*eth%u; 2 = 6*16*%02u&amp;lt;/tt&amp;gt;&amp;quot;. Exact key values can be found on the dictionary page in chapters PortType and RackObjectType (keys are rendered as mouse hints for each dictionary record). The format string is the one accepted by printf family functions and ports are indexed starting from 0 and upwards.&lt;br /&gt;
&lt;br /&gt;
The feature can also make its work later. When you browse an object, which has no ports yet, but could have (according to AutoPorts configuration), you see additional tab named &amp;quot;AutoPorts&amp;quot;. It will present you with its idea of which records could be added, and a button to do so. Once you add any ports to an object by automatic or manual mean, the tab will hide itself.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= KVM ports =&lt;br /&gt;
Since the 0.14.11 release RackTables has 2 distinct port types for physical console: &amp;quot;KVM (host)&amp;quot; and &amp;quot;KVM (console)&amp;quot;. Both types more describe the function, than an exact set of connectors. The former one emits video signal and accepts keyboard and/or mice events. The latter one accepts video signal and emits HID events. There's no specification, which mix of DVI, DB15, PS/2, DIN, USB and even Sun connectors is in question for each particular port, hence a variety of cabling and convertors is usually available. One usually just has &amp;quot;console&amp;quot; or &amp;quot;KVM switch&amp;quot; boxes with &amp;quot;KVM (console)&amp;quot; ports and server boxes with &amp;quot;KVM (host)&amp;quot; ports. Then connecting two servers or two pure consoles together wouldn't be possible, as it used to be before. &lt;br /&gt;
&lt;br /&gt;
A known drawback of the new approach is that all the ports (especially connected ones), which were created before the 0.14.11 release, now belong to &amp;quot;KVM (host)&amp;quot; type regardless of their actual function. There are ways to fix this depending on the number of such ports:&lt;br /&gt;
* Small: delete and re-add ports manually.&lt;br /&gt;
* Average: issue UPDATE SQL statement (backup you data first!).&lt;br /&gt;
* Really big: ask for help on the racktables-users mailing list. We might invent an automatic upgrading script.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Fixing the RackCode =&lt;br /&gt;
It's possible to write RackCode that locks the administrator out, so you can't fix things the usual way. In most situations you can always log in as administrator and fix things, but it's possible to break even this. The following SQL code restores administrator access:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
UPDATE Script SET script_text = NULL WHERE script_name = 'RackCodeCache';&lt;br /&gt;
UPDATE Script SET script_text = CONCAT('allow {$userid_1} ', script_text) WHERE script_name = 'RackCode';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Permission configuration =&lt;br /&gt;
&lt;br /&gt;
If you have issues understanding of how the permissions thing works, try to consider the permissions script as firewall rules. &lt;br /&gt;
&lt;br /&gt;
The current security context (a packet in firewall terms) is sequentially compared to each rule (statement in permissions script), top to bottom. If it matches, the action specified in rule takes place ('''allow''' or '''deny''') and the process stops.&lt;br /&gt;
&lt;br /&gt;
The current context is a set of tags originated from the currently logged-in user, an entity being viewed, and navigation data (current page and tab name). These could be either [[RackTablesUserGuide#Automatic_tags|automatic tags]] or user-assigned tags.&lt;br /&gt;
&lt;br /&gt;
So, the rules like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;allow {$userid_1}&lt;br /&gt;
allow {$username_jack}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unconditionally allow any context containing tags {$userid_1} or {$username_jack}, which makes those users the power- ones.&lt;br /&gt;
&lt;br /&gt;
But the rule&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;allow {$username_bill} and {$tab_default}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
allows anything to user named 'bill' when he is on 'default' tab. The default tab never contains controls to modify the DB, so the user has read-only permissions if there are no other allowing rules below.&lt;br /&gt;
&lt;br /&gt;
== More examples ==&lt;br /&gt;
&lt;br /&gt;
'''Note:''' The RackCode saved in the MySQL database contains hidden characters, such as carriage returns and new lines. RackCode edited using the &amp;lt;tt&amp;gt;mysql&amp;lt;/tt&amp;gt; command line client or using the [http://wb.mysql.com/ MySQL Workbench client] may not work.&lt;br /&gt;
&lt;br /&gt;
=== Administrator with unlimited access ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$userid_1} # admin is always UID 1 regardless of username&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin and power user ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$userid_1}&lt;br /&gt;
allow {$username_jack}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin and a group of power users ===&lt;br /&gt;
This approach requires creating a tag (Configuration -&amp;gt; Tag tree) named &amp;quot;power user&amp;quot;. Then each user account of the group must be tagged with this tag. After that the following code text would do the trick:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$userid_1}&lt;br /&gt;
allow {power user}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This way it's possible to run with the same text, granting and revoking permissions through attaching and removing role tags.&lt;br /&gt;
&lt;br /&gt;
=== Admin, a group of power users and a group of managers ===&lt;br /&gt;
RackTables pages have tabs. There is always at least one tab, the &amp;quot;default&amp;quot; one. Its name is always &amp;quot;default&amp;quot; and its contents doesn't allow changing things. It may show data about current items and/or links to other pages, but basically it's read-only. This way, having {$tab_default} on the current security context is equivalent to read-only access. Of course, this setup assumes a role tag for managers accounts.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$userid_1} or {power user}&lt;br /&gt;
allow {manager} and {$tab_default}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin can write, anyone can read ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$userid_1} or {$tab_default}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Permitting a user to view his own assets ===&lt;br /&gt;
Let's suppose you are setting up !RackTables for a colocation provider. A customer company, Snake Oil Web Services, wants to see what happened to their 20 servers you have accepted for colocation. They would also like to know which server must use which IP address for things to work. You create a user account, &amp;quot;snakeoil&amp;quot;, then create a tag &amp;quot;Snake Oil asset&amp;quot; and use it to tag each of that 20 servers. After that the following code permits them what they need and nothing else:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {$username_snakeoil} and {$tab_default} and {Snake Oil asset}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
There are things to mention, which aren't obvious:&lt;br /&gt;
# The user will be able to access only the pages, which display objects. As long as you use &amp;quot;Snake Oil asset&amp;quot; tag to mark servers only, it will get onto the context only in specific locations. IOW, the &amp;quot;snakeoil&amp;quot; account will not be authorized to view even the main page. For this specific case you can provide them with a list of 20 URLs.&lt;br /&gt;
# When you decide to use a more generic description for the tag, it becomes easy to achieve unexpected results with valid permission rules. Say, if you have a &amp;quot;Snake Oil&amp;quot; tag, some hardware and several user accounts, you may decide to tag not only servers, but accounts as well. This will effectively permit every Snake Oil account to access default tab of every page in the system, because of {Snake Oil} being always present on the security context. That's why the example uses {Snake Oil asset} to help things remain separated.&lt;br /&gt;
&lt;br /&gt;
=== Disable log delete for everyone ===&lt;br /&gt;
It is usually not desirable to remove log entries (especially accidentally), so you may set this as the ''first'' rule of permissions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
deny {$tab_log} and {$op_del}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Headquarters and branch offices ===&lt;br /&gt;
&lt;br /&gt;
Let's assume having the follwing tag tree:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
- assets&lt;br /&gt;
-- HQ assets&lt;br /&gt;
-- HQ managed&lt;br /&gt;
-- Arizona assets&lt;br /&gt;
-- Texas assets&lt;br /&gt;
- admins&lt;br /&gt;
-- HQ admins&lt;br /&gt;
-- Arizona admins&lt;br /&gt;
-- Texas admins&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then the following ruleset would allow each branch viewing all, but managing only own assets. Some local stuff could be placed under authority of the headquarters (this stuff would be tagged with 2 tags at a time).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allow {admins} and {assets} and {$tab_default}&lt;br /&gt;
deny not {HQ admins} and {HQ managed}&lt;br /&gt;
allow {Arizona admins} and {Arizona assets}&lt;br /&gt;
allow {Texas admins} and {Texas assets}&lt;br /&gt;
allow {HQ admins} and ({HQ assets} or {HQ managed})&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disabling Live PTR ===&lt;br /&gt;
&lt;br /&gt;
If you need to disable the Live PTR functionality, the following steps should be followed.&lt;br /&gt;
&lt;br /&gt;
1.  Create a Tag entry for each network you want to disable this functionality for. For example, say there are two networks: &amp;quot;Production&amp;quot; (10.10.0.0/24) and &amp;quot;Office&amp;quot; (10.3.0.0/24), and you only want to disable this functionality on the Production network. You would create a tag called &amp;quot;Production-LAN&amp;quot;, then associate the &amp;quot;Production&amp;quot; Network to the &amp;quot;Production-LAN&amp;quot; tag.&lt;br /&gt;
&lt;br /&gt;
2. Go to Configuration &amp;gt; Permissions.&lt;br /&gt;
&lt;br /&gt;
3. Add the below line to the top of the Permissions list and save. (Remember that if you add the deny rule below the allow ones, it won't take affect.)&lt;br /&gt;
  deny {$tab_liveptr} and {Production-LAN}&lt;br /&gt;
&lt;br /&gt;
4. Navigate to the Production network under IPv4space section. Click the network, and confirm that the &amp;quot;LivePTR&amp;quot; tab is not available.&lt;br /&gt;
&lt;br /&gt;
= Defining a new RackTables Object Type =&lt;br /&gt;
(contributed by Craig Hoffman)&lt;br /&gt;
&lt;br /&gt;
[[File:RackTables_CircuitExample.PNG]]&lt;br /&gt;
&lt;br /&gt;
Perhaps you have a piece of equipment that is currently not listed in the default list of RackTables objects. This could be something physical, like an environmental sensor. Or, perhaps, you want to extend RackTables to support something more virtual, like network circuits. (I wanted to have my CircuitDB info in one location. To this end, I created a Rack Row called &amp;quot;Virtual&amp;quot;, and a 47U rack called &amp;quot;Circuits&amp;quot;. I just stick each of my circuits in there so they don't show up as &amp;quot;Unmounted&amp;quot;.)&lt;br /&gt;
&lt;br /&gt;
This process has been discussed from time to time on the mailing list.  Here's how you do it:&lt;br /&gt;
&lt;br /&gt;
* From the Main Menu, select Configuration, '''Dictionary''', and then select the dictionary '''&amp;quot;ObjectType&amp;quot;'''.&lt;br /&gt;
* Select &amp;quot;Edit&amp;quot; and add the new object type that you wish to create.  I extended RackTables to support circuits, so I typed in &amp;quot;Circuit&amp;quot;.&lt;br /&gt;
Now you need to create a dictionary entry that will be used to tie attributes to.&lt;br /&gt;
* From the Main Menu, select Configuration, '''Dictionary''', and then select '''&amp;quot;Manage Chapters&amp;quot;''' at the top. Add a new entry (Circuit ID as my example).&lt;br /&gt;
You can now create the attributes that will be assigned to your new dictionary entry.  For a circuit, things like circuit ID#, carrier, AS#, contact name, etc.  You can always go back and add more attributes as needed.&lt;br /&gt;
* From the Main Menu, select Configuration, '''Attributes''', and '''Edit Attributes'''.  Create each attribute that you need.  Take care in the '''type''' of attribute.  &amp;quot;String&amp;quot; is probably what you want, when in doubt.  &lt;br /&gt;
You're just about done.  It's time to assign those new attributes to the dictionary entry.  &lt;br /&gt;
* From the Main Menu, select Configuration, '''Attributes''', and '''Edit Map'''.  Select the new attributes and apply it to the new dictionary entry.  You probably do not need to do anything with the third dropdown box, &amp;quot;dictionary chapter...&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Now you should be able to go back to the Main Menu, and create some new objects with your new Object Type.&lt;br /&gt;
&lt;br /&gt;
= Adding new port types =&lt;br /&gt;
See [[adding new port types]] page to activate hidden port types or to create new ones.&lt;br /&gt;
&lt;br /&gt;
= Configuring CLI gateways to network devices =&lt;br /&gt;
&lt;br /&gt;
RackTables has some features which need to have CLI access to network devices. Those features are:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
 &amp;lt;li&amp;gt;Live ports (FDB, interface status)&lt;br /&gt;
 &amp;lt;li&amp;gt;Live neighbors (CDP, LLDP)&lt;br /&gt;
 &amp;lt;li&amp;gt;802.1Q management&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RackTables is able to interact with devices through pure TCP terminal session (netcat), telnet or ssh.&lt;br /&gt;
The administrator is responsible for configuring the protocol, connection options and authentication credentials used to connect to particular devices.&lt;br /&gt;
&lt;br /&gt;
To do so, administrator must include the function terminal_settings into secret.php.&lt;br /&gt;
&lt;br /&gt;
The minimal example of such function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function terminal_settings ($cell, $params)&lt;br /&gt;
{&lt;br /&gt;
        $params[0]['username'] = 'login';&lt;br /&gt;
        $params[0]['password'] = 'password';&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The defaults of $params[0] array are pretty reasonable, and the only keys which are absolutely needed to be set are 'usermame', 'password' (for netcat or telnet), and 'sudo_user'/'identity_file' (for ssh).&lt;br /&gt;
&lt;br /&gt;
More comprehensive example might look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$typeid_4} or {Juniper}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['protocol'] = 'ssh';&lt;br /&gt;
                $params[0]['proto'] = '4';&lt;br /&gt;
                $params[0]['sudo_user'] = 'racktables';&lt;br /&gt;
                $params[0]['connect_timeout'] = 2;&lt;br /&gt;
        }&lt;br /&gt;
        elseif (considerGivenConstraint ($cell, '{Switch}'))&lt;br /&gt;
        {&lt;br /&gt;
                $params[0]['username'] = 'racktables';&lt;br /&gt;
                $params[0]['password'] = 'password';&lt;br /&gt;
                $params[0]['timeout'] = 60;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (considerGivenConstraint ($cell, '{$attr_4_251}')) // IOS 12.1&lt;br /&gt;
                $params[0]['protocol'] = 'telnet';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The possible keys in $params[0] can be determined by calling print_r ($params[0]).&lt;br /&gt;
&lt;br /&gt;
To make the remote gateways features work for a particular device, the following conditions should be met:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
 &amp;lt;li&amp;gt;FQDN field of object is set to DNS name or IP address of device&lt;br /&gt;
 &amp;lt;li&amp;gt;SW type field of object is set and supported by particular gateway feature&lt;br /&gt;
 &amp;lt;li&amp;gt;terminal_settings function defined in secret.php&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= File uploads =&lt;br /&gt;
There are several settings in php.ini which you may need to modify:&lt;br /&gt;
;file_uploads&lt;br /&gt;
: needs to be On&lt;br /&gt;
;upload_max_filesize&lt;br /&gt;
: max size for uploaded files&lt;br /&gt;
;post_max_size&lt;br /&gt;
: max size of all form data submitted via POST (including files)&lt;br /&gt;
The following MySQL server parameter may be relevant too:&lt;br /&gt;
;max_allowed_packet&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
Adding the following parameter to &amp;quot;secret.php&amp;quot; will enable debugging, which is helpful in tracking down permissions errors (for example).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$debug_mode= 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you disable this by commenting it out or deleting it when you're done debugging! :)&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=841</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=Main_Page&amp;diff=841"/>
		<updated>2017-05-17T19:26:19Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: use the 2017Q2 roadmap image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:RackTables-development-roadmap-2017Q2.png|535px|thumb|right]]&lt;br /&gt;
* for RackTables users&lt;br /&gt;
** The [https://github.com/RackTables/racktables/blob/maintenance-0.20.x/README.md README file] now replaces the old Installation HOWTO.&lt;br /&gt;
** [[RackTablesAdminGuide | Administrator's guide]]&lt;br /&gt;
** [[RackTablesUserGuide | User's guide]]&lt;br /&gt;
** [[8021Q | 802.1Q VLAN management in RackTables]] (still work in progress)&lt;br /&gt;
** [[FAQ]]&lt;br /&gt;
** [[LDAP | LDAP authentication]]&lt;br /&gt;
** [[SAML | SAML authentication]]&lt;br /&gt;
* for RackTables hackers&lt;br /&gt;
** [[SourceCode | Working with RackTables source code]]&lt;br /&gt;
** [[RackTablesDevelGuide | Developer's guide]]&lt;br /&gt;
** [[FeatureWishlist | Feature wishlist]]&lt;br /&gt;
** [[Gateways | Extending RackTables with gateways]]&lt;br /&gt;
* for development team&lt;br /&gt;
** [[NewRelease | Checklist and procedures to make a new release]]&lt;br /&gt;
** [[ProjectInfrastructure | Project infrastructure]]&lt;br /&gt;
** [[Roadmap]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To use this site over an SSL connection, visit [https://wiki.racktables.org/ https://wiki.racktables.org/]&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=File:RackTables-development-roadmap-2017Q2.png&amp;diff=839</id>
		<title>File:RackTables-development-roadmap-2017Q2.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=File:RackTables-development-roadmap-2017Q2.png&amp;diff=839"/>
		<updated>2017-05-17T14:53:53Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=837</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=837"/>
		<updated>2017-05-12T13:21:51Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* updating the main web-site */ add a comment&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
First make sure that all necessary changes are already committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and that no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure that &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
phpunit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* Log into SF shell service:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat ~/.ssh/config &lt;br /&gt;
Host shell.sourceforge.net&lt;br /&gt;
	User YOUR_SF_USERNAME,racktables&lt;br /&gt;
$ ssh -t shell.sourceforge.net create&lt;br /&gt;
# Sometimes the above command doesn't drop you into the newly created session, in this case&lt;br /&gt;
# an additional &amp;quot;ssh shell.sourceforge.net&amp;quot; will put you through.&lt;br /&gt;
# upon logging in update $lastrelease&lt;br /&gt;
$ vim /home/project-web/racktables/htdocs/header.php&lt;br /&gt;
# test the &amp;quot;download&amp;quot; link at racktables.org to work&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=835</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=835"/>
		<updated>2017-05-12T13:11:37Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* producing and publishing deliverables */ add minor clarifications&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
First make sure that all necessary changes are already committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and that no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure that &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
phpunit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer). Give the uploaded files a couple minutes to make it through the system to the user-visible download area.&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded release files as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* Log into SF shell service:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat ~/.ssh/config &lt;br /&gt;
Host shell.sourceforge.net&lt;br /&gt;
	User YOUR_SF_USERNAME,racktables&lt;br /&gt;
$ ssh -t shell.sourceforge.net create&lt;br /&gt;
# upon logging in update $lastrelease&lt;br /&gt;
$ vim /home/project-web/racktables/htdocs/header.php&lt;br /&gt;
# test the &amp;quot;download&amp;quot; link at racktables.org to work&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=833</id>
		<title>NewRelease</title>
		<link rel="alternate" type="text/html" href="https://wiki.racktables.org/index.php?title=NewRelease&amp;diff=833"/>
		<updated>2017-05-11T16:44:34Z</updated>

		<summary type="html">&lt;p&gt;Infrastation: /* part 1 */ specify a missing argument&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== pre-release checklist ==&lt;br /&gt;
=== part 1 ===&lt;br /&gt;
First make sure that all necessary changes are already committed into the repository:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you have the right branch checked out and that no pending changes are in the working copy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the new release should have any release notes, make sure these appear both in &amp;lt;tt&amp;gt;wwwroot/inc/upgrade.php&amp;lt;/tt&amp;gt; and in &amp;lt;tt&amp;gt;README&amp;lt;/tt&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Update &amp;lt;tt&amp;gt;ChangeLog&amp;lt;/tt&amp;gt; file to have the current date on the to-be version line.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure that &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; has the to-be version listed in &amp;lt;tt&amp;gt;$versionhistory&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt;. It is normal to accumulate updates in &amp;lt;tt&amp;gt;getUpgradeBatch()&amp;lt;/tt&amp;gt; long before the release, this way on the release day you will have nothing to do in &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt;. 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.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Bump up &amp;lt;tt&amp;gt;CODE_VERSION&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;wwwroot/inc/config.php&amp;lt;/tt&amp;gt;. DON'T do this unless you really intend to make a release right now.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Produce a commit with all changes pending thus far:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add ChangeLog&lt;br /&gt;
[...]&lt;br /&gt;
git add wwwroot/inc/config.php&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Check the source code out from the same branch to a '''clean location''' to test it.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone git@github.com:RackTables/racktables.git&lt;br /&gt;
cd racktables&lt;br /&gt;
git checkout master # or maintenance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The application must be able to install itself using its own installer (only some of the involved code is in the Travis CI script). Once this is found working, dump the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-fresh.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Demo data must be loadable without any errors (this is likely to be OK since this check is now in the Travis CI script):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql racktables_db &amp;lt; scripts/init-sample-racks.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;The release being tested must detect and upgrade a database from the previous release(s) correctly. Load the database with one of the previous releases data, then upgrade it with the current upgrader, then dump the DB. Now compare to the previous dump, there must be no meaningful differences.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reload using the previous release&lt;br /&gt;
/path/to/racktables-contribs/demo.racktables.org/demoreload.sh X.Y.z racktables_db no&lt;br /&gt;
# (make through upgrade.php)&lt;br /&gt;
mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; ~/tmp/dump-upgraded.sql&lt;br /&gt;
diff -u ~/tmp/dump-fresh.sql ~/tmp/dump-upgraded.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 2 (unit testing) ===&lt;br /&gt;
'''''The commands below need some proofreading because the test data files in the source code tree still use the &amp;lt;tt&amp;gt;racktables&amp;lt;/tt&amp;gt; MySQL username, which I had for this reason used for the 0.20.12 release. Whatever is the agreed username, it probably should be consistent with what is in the SQL dumps. -- Denis'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Setup a fresh database and grant access to the 'racktables_user' user.  This will be used to create a sample data file that unit tests can utilize.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE IF EXISTS racktables_unittest;&lt;br /&gt;
CREATE DATABASE racktables_unittest CHARACTER SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
GRANT ALL PRIVILEGES ON racktables_unittest.* TO racktables_user@localhost IDENTIFIED BY 'MY_SECRET_PASSWORD';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Use the web interface to complete the installation. When prompted to set the admin password, enter 'admin'.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Load the sample data.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u racktables_user -p racktables_unittest &amp;lt; scripts/init-sample-racks.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Export the database to the tests/data dir.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqldump --skip-comments -u racktables_user -p racktables_unittest &amp;gt; tests/data/$CODE_VERSION.sql&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Run the unit tests. Fix any failures.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd tests&lt;br /&gt;
phpunit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;If all is OK, commit the new SQL dump in the &amp;lt;tt&amp;gt;tests/data&amp;lt;/tt&amp;gt; directory.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== part 3 (finalization) ===&lt;br /&gt;
&amp;lt;li&amp;gt;Test the source by hand as much as you find possible.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Look into the error log of the server you used for the tests. There shouldn't be any error/warning messages.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;Do not proceed further until everything is fixed consistently.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== the release itself ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag RackTables-X.Y.z&lt;br /&gt;
git push origin RackTables-X.Y.z&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== rolling out ==&lt;br /&gt;
=== producing and publishing deliverables ===&lt;br /&gt;
* Make two archives:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export TAG='RackTables-X.Y.z'&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.tar.gz $TAG&lt;br /&gt;
git archive --prefix=$TAG/ -o $TAG.zip $TAG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Upload the tarball and zipfile to SF FRS ([https://sourceforge.net/projects/racktables/files/ from browser] or by [https://sourceforge.net/p/forge/documentation/Release%20Files%20for%20Download/ other means] if you prefer).&lt;br /&gt;
* Open [https://sourceforge.net/projects/racktables/files files section] and set the newly uploaded tar.gz as &amp;quot;current&amp;quot;.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release tar.gz.&lt;br /&gt;
** Unset all &amp;quot;default download for&amp;quot; checkboxes.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** Open properties form (round &amp;quot;(i)&amp;quot; sign) of the previous release zip.&lt;br /&gt;
** Unset Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Unset &amp;quot;download button&amp;quot; text.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest tarball properties form, set all &amp;quot;default download for&amp;quot; checkboxes except Windows.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** In the latest zipfile properties form, set Windows &amp;quot;default download for&amp;quot; checkbox.&lt;br /&gt;
** Set &amp;quot;download button&amp;quot; text to &amp;quot;latest stable&amp;quot;.&lt;br /&gt;
** Save.&lt;br /&gt;
** Hover the big green &amp;quot;download&amp;quot; button and watch the mouse hint featuring the right version number (sometimes it takes several minutes to see the update).&lt;br /&gt;
** '''Tip:''' to update the banner text below the file list of the SF's &amp;quot;Files&amp;quot; section open a terminal connection to SF shell and edit the file &amp;lt;tt&amp;gt;/home/frs/project/racktables/README.md&amp;lt;/tt&amp;gt; (once done, let the SF 5-10 minutes to start using the updated revision).&lt;br /&gt;
&lt;br /&gt;
=== updating Mantis ===&lt;br /&gt;
* In MantisBT mark the released version as released (Manage, Manage Projects, [https://bugs.racktables.org/manage_proj_edit_page.php?project_id=1 RackTables], Versions). Add the next version with the release date several months into the future.&lt;br /&gt;
&lt;br /&gt;
=== updating the main web-site ===&lt;br /&gt;
* Log into SF shell service:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat ~/.ssh/config &lt;br /&gt;
Host shell.sourceforge.net&lt;br /&gt;
	User YOUR_SF_USERNAME,racktables&lt;br /&gt;
$ ssh -t shell.sourceforge.net create&lt;br /&gt;
# upon logging in update $lastrelease&lt;br /&gt;
$ vim /home/project-web/racktables/htdocs/header.php&lt;br /&gt;
# test the &amp;quot;download&amp;quot; link at racktables.org to work&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== updating other news feeds ===&lt;br /&gt;
* Send a letter to the racktables-users list (even more so after Freshmeat/Freecode is gone).&lt;br /&gt;
* Update IRC channel topic&lt;br /&gt;
&lt;br /&gt;
=== updating demo ===&lt;br /&gt;
* Produce a helper SQL file for the current release in [https://github.com/RackTables/racktables-contribs racktables-contribs].&lt;br /&gt;
** Copy the previous &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; to current &amp;lt;tt&amp;gt;init-full-X.Y.z.sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; the new file.&lt;br /&gt;
** Take a clean RackTables instance of the current release (either use demoreload on the previous release or install manually).&lt;br /&gt;
** Make an SQL dump of this new installation like this: &amp;lt;tt&amp;gt;mysqldump --extended-insert=FALSE --order-by-primary racktables_db &amp;gt; init-full-X.Y.z.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
** Edit permissions like [https://github.com/RackTables/racktables-contribs/commit/0354001ce16e937cfc5ae555fe73dc3c351a6556 this].&lt;br /&gt;
** &amp;lt;tt&amp;gt;git add -i&amp;lt;/tt&amp;gt; the new file again (note interactive add). Use patch mode to add relevant chunks. Quit.&lt;br /&gt;
** &amp;lt;tt&amp;gt;git commit; git push&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Update demo.racktables.org to the current release (&amp;lt;tt&amp;gt;$demorelease&amp;lt;/tt&amp;gt; will need an update). Below is a sample CLI session of updating the demo to 0.20.3:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/racktables&lt;br /&gt;
git fetch&lt;br /&gt;
git checkout RackTables-0.20.3&lt;br /&gt;
cd&lt;br /&gt;
ln -s racktables RackTables-0.20.3&lt;br /&gt;
# the symlink to ~/secret.php is already in wwwroot/inc/&lt;br /&gt;
# the symlink to ~/google_analytics.php is already in plugins/&lt;br /&gt;
cd ~/racktables-contribs/&lt;br /&gt;
git pull&lt;br /&gt;
cd&lt;br /&gt;
~/racktables-contribs/demo.racktables.org/demoreload.sh 0.20.3 rackdemo&lt;br /&gt;
crontab -e&lt;br /&gt;
# ../../racktables/wwwroot/index.php is already linked as www/demo/index.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Infrastation</name></author>
		
	</entry>
</feed>