For any company managing a large number of Iguana instances, producing a centrally-managed picture of site configurations provides a great deal of value. The following sample script would be a helpful part of that solution.
- This script is written for Mac OS X and Linux, but you could easily port it into a Windows environment.
- This script is deployed via a ‘From Translator’ component configured to run every 5 minutes or so. We suggest making this channel (and script) part of your standard Iguana installation practice.
Here is a copy-paste version of the main script:
mon = require "monitor_config" function main() mon.checkRepo() end
Here is a copy-paste version of the ‘monitor_config’ module that you will need to add to your shared library:
mon = {}
-- This script should work on Mac OS X or Linux.
-- It makes use of these commands:
-- rm, cp, tar
-- To make the same script work on Windows you'd need to change those commands
-- to use equivalent tools - possible zip on windows.
-- The script:
-- Unpacks the fossil repository if the modification time has changed.
-- Renames the directory GUIDs of the translator projects to use a combination of channel names + translator type prefix.
-- Gets the other and shared directories
-- and the IguanaConfiguration.xml file
-- and makes a tarball
-- For this back up to be useful you'd need to then tranfer the tarball to a central server say by HTTPS and then do something
-- useful - like unpack it into a central repository which can be used to track changes.
local CONFIG_FILE = "backup.json"
local SCRATCH_DIR = "scratch"
local PACKAGE_DIR = "package"
local TAR_BALL = 'package.tgz'
local Config={
lasttime=0
}
function mon.checkRepo()
local ConfigFile = iguana.workingDir()..'vcs_repo.sqlite'
local ConfigLastModified = os.fs.stat(ConfigFile).mtime
trace(ConfigLastModified)
trace(Config.lasttime)
if Config.lasttime < ConfigLastModified then
iguana.logInfo("Configuration changed making "..TAR_BALL)
mon.backupRepo()
if os.fs.access(TAR_BALL) then
iguana.logInfo(TAR_BALL..' successfully created');
iguana.setChannelStatus{text='Backed up at '..os.ts.date('%c',os.ts.gmtime())}
else
iguana.logError('Unable to create '..TAR_BALL);
iguana.setChannelStatus{color='red'}
end
Config.lasttime = ConfigLastModified
if not iguana.isTest() then
mon.saveLastBackup()
end
end
end
function MkEmptyDir(Dir)
if not os.fs.access(Dir) then
os.fs.mkdir(Dir)
return 'Created Dir'
end
os.execute('cd '..Dir..' && rm -rf *')
return 'Cleaned Dir'
end
function Rename(Guid, Name)
local From = SCRATCH_DIR..'/'..Guid
local To = PACKAGE_DIR..'/'..Name
trace(From)
trace(To)
os.rename(From, To)
end
-- This routine copies the
function mon.backupRepo()
MkEmptyDir(SCRATCH_DIR)
MkEmptyDir(PACKAGE_DIR)
if os.fs.access(TAR_BALL) then
os.remove(TAR_BALL)
end
local ConfigFile = iguana.workingDir()..'vcs_repo.sqlite'
os.execute('cp '..ConfigFile..' '..SCRATCH_DIR..'/')
os.execute('cd '..SCRATCH_DIR..' && ../fossil open vcs_repo.sqlite')
os.remove(SCRATCH_DIR..'/_FOSSIL_')
mon.transform()
Rename('shared', 'shared')
Rename('other', 'other')
os.execute('cp IguanaConfiguration.xml '..PACKAGE_DIR)
os.execute('tar -zcf '..TAR_BALL..' '..PACKAGE_DIR)
return false
end
-- this routine takes the GUID based file structure and turns
-- directories based on channel names with postfixes describing the
-- channel type.
function mon.transform()
local F = io.open('IguanaConfiguration.xml', 'r')
local IC = xml.parse{data=F:read('*a')}.iguana_config.channel_config
F:close()
for i=1, IC:childCount("channel") do
local C = IC:child("channel", i)
if C.from_mapper then
Rename(C.from_mapper.guid, C.name..'_From')
end
if C.message_filter and C.message_filter.translator_guid then
Rename(C.message_filter.translator_guid, C.name..'_Filter')
end
if C.to_mapper then
Rename(C.to_mapper.guid, C.name..'_To')
end
if C.from_llp_listener and C.from_llp_listener.ack_script then
Rename(C.from_llp_listener.ack_script, C.name.."_Ack")
end
if C.from_http and C.from_http.guid then
Rename(C.from_http.guid, C.name.."_Http")
end
end
end
function mon.saveLastBackup()
local Json = json.serialize{data=Config}
local F = io.open(CONFIG_FILE, "w+")
F:write(Json)
F:close()
end
function mon.loadLastBackup()
if not os.fs.access(CONFIG_FILE) then
return
end
local F = io.open(CONFIG_FILE, "r")
local File = F:read("*a")
Config = json.parse{data=File}
end
-- Load the configuration file when the script starts up
mon.loadLastBackup()
return mon
At a high level, here is how the script works:
- It keeps a configuration file called ‘backup.json’ that notes when the last backup operation was run.
- If the vcs_repo.sqlite file’s “last modified” time has changed since the last backup, then this script constructs a tarball called ‘package.tgz’. This tarball includes ‘IguanaConfiguration.xml’ and the most recent Lua/VMD files (pulled from the Fossil repository).
- It updates the channel’s status tooltip and logs the backup activity.
You may also notice that this script makes use of the following:
- cp to copy files
- rm to clean out directories
- tar to make a tarball called ‘package.tgz’
These could be replaced with copy, remove, and a command line zip utility in Windows. You could also install CYGNUS tools if you are working in a Windows environment.
For this solution to be useful, you’ll need to do something with the resulting tarball file. It would be quite simple to set up an Iguana instance to host a web service, then push the tarball to this service via HTTP. This Iguana instance would then unpack the archive into an enterprise source code repository. You could choose to do this slightly differently, depending on whether you want the original ‘vcs_repo.sqlite’ file or not. You could also tweak things to retrieve the Chameleon VMD files used by classic Iguana channels (if you use that functionality).
Please note that this script translates the normal structure of the repository, which uses GUIDs for each Translator. It renames each directory based on the name of the channel the Translator script belongs to, then adds a different extension depending on the Translator type. This makes it much easier to read directory structures:

As you can see, the human-readable directory names make it much easier to navigate.
When putting this script into production, we also recommend that you add some error checking.
If you have any questions or suggestions, please let me know.
Eliot Muir,
CEO iNTERFACEWARE