430 lines
16 KiB
Python
Executable File
430 lines
16 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 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.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
|
|
|
|
|
|
applicationName = 'VirtualHostsServer'
|
|
applicationRole = 'virtualhosts'
|
|
dispatcher = None
|
|
|
|
|
|
class AdminVirtualHosts(AdminServerMixin, AdminVirtualHostsCommon):
|
|
pass
|
|
register(AdminVirtualHosts)
|
|
|
|
|
|
class VirtualHost(ObjectServerMixin, VirtualHostCommon):
|
|
strings = None
|
|
strings_keyType = 'string'
|
|
strings_type = 'mapping'
|
|
strings_valueType = 'string'
|
|
|
|
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
|
|
del(objectsByHostName[self.hostName])
|
|
|
|
def importFromXmlRpc(self, virtualHostImport, parentSlot = None):
|
|
hostName = self.hostName
|
|
VirtualHostCommon.importFromXmlRpc(
|
|
self, virtualHostImport, parentSlot = parentSlot)
|
|
objectsByHostName = self.getServer().virtualServer.objectsByHostName
|
|
if self.hostName != hostName:
|
|
if hostName is not None:
|
|
del objectsByHostName[hostName]
|
|
if self.hostName is not None:
|
|
if objectsByHostName.has_key(self.hostName) \
|
|
and self.id != objectsByHostName[self.hostName].id:
|
|
raise faults.DuplicateHostName(self.hostName)
|
|
objectsByHostName[self.hostName] = self
|
|
|
|
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
|
|
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:
|
|
# BAD, we remove it before it harms somebody
|
|
del virtualServer.objects[objectId]
|
|
virtualServer.markObjectAsDeleted(objectId)
|
|
virtualServer.markCoreAsDirty()
|
|
raise faults.BadValue()
|
|
if object.defaultDispatcherId[-1] == '/':
|
|
object.defaultDispatcherId = object.defaultDispatcherId[:-1]
|
|
virtualServer.markObjectAsDirty(object)
|
|
|
|
newDispatcherId = object.defaultDispatcherId
|
|
registerDispatcherId(newDispatcherId)
|
|
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 convertVirtualServersIds(self, sourceDispatcherId,
|
|
destinationDispatcherId):
|
|
self.virtualServer.convertIds(
|
|
sourceDispatcherId, destinationDispatcherId)
|
|
self.virtualServer.markAllAsDirtyFIXME()
|
|
return None
|
|
|
|
def exportVirtualServer(self, virtualServerId, exportDirectoryPath):
|
|
return None
|
|
|
|
def getDefaultVirtualHost(self):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
if not virtualServer.admin.defaultVirtualHostId:
|
|
raise faults.MissingItem('default virtual host')
|
|
return self.getObjectXmlRpc(self.admin.defaultVirtualHostId)
|
|
|
|
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('http://' + object.hostName)
|
|
raise faults.MissingItem(dispatcherId)
|
|
|
|
def getObjectByHostName(self, hostName):
|
|
"""Return the first virtual host with the given host name."""
|
|
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
if not virtualServer.objectsByHostName:
|
|
# The 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'
|
|
self.addObjectXmlRpc(object.exportToXmlRpc())
|
|
if not virtualServer.objectsByHostName.has_key(hostName):
|
|
for k in virtualServer.objectsByHostName.keys():
|
|
if fnmatch(hostName, k):
|
|
hostName = k
|
|
break
|
|
else:
|
|
raise faults.MissingItem(hostName)
|
|
object = virtualServer.objectsByHostName[hostName]
|
|
return object
|
|
|
|
def getObjectByHostNameXmlRpc(self, hostName):
|
|
hostName = iso8859_15(hostName)
|
|
return self.getObjectByHostName(hostName).exportToXmlRpc()
|
|
|
|
def getObjectIdByHostNameXmlRpc(self, hostName):
|
|
hostName = iso8859_15(hostName)
|
|
return self.getObjectByHostName(hostName).id
|
|
|
|
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):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
hostName = iso8859_15(hostName)
|
|
if virtualServer.objectsByHostName.has_key(hostName):
|
|
return 1
|
|
for k in virtualServer.objectsByHostName.keys():
|
|
if fnmatch(hostName, k):
|
|
return 1
|
|
return 0
|
|
|
|
def importVirtualServer(self, virtualServerId, importDirectoryPath):
|
|
return None
|
|
|
|
def modifyObjectXmlRpc(self, objectImport):
|
|
version = ObjectsServer.modifyObjectXmlRpc(self, objectImport)
|
|
id = objectImport['id']
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(id)
|
|
self.updateApacheVHost(object)
|
|
return version
|
|
|
|
def registerPublicMethods(self):
|
|
ObjectsServer.registerPublicMethods(self)
|
|
self.registerPublicMethod('getHostName', self.getHostNameXmlRpc)
|
|
self.registerPublicMethod('getDefaultVirtualHost',
|
|
self.getDefaultVirtualHost)
|
|
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)
|
|
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 vistual 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 writen, or the templace 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
|
|
|
|
virtualHostsServer = VirtualHostsServer()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
virtualHostsServer.launch(applicationName, applicationRole)
|
|
|