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