Article ID: 2824, created on Oct 29, 2007, last review on May 5, 2014

  • Applies to:
  • Pro Control Panel Linux


View Knowledge
Knowledge ID 2244
Product : Ensim Pro for Linux
Version : 3.5
Topic : Hotfix

Customizing virtual domain services (Apache directives, configs, etc.)

Customizing virtual domain services (Apache directives, configs, etc.)



Please look over this text in conjunction with the files /usr/share/doc/webppliance-[apache|proftpd|cgi|ssi|...]- / on the WEBppliance. This is a somewhat complicated topic, so please read carefully.

Let's begin with an example. A couple of customers have expressed an interest in providing a URL /webmail that will direct the user to the squirrel mail login page in preference to Ensim's default link of /squirrelmail. What we essentially want to do is add a directive to the apache virtual hosting containers. To make this link available to all domains, the following code would be placed into a file /usr/lib/python2.1/site-packages/vh3/custom/

import virthost
def add_custom_directives(site, newconf, oldconf, cust):
return "Alias /webmail %s/var/www/squirrelmail/" % \

Now, a real quick intro to python:

import is similar to perl's use statement, in that it makes symbols from available for use. e.g. I can do the following:

import foo

which is identical to perl's

use foo;

def (, , ...):
        # code
        # more code

is how you define functions in python. With Python, there are no braces to surround code, so white-space indentation is important. That is how you define blocks of code. But for the example we are looking at, that isn't really important. Python dictionary references are simple too:

foo = dict[ ] (e.g. foo = dict['bar'])

Finally, string operations. As in C, python uses % for interpolation. For instance, "%s" indicates that you want to interpolate the value as a string. To pass the values that you want interpolated, you include them at the end of the string in a list, separated from the string by a %. e.g. "This %s a sample %s" % ('is', 'string') would yield "This is a sample string". The last bit you'll probably need is string concatenization, which is done with the '+' operator; e.g. "foo" + "bar" yields "foobar."

All right, end of python tutorial. For a more thorough introduction see This is a nice, short introduction to python, and will cover about all anyone who needs to do customizations will ever have to know (and they can skip the parts on exceptions and classes).

Now let's look at the specific example:

import virthost
def add_custom_directives(site, newconf, oldconf, cust):
        return "Alias /webmail %s/var/www/squirrelmail/" % \

The first line, "import virthost", makes accessible functions defined in the virthost module. The only one I can foresee people wanting to use right now is virthost.domainfs_path(site), which returns the path to the filesystem for the given site.

The second line begins the definition of the function add_custom_directives(). This is documented more thoroughly in /usr/share/doc/webppliance-apache- /; whatever string it returns is appended to the end of the apache virtualhosting container for a site. Other functions are available as well; for example, there are hooks for adding global scope directives per site, as well as a hook that will allow you to define the entire container yourself. See the files for each of the services to see what they offer.

This function takes four arguments. The first is a string identifier that tells us the anonymous name for what site we are dealing with. All we should really care about this value is that it is a string. Its form is 'site ', where is a unique integer for each site on a box.

The second argument is a dictionary of dictionaries, or to put it in perl terms, a hash of hashes. This hash of hashes contains the configuration information for the site, broken up by individual backend modules. For instance, newconf['apache'] is the site configuration information for apache, and includes whether apache is enabled and what the webserver name is. To access the enabled flag, you would use newconf['apache']['enabled']; likewise, to access the webserver name, you would use newconf['apache']['webserver']. To see what all the hashes might look like, look at the contents of any /home/virtual/site /info/current directory. For each file in there, there will be an identically named key in newconf which points to a hash of (key,value) pairs matching the contents of the file. For instance, the cgi file contains three entries: version, enabled, and scriptalias. That means that newconf['cgi'] will point to a hash with three entries, newconf['cgi']['version'], newconf['cgi']['enabled'], and newconf['cgi']['scriptalias'], whose values are the strings you see in the file. So if you wanted to add more directives pertaining to the cgi directory of a site, you would perhaps write something like:

content = " " % newconf['cgi']['scriptalias']

The third argument to the function is a similar hash of hashes, but this is the old configuration of a site. Obviously this doesn't pertain to a site being added, but if you wanted to do some complicated customization on a site that you were editing (not adding), then perhaps you would use this information. However, I don't expect this will be used much (it's there more for completeness' sake).

The final argument is a bit more complicated, but it is really only targeted at people who want to do fancy things with the customizations. We support the concept of both local and global customizations. Global customizations are implemented by files placed in /usr/lib/python2.1/site-packages/vh3/custom. Local customizations are implemented by similar files placed in /home/virtual/site /info/custom, and if both customization files define the same hook function, the local customization takes priority. These files are imported into the backend as python modules, which python is quite happy to pass around as variables. So, in order to allow customizations to "stack" on each other, we pass whichever customization module is not used to the customization module that is used, so that the used customization module has access to the unused customization module's information. Let's consider an example where all your customers on a box except one want to have /webmail as an alias to squirrelmail, and that one who doesn't wants /mail instead. In the global customization file you would include the code printed above.
In the local customization file, you would include the code:

import virthost
def add_custom_directives(site, newconf, oldconf, cust):
return "Alias /mail %s/var/www/squirrelmail/" % \

That would be it. This one customer would get what he wants, and you don't have to clutter your global customizations with 'if' cases. We don't need to use the fourth argument in this case. Now, let's say everyone is happy with the global customizations, but you have one customer who wants both directives added to his container. Once again, you would define a local customization for him similar to above, but notice the difference:

import virthost
def add_custom_directives(site, newconf, oldconf, cust):
        content = "Alias /mail %s/var/www/squirrelmail/\n" % \
        content = content + cust.add_custom_directives(site, newconf,
oldconf, cust)
        return content

Now you can change the global customizations all you want, and this site, even though locally customized, will still contain those global changes too. You can do this the other way around too, where the hook is only defined at the global customization level, but it may make use of some value defined in the local customization file. As a final example, here is a definition of add_custom_directives() that will allow for subdomains (this would need to be modified somewhat if you wanted to support subdomains for domain aliases too):

import virthost
def add_custom_directives(site, newconf, oldconf, cust):
    content = """
        ServerAlias *.%s
        RewriteEngine On
        RewriteCond %%{HTTP_HOST} .*.%s [NC]
        RewriteCond %%{HTTP_HOST} !%s  [NC]
        RewriteRule ^/(.*)$      %s/var/www/html/%%{HTTP_HOST}/$1 [L]
""" % (newconf['siteinfo']['domain'],newconf['siteinfo']['domain'],
    return content

Of course, the squirrelmail redirects could be included in this as well.

Finally, these customization files contain some simple variables. In cases where I felt users would want to customize simple things, e.g. the number of anonymous users allowed to connect to profptd per site, I simply created a variable which, if set, will provide the value that will be used. So in anonftp's customization (base file is located in /usr/share/doc/webppliance-anonftp- /, and would be copied to /usr/lib/python2.1/site-packages/vh3/custom/ or /home/virtual/site /info/custom/ where appropriate), you can simply set the variable max_clients; e.g.

max_clients = 10

When adding global customizations, you will need to restart the GUI before any changes take effect. In order to apply these customizations to already existing sites, simply click on the edit button for the domain and then select 'save changes'. Alternatively, you can simply use the commandline to invoke the edit: /usr/local/bin/EditVirtDomain site. If a customization does prevent a service from starting due to a syntax error, simply remove the customization file (both the .py file and the .pyc file (created by restarting the GUI/running the command line) if it exists) and use either the GUI or the commandline to edit the site. For this reason, it may be better to experiment with a local customization to a single site first, thus reducing the number of sites you may have to edit in case something goes wrong.



Below is python source code that can be used in that will essentially recreate the httpd configuration that webppliance produces by default when a site is created.  Please be sure to preserve indentation, python requires the indentation you see below.

import string
import socket
import virthost
def vh_conf(site, newconf, oldconf, cust):
   ln     = []
   tmpstr = ""

   if newconf['ipinfo']['namebased'] == "1":
      iplist = eval(newconf['ipinfo']['nbaddrs'])
      iplist = eval(newconf['ipinfo']['ipaddrs'])

   tmpstr = "<VirtualHost"
   for ip in iplist:
      tmpstr = "%s %s:80" % (tmpstr,ip)
   ln.append("%s>" % (tmpstr))
   ln.append("\tServerName %s" % newconf['apache']['webserver'])

   tmpstr = "\tServerAlias %s" % newconf['siteinfo']['domain']
   for al in eval(newconf['aliases']['aliases']):
      tmpstr = "%s %s" % (tmpstr,al)
   ln.append("\tServerAdmin %s" % newconf['siteinfo']['email'])
   ln.append("\tDocumentRoot /home/virtual/%s/fst/var/www/html" % site)
   ln.append("\tUser %s" % newconf['siteinfo']['admin'])
   ln.append("\tGroup %s" % newconf['siteinfo']['admin'])

   adminurl = "webhost/services/virtualhosting/siteadmin?ocw_login_domain=%s" % newconf['siteinfo']['domain']
   userurl  = "webhost/services/virtualhosting/useradmin?ocw_login_domain=%s" % newconf['siteinfo']['domain']
   hostip   = socket.gethostbyname(socket.gethostname())

   ln.append("\tRedirect /admin https://%s:19638/%s" % (hostip,adminurl))
   ln.append("\tRedirect /user https://%s:19638/%s" % (hostip,userurl))
   ln.append("\tUserDir /home/virtual/%s/fst/home/*/public_html" % site)
   ln.append("\tAliasMatch ^/users/([^/]+)/?(.*) /home/virtual/%s/fst/home/$1/public_html/$2" % site)
   ln.append("\t<Directory /home/virtual/%s/fst/var/www/html/>" % site)
   ln.append("\t\tAllow from all")
   ln.append("\t\tAllowOverride All")
   ln.append("\t\tOrder allow,deny")
   ln.append("\tSetEnv SITE_ROOT /home/virtual/%s/fst" % site)
   ln.append("\tSetEnv SITE_HTMLROOT /home/virtual/%s/fst/var/www/html" % site)
   ln.append("\tInclude /etc/httpd/conf/%s" % site)
   return string.join(ln,"\n")


Related Knowledge

Related Links
Last ModifiedUsageSatisfiedLast Used
6/16/2006 2:05:29 AM67 10/11/2007 2:09:43 AM

4cc899da08664637a8bc437308d3ddd7 3ccb419cf98083f3bb45808fba8dbc7c 6311ae17c1ee52b36e68aaf4ad066387

Email subscription for changes to this article
Save as PDF