465 lines
13 KiB
Python
465 lines
13 KiB
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 Common Tools"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
try:
|
|
import glasnost
|
|
except ImportError:
|
|
class G:
|
|
applicationName = 'glasnost'
|
|
glasnost = G()
|
|
|
|
|
|
|
|
import ConfigParser
|
|
import gettext
|
|
import random
|
|
|
|
import context
|
|
|
|
|
|
config = None
|
|
configFile = '/etc/%s/config' % glasnost.applicationName
|
|
|
|
|
|
def buildPython22ClassLinearization(aClass, baseClasses):
|
|
if aClass in baseClasses:
|
|
baseClasses.remove(aClass)
|
|
baseClasses.append(aClass)
|
|
for baseClass in aClass.__bases__:
|
|
buildPython22ClassLinearization(baseClass, baseClasses)
|
|
|
|
|
|
def createMultiId(listIds):
|
|
"""Create new Id, with list of Id
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*list*:
|
|
list with len >= 1
|
|
|
|
Return:
|
|
=======
|
|
one Id. This Id can be splited with splitSubId.
|
|
"""
|
|
return ':'.join(listIds)
|
|
|
|
|
|
def extractDispatcherId(id):
|
|
"""Return the dispatcher ID string from an server ID string
|
|
|
|
Keyword argument:
|
|
=================
|
|
|
|
*id*:
|
|
The server ID string.
|
|
|
|
Exception:
|
|
==========
|
|
|
|
*AssertionError*:
|
|
The server ID doesn't begin by 'glasnost://'.
|
|
|
|
*Standard Exception*:
|
|
The server ID folowing the protocol specifier is not valid.
|
|
|
|
"""
|
|
|
|
try:
|
|
assert id.startswith('glasnost://')
|
|
splittedApplicationId = id[11:].split('/', 1)
|
|
return 'glasnost://%s' % splittedApplicationId[0]
|
|
except (AssertionError, IndexError):
|
|
raise Exception('Malformed id or application id = "%s"' % str(id))
|
|
|
|
|
|
def extractLocalId(id):
|
|
try:
|
|
assert id.startswith('glasnost://')
|
|
splittedId = id[11:].split('/', 2)
|
|
if len(splittedId) <= 2:
|
|
return ''
|
|
else:
|
|
return splittedId[2]
|
|
except (AssertionError, IndexError):
|
|
raise Exception('Malformed id = "%s"' % str(id))
|
|
|
|
|
|
def extractRole(id):
|
|
try:
|
|
assert id.startswith('glasnost://')
|
|
splittedApplicationId = id[11:].split('/', 2)
|
|
if len(splittedApplicationId) == 1:
|
|
return ''
|
|
else:
|
|
return splittedApplicationId[1]
|
|
except (AssertionError, IndexError):
|
|
raise Exception('Malformed id or application id = "%s"' % str(id))
|
|
|
|
|
|
def extractServerId(id):
|
|
"""Extract the server ID from an object ID.
|
|
|
|
Keyword argument:
|
|
=================
|
|
|
|
*id*:
|
|
The object ID.
|
|
|
|
Return the server ID.
|
|
|
|
Exception:
|
|
==========
|
|
|
|
*AssertionError*:
|
|
The object ID string is not a glasnost service ('glasnost://').
|
|
|
|
*IndexError*:
|
|
The given object ID is incomplete.
|
|
|
|
"""
|
|
|
|
try:
|
|
assert id.startswith('glasnost://')
|
|
splittedId = id[11:].split('/', 2)
|
|
if len(splittedId) == 1 or not splittedId[1]:
|
|
# Id is a dispacherId.
|
|
return 'glasnost://%s' % splittedId[0]
|
|
else:
|
|
return 'glasnost://%s/%s' % (splittedId[0], splittedId[1])
|
|
except (AssertionError, IndexError):
|
|
raise Exception('Malformed id or application id = "%s"' % str(id))
|
|
|
|
|
|
def getAllThingClasses():
|
|
thingClasses = context.getVar('thingClasses')
|
|
return thingClasses.getAll()
|
|
|
|
|
|
def getC3ClassLinearization(c):
|
|
# An implementation of the C3 class hierarchy Linearization.
|
|
# Cf. http://www.webcom.com/haahr/dylan/linearization-oopsla96.html
|
|
linearization = [c]
|
|
if c.__bases__:
|
|
basesByPrecedenceOrder = list(c.__bases__)
|
|
basesByPrecedenceOrder.reverse()
|
|
remainingInputs = [getC3ClassLinearization(base)
|
|
for base in c.__bases__] + [basesByPrecedenceOrder]
|
|
while remainingInputs:
|
|
# The first class which is not in the tail of each remaining input
|
|
# is the next class to append to the linearization.
|
|
for remainingInput in remainingInputs:
|
|
candidate = remainingInput[0]
|
|
for remainingInput2 in remainingInputs:
|
|
if candidate in remainingInput2[1:]:
|
|
break
|
|
else:
|
|
break
|
|
else:
|
|
raise Exception(
|
|
'Inconsistent class hierarchy for class %s' % c)
|
|
# A good candidate has been found.
|
|
# Add it to the partial linearization and remove it from the
|
|
# remaining inputs.
|
|
linearization.append(candidate)
|
|
newRemainingInputs = []
|
|
for remainingInput in remainingInputs:
|
|
if remainingInput[0] == candidate:
|
|
del remainingInput[0]
|
|
if not remainingInput:
|
|
continue
|
|
newRemainingInputs.append(remainingInput)
|
|
remainingInputs = newRemainingInputs
|
|
return linearization
|
|
|
|
|
|
def getConfig(section, value, default = None, vars = None, raw = 0):
|
|
"""Retrieve values from the Glasnost config file.
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*section*:
|
|
The section string in the config file where the attribute is.
|
|
|
|
*value*:
|
|
The attribute name string to get value.
|
|
|
|
*default*:
|
|
The default value to set if the attribute is not found.
|
|
|
|
*vars*:
|
|
A dictionnary used to override the set "%" attributes of the config
|
|
file.
|
|
|
|
*raw*:
|
|
If true, the vars dictionnary is not used.
|
|
|
|
Return:
|
|
=======
|
|
|
|
+ Return the wanted value
|
|
|
|
+ The default value if no attribute was found.
|
|
|
|
+ None if no attribute was found and no default value given.
|
|
|
|
"""
|
|
|
|
global config
|
|
if config is None:
|
|
config = ConfigParser.ConfigParser()
|
|
config.readfp(open(configFile))
|
|
return getConfig(section, value, default = default, vars = vars,
|
|
raw = raw)
|
|
else:
|
|
try:
|
|
return config.get(section, value, vars = vars, raw = raw)
|
|
except ConfigParser.NoOptionError:
|
|
return default
|
|
except ConfigParser.NoSectionError:
|
|
return default
|
|
|
|
|
|
def getConfigNoCache(section, value, default = None, vars = None, raw = 0):
|
|
conf = ConfigParser.ConfigParser()
|
|
conf.readfp(open(configFile))
|
|
try:
|
|
return conf.get(section, value, vars = None, raw = raw)
|
|
except ConfigParser.NoOptionError:
|
|
return default
|
|
except ConfigParser.NoSectionError:
|
|
return default
|
|
|
|
|
|
### FIXME: To remove ASAP.
|
|
|
|
|
|
def getSlotPath(slot):
|
|
if slot is None:
|
|
return 'self'
|
|
else:
|
|
return slot.getPath()
|
|
|
|
|
|
def getThingClass(thingCategory, thingName):
|
|
"""Return the class of a thing."""
|
|
|
|
thingClasses = context.getVar('thingClasses')
|
|
thingClass = thingClasses.get(thingCategory, thingName)
|
|
return thingClass
|
|
|
|
|
|
def importThing(thingImport, parentSlot = None):
|
|
thing = newThing(
|
|
thingImport['__thingCategory__'], thingImport['__thingName__'])
|
|
thing.importFromXmlRpc(thingImport, parentSlot = parentSlot)
|
|
return thing
|
|
|
|
|
|
def makeApplicationId(id, role):
|
|
"""Compute the application ID string.
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*id*:
|
|
The server ID string.
|
|
|
|
*role*:
|
|
The server role String
|
|
|
|
Return the application ID string. Formaly, it is the dispatcher address
|
|
folowed by the given role.
|
|
|
|
Exception:
|
|
==========
|
|
|
|
*AssertionError*:
|
|
The server ID doesn't begin by 'glasnost://'.
|
|
|
|
*Standard Exception*:
|
|
The application ID folowing the protocol specifier is not valid.
|
|
|
|
"""
|
|
|
|
if role:
|
|
return '%s/%s' % (extractDispatcherId(id), role)
|
|
else:
|
|
return extractDispatcherId(id)
|
|
|
|
|
|
def makeHttpHostNameAndPort(httpHostName, httpPort):
|
|
if httpHostName is None and httpPort is None:
|
|
return None
|
|
elif httpPort is None:
|
|
return httpHostName
|
|
elif httpHostName is None:
|
|
return 'localhost:%s' % httpPort
|
|
else:
|
|
return '%s:%s' % (httpHostName, httpPort)
|
|
|
|
|
|
def makeHttpPathAndQuery(httpPath, httpQuery):
|
|
if httpPath is None and httpQuery is None:
|
|
return None
|
|
elif httpQuery is None:
|
|
return httpPath
|
|
elif httpPath is None:
|
|
return '/?%s' % httpQuery
|
|
else:
|
|
return '%s?%s' % (httpPath, httpQuery)
|
|
|
|
|
|
def makeHttpScriptDirectoryPath(httpScriptPath):
|
|
if httpScriptPath is None:
|
|
return '/'
|
|
splittedPath = httpScriptPath.split('/')
|
|
del splittedPath[-1]
|
|
path = '/'.join(splittedPath)
|
|
if not path or path[0] != '/':
|
|
path = '/' + path
|
|
if not path[-1] == '/':
|
|
path += '/'
|
|
return path
|
|
|
|
|
|
def makepassword(
|
|
length = 8,
|
|
allowChar = 'ABCDEFGHIJKLMNPQRSTUVWXYabcdefghijmnopqrstuvwxyz0123456789'):
|
|
"""Generate a random password.
|
|
|
|
@param length: length of the result string
|
|
@type length: int
|
|
@param CARAC: allowed charactere for the result string
|
|
@type allowChar: String
|
|
"""
|
|
g = random.Random()
|
|
result = ''
|
|
for i in range(length):
|
|
result += allowChar[g.randrange(len(allowChar))]
|
|
return result
|
|
|
|
|
|
def newThing(thingCategory, thingName, *arguments, **keywords):
|
|
"""Instantiate a new thing.
|
|
|
|
This is a generic method designed to instantiate any kind of thing.
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*thingCategory*:
|
|
The category name of the class to instantiate (i.e.: 'object').
|
|
|
|
*thingName*:
|
|
The class name to instantiate (i.e.: 'AdminPeople'). The class name is
|
|
linked to a category. Same class names could be used in differents
|
|
categories.
|
|
|
|
*\*arguments*:
|
|
The arguments sequence to pass to the class constructor.
|
|
|
|
*\*\*keywords*:
|
|
The keyword dictionnary to pass to the class contructor.
|
|
|
|
Return the wanted thing instance (category + class name).
|
|
|
|
"""
|
|
|
|
return getThingClass(thingCategory, thingName)(*arguments, **keywords)
|
|
|
|
|
|
def splitMultiId(id):
|
|
"""Return list of sub id
|
|
|
|
Keyword arguments:
|
|
==================
|
|
|
|
*id*:
|
|
The id to split
|
|
example: glasnost://host.tld/serverName/id0:id1:id2:id3
|
|
become: [glasnost://host.tld/serverName/id0, id1, id2, id3]
|
|
|
|
Return:
|
|
=======
|
|
list of all sub id, this list have one or more component.
|
|
"""
|
|
try:
|
|
assert id.startswith('glasnost://')
|
|
splittedId = id[11:].split('/',2)
|
|
assert len(splittedId) == 3
|
|
applicationHostNameAndPort, applicationRole, localId = splittedId
|
|
result = localId.split(':')
|
|
result[0] = 'glasnost://%s/%s/%s' % (applicationHostNameAndPort,
|
|
applicationRole, result[0])
|
|
except IndexError:
|
|
raise Exception('Malformed id = %s' % str(id))
|
|
except AssertionError:
|
|
raise Exception('Malformed id = %s' % str(id))
|
|
return result
|
|
|
|
|
|
def translation(domains, languages = None):
|
|
localeDirectoryPath = context.getVar('localeDirectoryPath')
|
|
translation = gettext.NullTranslations()
|
|
|
|
for domain in domains:
|
|
try:
|
|
trans = gettext.translation(domain, localeDirectoryPath, languages)
|
|
except IOError:
|
|
continue
|
|
if not hasattr(trans, '_catalog'):
|
|
continue
|
|
if translation.__class__.__name__ == 'NullTranslations':
|
|
translation = trans
|
|
continue
|
|
translation._catalog.update(trans._catalog)
|
|
|
|
return translation
|
|
|