Forum OpenACS Development: Re: Ideas for easier installation of OpenACS

Collapse
Posted by Tom Jackson on

The current situation is that to add a server, you first add a line to a central file:

ns_section ns/servers
 ns_param server1 "Original Server"
 ns_param myserver "New Server"

# then a config section
ns_section ns/server/myserver
 ns_pram ....

The same thing is repeated for each thing you add: ns_sock modules, database pools, etc.

With everything in one file, any typo (this is a tcl script), or even certain other errors can cause the entire startup to abort and loop over and over. So adding anything to a working file is error-prone, with the potential to bring down everything using the file.

Several years ago I started experimenting with a new idea, which was to have each virtual server (server1, myserver) keep their configuration in their own directory, next to pageroot, and the modules directory:

servers/
        server1/
                pages/
                modules/
                config/
                       server.tcl
                       nslog.tcl
                       nscgi.tcl
                       nscp.tcl
                       threadpool-default.tcl
                       ...
                       

When I add a new server, I simply copy this directory and make a few changes where needed, maybe comment out a few modules I don't need. But then I'm still stuck editing main server config file so that it knows about the new server.

This is okay, because I have scripts which source the above files, and if there is an error in sourcing, the server is not added to the system, the others come up normally. The scripts also log what didn't work so the error is easier to track down.

But I also did work on the main level scripts so that it was easier to determine what the configuration was without hunting through a bunch of files. This works but is awkward and still requires editing the main file a little, but somewhat more safely.

The next 'advance' was to include a few helper procs, so that my top-level file starts like this:

# Set Main Server Variables:
namespace eval ::config {

    variable Home      /web/nsd45
    variable ConfigDir [file join $Home config]

    source [file join $ConfigDir lib config-procs.tcl]

}

# Override default values in config:
namespace eval ::config {
    variable Debug 1
    variable StartupScript [file join $Home [info script]]
    variable ErrorFile "/tmp/errors-from-[string map {/ _} $StartupScript].txt"
}

# Virtual Server / Socket Config:
namespace eval ::config {

    addServer jnm "Junom Server"
    addServer tutos "Tutos tWSDL"
    addServer server1 "Simple Server"
    addServer openacs "OpenACS" "/web/openacs-5.2.3/config/server.tcl"
    addServer oacs "OpenACS 5.3.2" "/web/openacs-5-3-2/config/server.tcl"
    addSocket nssock1 192.168.1.102 8001
    addSocket nssock2 192.168.1.102 8005
    addSocket nssock3 192.168.1.102 8000
    addSocket nssock4 192.168.1.102 8002
    addSocket nssock5 192.168.1.102 8003
    mapHostsToSocket jnm nssock1 {maria junom}
    mapHostsToSocket tutos nssock2 tutos
    mapHostsToSocket server1 nssock3
    mapHostsToSocket openacs nssock4
    mapHostsToSocket oacs nssock5
    setDefaultServerForSock jnm nssock1
    setDefaultServerForSock tutos nssock2
    setDefaultServerForSock server1 nssock3
    setDefaultServerForSock openacs nssock4
    setDefaultServerForSock oacs nssock5
}

There are several catch-22s in server startup that I avoid. One is the defaultServer for a sock. If the default server for a sock doesn't come up, the entire process aborts! For some reason it is okay to have a sock that isn't used (AOLserver removes it automatically), but not having a default server for a sock is fatal.

The above script still requires creating files in correct places, but it is easier to put in variations. Notice that I have regular old AOLserver virtual servers, and two OpenACS installations. OpenACS uses a different path structure, so the default of where server.tcl exists, is overridden by the direct path. It is very hard to use a single file (with typical variables set at the top) in this situation, and most users would be left struggling to make a copy of what they need with a few small changes, put that in a new file and try again. Then all the upstream scripts have to change, developers no longer know where to even find the configuration file for a given server. Of course once they find it, it is just like reading a tcl script and trying to imagine the output. When your server is down, this is not fun. If everyone's server is down, you have a real problem.

Anyway, even the above setup is confusing to me and I wrote the code. Fortunately, the script does print out what it is trying to do and what fails to work, so I can always look at the server.log to track down what isn't working, that is, I can track down what I did wrong. Also, the real issue is that this process cannot be automated. You cannot automate adding a line to the middle of a generic file. You can patch source code, but you can't patch configuration files. This is a big gap in AOLserver, where installation and configuration are of secondary importance to code development, or maybe just not on the radar. It is typical to write C code which checks for a config section, and if it is missing, uses a default. There is no indication to the module user that these configurations settings even exist.

For example, recently I added a Ns_Log statement to the config lookup code. Now, every call to look for something stored in the server config structure is logged (even Tcl modules). Unfortunately I don't get information on who is calling the config API, but I do get a time ordered list of what is looked for. It is amazing how many undocumented config params exist in AOLserver, and a lot of the ones being passed around in current config files are no longer used.

File based signaling of configuration could accomplish a simplification and rationalization of the above ad-hoc process and it would provide a development framework for integration of code, installation and configuration (for instance, nobody would be happy if there wasn't a 'make install' target for modules, and nobody would be happy if you didn't have revision control over your source code. If module configuration becomes part of the source code, you get this benefit with very little extra work, and you can automate it more easily.

The details are not completely obvious, but the main principle should be simplicity in design and operation. The end result of sourcing a config is a static set of data. All of this is added _before_ AOLserver starts to act on the information. All the information is added, and errors potentially occur, before AOLserver has a log file to log errors! So config processing should be verbose and explicit, but the rules for processing should be generic enough, and few enough that a simple script could produce a static config that won't crash the startup and would report any config errors and the result: db driver not found, server config skipped because 'down' exists, ip address is invalid for sock. Probably the script, if run from nstclsh would produce logging information, diagnostics, and output a static script containing only ns_section and ns_param calls. Then you could test configurations independent of the server startup, or if you had a config problem, you could run the script on the problem area so you could ask for help in diagnosis.