548 lines
20 KiB
Python
Executable File
548 lines
20 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
# Glasnost
|
|
# By: Odile Bénassy <obenassy@entrouvert.com>
|
|
# Romain Chantereau <rchantereau@entrouvert.com>
|
|
# Nicolas Clapiès <nclapies@easter-eggs.org>
|
|
# Pierre-Antoine Dejace <padejace@entrouvert.be>
|
|
# Thierry Dulieu <tdulieu@easter-eggs.com>
|
|
# Florent Monnier <monnier@codelutin.com>
|
|
# Cédric Musso <cmusso@easter-eggs.org>
|
|
# Frédéric Péters <fpeters@entrouvert.be>
|
|
# Benjamin Poussin <poussin@codelutin.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
# Sébastien Régnier <regnier@codelutin.com>
|
|
# Emmanuel Saracco <esaracco@easter-eggs.com>
|
|
#
|
|
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
|
|
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
|
|
# Emmanuel Saracco & Théridion
|
|
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
|
|
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
|
|
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
|
|
# Régnier, Emmanuel Saracco, Théridion & Vecam
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
__doc__ = """Glasnost Virtual Hosts Server"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
from fnmatch import fnmatch
|
|
import os
|
|
import sgmllib
|
|
import sys
|
|
|
|
|
|
glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install
|
|
sys.path.insert(0, glasnostPythonDir)
|
|
|
|
import glasnost
|
|
|
|
from glasnost.common.VirtualHostsCommon import *
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.system as system
|
|
import glasnost.common.tools_new as commonTools
|
|
import glasnost.common.xhtmlgenerator as X
|
|
|
|
from glasnost.server.ObjectsServer import register, ObjectServerMixin, \
|
|
AdminServerMixin, ObjectsServer, ObjectsVirtualServer
|
|
from glasnost.server.tools import *
|
|
|
|
from glasnost.proxy.DispatcherProxy import getApplicationToken, \
|
|
registerDispatcherId, registerVirtualServer, \
|
|
updateDispatcherIdProfiles
|
|
|
|
|
|
applicationName = 'VirtualHostsServer'
|
|
applicationRole = 'virtualhosts'
|
|
dispatcher = None
|
|
|
|
|
|
class AdminVirtualHosts(AdminServerMixin, AdminVirtualHostsCommon):
|
|
pass
|
|
register(AdminVirtualHosts)
|
|
|
|
|
|
class VirtualHostXmlParser(sgmllib.SGMLParser):
|
|
inCustomWeb = 0
|
|
customWeb = None
|
|
customWebs = None
|
|
currentRole = None
|
|
|
|
inLocale = 0
|
|
locale = None
|
|
|
|
def __init__(self, body):
|
|
sgmllib.SGMLParser.__init__(self)
|
|
self.customWebs = {}
|
|
self.feed(body)
|
|
|
|
def start_customweb(self, attrs):
|
|
if attrs[0][0] == 'role':
|
|
self.currentRole = str(attrs[0][1])
|
|
self.inCustomWeb = 1
|
|
self.customWeb = ''
|
|
|
|
def end_customweb(self):
|
|
if self.inCustomWeb:
|
|
self.inCustomWeb = 0
|
|
self.customWebs[self.currentRole] = self.customWeb
|
|
|
|
def start_locale(self, attrs):
|
|
self.inLocale = 1
|
|
self.locale = ''
|
|
|
|
def end_locale(self):
|
|
self.locale = self.locale.strip()
|
|
self.inLocale = 0
|
|
|
|
def handle_data(self, data):
|
|
if self.inCustomWeb:
|
|
self.customWeb += data
|
|
if self.inLocale:
|
|
self.locale += data
|
|
|
|
|
|
class VirtualHost(ObjectServerMixin, VirtualHostCommon):
|
|
def acquireNonCore(self, objectDirectoryPath = None,
|
|
dataDirectoryPath = None, parentSlot = None):
|
|
ObjectServerMixin.acquireNonCore(
|
|
self, objectDirectoryPath = objectDirectoryPath,
|
|
dataDirectoryPath = dataDirectoryPath, parentSlot = parentSlot)
|
|
self.customWebs = {}
|
|
self.locales = []
|
|
profilesPath = os.path.join(commonTools.configDir, 'profiles')
|
|
for p in self.profiles or []:
|
|
pFileName = os.path.join(profilesPath, p + '.xml')
|
|
vhP = VirtualHostXmlParser(open(pFileName).read())
|
|
self.customWebs.update(vhP.customWebs)
|
|
if vhP.locale:
|
|
self.locales.append(vhP.locale)
|
|
|
|
def checkModifyIsPossible(self, changes, givenSlotNames = None):
|
|
ObjectServerMixin.checkModifyIsPossible(
|
|
self, changes, givenSlotNames = givenSlotNames)
|
|
objectsByHostName = self.getServer().virtualServer.objectsByHostName
|
|
if (not givenSlotNames or 'hostName' in givenSlotNames) \
|
|
and changes.hostName != self.hostName \
|
|
and changes.hostName is not None:
|
|
if objectsByHostName.has_key(changes.hostName) \
|
|
and changes.id != objectsByHostName[changes.hostName].id:
|
|
raise faults.DuplicateHostName(changes.hostName)
|
|
|
|
def clear(self):
|
|
objectsByHostName = self.getServer().virtualServer.objectsByHostName
|
|
if objectsByHostName.has_key(self.hostName):
|
|
del objectsByHostName[self.hostName]
|
|
|
|
def modify(self, changes, givenSlotNames = None):
|
|
objectsByHostName = self.getServer().virtualServer.objectsByHostName
|
|
hostName = self.hostName
|
|
ObjectServerMixin.modify(self, changes,
|
|
givenSlotNames = givenSlotNames)
|
|
if self.hostName != hostName:
|
|
if hostName is not None:
|
|
del objectsByHostName[hostName]
|
|
if self.hostName is not None:
|
|
objectsByHostName[self.hostName] = self
|
|
|
|
def releaseNonCore(self, parentSlot = None):
|
|
if self.__dict__.has_key('customWebs'):
|
|
del self.customWebs
|
|
if self.__dict__.has_key('locales'):
|
|
del self.locales
|
|
ObjectServerMixin.releaseNonCore(self, parentSlot = parentSlot)
|
|
|
|
register(VirtualHost)
|
|
|
|
|
|
class VirtualHostsVirtualServer(ObjectsVirtualServer):
|
|
objectsByHostName = None
|
|
|
|
def init(self):
|
|
ObjectsVirtualServer.init(self)
|
|
self.objectsByHostName = {}
|
|
|
|
def initFromOldData(self, data):
|
|
ObjectsVirtualServer.initFromOldData(self, data)
|
|
self.objectsByHostName = {}
|
|
for object in self.objects.values():
|
|
if object.hostName is not None:
|
|
self.objectsByHostName[object.hostName] = object
|
|
|
|
|
|
class VirtualHostsServer(VirtualHostsCommonMixin, ObjectsServer):
|
|
VirtualServer = VirtualHostsVirtualServer
|
|
hasMultipleVirtualServers = 0
|
|
|
|
def addObjectXmlRpc(self, objectImport):
|
|
"""Create a new virtual host on the server.
|
|
|
|
Keyword argument:
|
|
=================
|
|
|
|
*objectImport*:
|
|
The new object in XML RPC dictionnary format.
|
|
|
|
Returns:
|
|
========
|
|
|
|
The new virtual host ID.
|
|
|
|
Exceptions:
|
|
===========
|
|
|
|
*faults.UserAccessDenied*:
|
|
The user is not in the admin set.
|
|
|
|
*standard Exception*:
|
|
- The thing category 'object' doesn't exists. (very grave !)
|
|
- The adminImport __thingName__ key is not a valid Thing name.
|
|
|
|
*KeyError*:
|
|
+ The virtual server ID does not correspond to a instanciated
|
|
virtual server.
|
|
+ No client ID corresponding to the client token.
|
|
|
|
*AttributeError*:
|
|
The default dispatcherId contained in the object is not a string.
|
|
|
|
*faults.UnknownApplicationToken*:
|
|
The given application token is not in the dispatcher virtual server
|
|
ids dictionnary.
|
|
|
|
*OSError*:
|
|
The apache configuration directory does not exists and cannot be
|
|
created.
|
|
|
|
*IOError*:
|
|
The vhost file cannot be writen, or the templace config file cannot
|
|
be read.
|
|
|
|
"""
|
|
|
|
objectId = ObjectsServer.addObjectXmlRpc(self, objectImport)
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if not object.defaultDispatcherId:
|
|
# we remove it before it harms somebody (actually it should have
|
|
# been detected by ObjectsServer.addObjectXmlRpc)
|
|
del virtualServer.objects[objectId]
|
|
virtualServer.markObjectAsDeleted(objectId)
|
|
virtualServer.markCoreAsDirty()
|
|
raise faults.BadValue()
|
|
|
|
if not object.profiles:
|
|
object.profiles = ['basic', 'cms', 'vote']
|
|
virtualServer.markObjectAsDirty(object)
|
|
|
|
if virtualServer.objectsByHostName.has_key(object.hostName):
|
|
# HostName already used.
|
|
del virtualServer.objects[objectId]
|
|
virtualServer.markObjectAsDeleted(objectId)
|
|
virtualServer.markCoreAsDirty()
|
|
raise faults.DuplicateHostName(object.hostName)
|
|
|
|
virtualServer.objectsByHostName[object.hostName] = object
|
|
virtualServer.markCoreAsDirty()
|
|
|
|
if object.defaultDispatcherId[-1] == '/':
|
|
object.defaultDispatcherId = object.defaultDispatcherId[:-1]
|
|
virtualServer.markObjectAsDirty(object)
|
|
|
|
newDispatcherId = object.defaultDispatcherId
|
|
registerDispatcherId(newDispatcherId, object.profiles)
|
|
newVirtualServerId = commonTools.makeApplicationId(
|
|
newDispatcherId, self.applicationRole)
|
|
context.push(
|
|
applicationId = newVirtualServerId,
|
|
)
|
|
context.getVar('applicationTokens')[newVirtualServerId] = \
|
|
getApplicationToken()
|
|
context.pull()
|
|
registerVirtualServer(self.hostName, self.port, newVirtualServerId)
|
|
|
|
self.updateApacheVHost(object)
|
|
return objectId
|
|
|
|
def canAddObject(self):
|
|
if context.getVar('noVirtualHost', default = 0):
|
|
return 1
|
|
return ObjectsServer.canAddObject(self)
|
|
|
|
def convertVirtualServersIds(self, sourceDispatcherId,
|
|
destinationDispatcherId):
|
|
self.virtualServer.convertIds(
|
|
sourceDispatcherId, destinationDispatcherId)
|
|
self.virtualServer.markAllAsDirtyFIXME()
|
|
return None
|
|
|
|
def exportVirtualServer(self, virtualServerId, exportDirectoryPath):
|
|
return None
|
|
|
|
def getHostNameXmlRpc(self):
|
|
"""Return the url of the virtual server."""
|
|
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
dispatcherId = commonTools.extractDispatcherId(
|
|
virtualServerId).lower()
|
|
for object in virtualServer.objects.values():
|
|
if dispatcherId == commonTools.extractDispatcherId(
|
|
object.defaultDispatcherId).lower():
|
|
return utf8(object.hostName)
|
|
raise faults.MissingItem(dispatcherId)
|
|
|
|
def getObjectByHostNameXmlRpc(self, hostName):
|
|
hostName = iso8859_15(hostName)
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
objectId = self.getObjectIdByHostName(hostName)
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
object.acquireNonCore()
|
|
try:
|
|
result = object.exportToXmlRpc()
|
|
finally:
|
|
object.releaseNonCore()
|
|
return result
|
|
|
|
def getObjectIdByHostName(self, hostName):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
askedHostName = hostName
|
|
if not virtualServer.objectsByHostName:
|
|
# There is no virtual host defined. Create one with the requested
|
|
# host name.
|
|
object = commonTools.newThing('object', 'virtualhosts.VirtualHost')
|
|
object.title = 'Glasnost'
|
|
object.hostName = hostName
|
|
object.defaultDispatcherId = 'glasnost://%s' % hostName
|
|
object.language = 'en'
|
|
object.writersSet = [system.generalPublicId]
|
|
object.readersSet = [system.generalPublicId]
|
|
context.push(noVirtualHost = 1)
|
|
try:
|
|
self.addObjectXmlRpc(object.exportToXmlRpc())
|
|
finally:
|
|
context.pull()
|
|
if not virtualServer.objectsByHostName.has_key(hostName):
|
|
# hostNames, without eventual www.
|
|
hostNames = virtualServer.objectsByHostName.keys()
|
|
hostNames.sort(lambda x,y: -cmp(len(x), len(y)))
|
|
for k in hostNames:
|
|
if fnmatch(hostName, '*.'+k):
|
|
hostName = k
|
|
break
|
|
else:
|
|
if not virtualServer.admin.defaultVirtualHostId:
|
|
raise faults.MissingItem(hostName)
|
|
try:
|
|
object = virtualServer.objects[
|
|
virtualServer.admin.defaultVirtualHostId]
|
|
except KeyError:
|
|
raise faults.MissingItem(hostName)
|
|
return object.id
|
|
object = virtualServer.objectsByHostName[hostName]
|
|
return object.id
|
|
|
|
def getObjectIdByHostNameXmlRpc(self, hostName):
|
|
hostName = iso8859_15(hostName)
|
|
return self.getObjectIdByHostName(hostName)
|
|
|
|
def hasDispatcherIdXmlRpc(self, dispatcherId):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
dispatcherId = iso8859_15(dispatcherId)
|
|
for object in virtualServer.objects.values():
|
|
if object.defaultDispatcherId == dispatcherId:
|
|
return 1
|
|
return 0
|
|
|
|
def hasHostNameXmlRpc(self, hostName):
|
|
# note that this method may return false while getObjectByHostName
|
|
# returns something
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
hostName = iso8859_15(hostName)
|
|
if virtualServer.objectsByHostName.has_key(hostName):
|
|
return 1
|
|
#hostNames = virtualServer.objectsByHostName.keys()
|
|
#hostNames.sort(lambda x,y: -cmp(len(x), len(y)))
|
|
#for k in hostNames:
|
|
# if fnmatch(hostName, '*.'+k):
|
|
# return 1
|
|
return 0
|
|
|
|
def importVirtualServer(self, virtualServerId, importDirectoryPath):
|
|
return None
|
|
|
|
def isAdmin(self):
|
|
if context.getVar('noVirtualHost', default = 0):
|
|
return 1
|
|
return ObjectsServer.isAdmin(self)
|
|
|
|
def modifyObjectXmlRpc(self, objectImport):
|
|
id = objectImport['id']
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
oldObject = virtualServer.loadObjectCore(id)
|
|
if oldObject.profiles:
|
|
oldObjectProfiles = oldObject.profiles[:]
|
|
else:
|
|
oldObjectProfiles = []
|
|
version = ObjectsServer.modifyObjectXmlRpc(self, objectImport)
|
|
object = virtualServer.loadObjectCore(id)
|
|
oldObjectProfiles.sort()
|
|
object.profiles.sort()
|
|
if oldObjectProfiles != object.profiles:
|
|
updateDispatcherIdProfiles(object.defaultDispatcherId,
|
|
object.profiles)
|
|
self.updateApacheVHost(object)
|
|
return version
|
|
|
|
def registerPublicMethods(self):
|
|
ObjectsServer.registerPublicMethods(self)
|
|
self.registerPublicMethod('getHostName', self.getHostNameXmlRpc)
|
|
self.registerPublicMethod('getObjectByHostName',
|
|
self.getObjectByHostNameXmlRpc)
|
|
self.registerPublicMethod('getObjectIdByHostName',
|
|
self.getObjectIdByHostNameXmlRpc)
|
|
self.registerPublicMethod('hasDispatcherId',
|
|
self.hasDispatcherIdXmlRpc)
|
|
self.registerPublicMethod('hasHostName', self.hasHostNameXmlRpc)
|
|
|
|
def registerToDispatcher(self):
|
|
ObjectsServer.registerToDispatcher(self)
|
|
for virtualHost in self.virtualServer.objects.values():
|
|
newDispatcherId = virtualHost.defaultDispatcherId
|
|
registerDispatcherId(newDispatcherId, virtualHost.profiles)
|
|
newVirtualServerId = commonTools.makeApplicationId(
|
|
newDispatcherId, self.applicationRole)
|
|
context.push(
|
|
applicationId = newVirtualServerId,
|
|
)
|
|
context.getVar('applicationTokens')[newVirtualServerId] = \
|
|
getApplicationToken()
|
|
context.pull()
|
|
registerVirtualServer(self.hostName, self.port, newVirtualServerId)
|
|
|
|
def repairVirtualServer(self, virtualServer, version):
|
|
changed = 0
|
|
if version < 5001:
|
|
changed = 1
|
|
virtualServer.objectsByHostName = {}
|
|
for object in virtualServer.objects.values():
|
|
if object.hostName is not None:
|
|
virtualServer.objectsByHostName[object.hostName] = object
|
|
if version < 5004:
|
|
changed = virtualServer.admin.repair(5004) or changed
|
|
for id, object in virtualServer.objects.items():
|
|
changed = object.repair(5004) or changed
|
|
if version <= 1009000:
|
|
admin = virtualServer.admin
|
|
adminId = '%s/%s/__admin__' % (
|
|
commonTools.extractDispatcherId(
|
|
virtualServer.virtualServerId),
|
|
self.applicationRole)
|
|
if admin.id != adminId:
|
|
changed = 1
|
|
admin.id = adminId
|
|
if version <= 1033000:
|
|
for object in virtualServer.objects.values():
|
|
if object.defaultDispatcherId[-1] == '/':
|
|
object.defaultDispatcherId = object.defaultDispatcherId[
|
|
:-1]
|
|
changed = 1
|
|
|
|
if changed:
|
|
virtualServer.markAllAsDirtyFIXME()
|
|
|
|
def updateApacheVHost(self, object):
|
|
"""Update the apache virtual host configuration.
|
|
|
|
Get the apache config template from the virtual host server data
|
|
directory.
|
|
Create the apache vhost files and fill them to add the given object as
|
|
a new virtual host.
|
|
|
|
Keyword argument
|
|
================
|
|
|
|
*object*:
|
|
The virtual host object instance to add to the apache virtual
|
|
host(s).
|
|
|
|
Exceptions
|
|
==========
|
|
|
|
*OSError*:
|
|
The apache configuration directory does not exists and cannot be
|
|
created.
|
|
|
|
*IOError*:
|
|
The vhost file cannot be written, or the template config file
|
|
cannot be read.
|
|
|
|
"""
|
|
|
|
hostName = object.hostName
|
|
templateFileName = '%s/%s/apache-template.conf' % (
|
|
self.dataDirectoryPath, applicationRole)
|
|
if not os.path.exists(templateFileName):
|
|
return
|
|
if not os.path.exists('%s/%s/apache' % (
|
|
self.dataDirectoryPath, applicationRole)):
|
|
try:
|
|
os.mkdir('%s/%s/apache' % (
|
|
self.dataDirectoryPath, applicationRole))
|
|
except OSError:
|
|
return
|
|
|
|
apacheConfFileName = '%s/%s/apache/vhost-%s.conf' % (
|
|
self.dataDirectoryPath, applicationRole, hostName)
|
|
try:
|
|
open(apacheConfFileName, 'w').write(
|
|
open(templateFileName).read() % {'hostName': hostName})
|
|
except IOError:
|
|
pass
|
|
|
|
def upgradeVirtualServer_0001_0025(self, virtualServer):
|
|
# Repair dissociated objects and objectsByHostName
|
|
virtualServer.objectsByHostName = {}
|
|
for object in virtualServer.objects.values():
|
|
if object.hostName is not None:
|
|
virtualServer.objectsByHostName[object.hostName] = object
|
|
virtualServer.markCoreAsDirty()
|
|
|
|
def upgradeVirtualServer_0001_0027(self, virtualServer):
|
|
for object in virtualServer.objects.values():
|
|
object.profiles = ['basic', 'cms', 'vote', 'translations']
|
|
virtualServer.markCoreAsDirty()
|
|
|
|
virtualHostsServer = VirtualHostsServer()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
virtualHostsServer.launch(applicationName, applicationRole)
|
|
|