This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/servers/VirtualHostsServer/VirtualHostsServer.py

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)