[Dev OpenGP] [67] Core and etree are finished, plugins in progress. |
[ Thread Index |
Date Index
| More opengp.tuxfamily.org/development Archives
]
Revision: 67
Author: nicolaf
Date: 2009-03-19 17:11:10 +0100 (Thu, 19 Mar 2009)
Log Message:
-----------
Core and etree are finished, plugins in progress.
Added comments.
Modified Paths:
--------------
trunk/src/lib/ogp/core/ogpcore.py
trunk/src/lib/ogp/core/ogpldapconsts.py
trunk/src/lib/ogp/etree/elementmethods.py
trunk/src/lib/ogp/etree/ogpxmlconsts.py
trunk/src/lib/ogp/plugins/__init__.py
trunk/src/lib/ogp/plugins/plugin.py
trunk/src/lib/ogp/plugins/test/test.py
trunk/src/lib/ogp/plugins/test/test.pyc
trunk/src/tests/etree-test.py
trunk/src/tests/ogp-test.py
Removed Paths:
-------------
trunk/src/lib/ogp/plugins/abstractmethod.py
trunk/src/lib/ogp/plugins/metaclass.py
Modified: trunk/src/lib/ogp/core/ogpcore.py
===================================================================
--- trunk/src/lib/ogp/core/ogpcore.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/core/ogpcore.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -8,13 +8,23 @@
from lxml.etree import *
from ogp.etree import *
-
class OgpCore(object):
-
+ """
+ Provides LDAP acces methods
+ Initialisation: OgpCore(uri, dn, passwd, certs)
+ uri: ldap://host:port
+ dn: user dn
+ passwd: user password
+ certs: path to certs files (.pem)
+ Usage: OgpCore.getInstance().[method]([args])
+ """
+
__instance = None
def __init__(self, uri, dn=None, passwd=None, certs=None):
- """ Create singleton instance """
+ """
+ Creates singleton instance
+ """
# Check whether we already have an instance
if OgpCore.__instance is None:
# Create and remember instance
@@ -23,14 +33,17 @@
self.__dict__['OgpCore__instance'] = OgpCore.__instance
def __getattr__(self, attr):
- """ Delegate access to implementation """
+ # Delegate access to implementation
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
- """ Delegate access to implementation """
+ # Delegate access to implementation
return setattr(self.__instance, attr, value)
def getInstance():
+ """
+ Returns the core unique instance
+ """
return OgpCore.__instance
getInstance = staticmethod(getInstance)
@@ -38,19 +51,25 @@
def __init__(self, uri, dn=None, passwd=None, certs=None):
"""
- Initlialize connection to LDAP server.
+ Initlializes connection to LDAP server.
uri: ldap://host:port
- dn: usdr dn
+ dn: user dn
passwd: user password
- certs: path to cert file (.pem)
+ certs: path to certs files (.pem)
"""
self.l = ldap.initialize(uri)
self.l.simple_bind_s(dn, passwd)
def __del__(self):
+ #close LDAP connection before deleting the object
self.l.unbind_s()
def createOU(self, dn, description=None):
+ """
+ Creates an oGPOrganizationalUnit LDAP object and initializes it.
+ dn: the distinguished name of the targeted object
+ description (optionnal): an optional description of the object
+ """
attrs = {}
attrs['objectclass'] = OgpLDAPConsts.OBJECTCLASS_OU
attrs[OgpLDAPConsts.ATTR_OGPSOA] = OgpLDAPConsts.VALUE_OGPSOA
@@ -59,21 +78,12 @@
attrs[OgpLDAPConsts.ATTR_CONFIG] = OgpLDAPConsts.VALUE_CONFIG
self.__add(dn, attrs)
- def deleteDN(self, dn):
- #TODO
- #self.__delete(dn)
- pass
-
-
- def __add(self, dn, attrs):
- ldif = modlist.addModlist(attrs)
- self.l.add_s(dn,ldif)
-
- def __delete(self, dn):
- #TODO
- self.l.delete_s(dn)
-
def createMachine(self, dn, others={}):
+ """
+ Creates an oGPComputer LDAP object and initializes it.
+ dn: the distinguished name of the targeted object
+ others (optionnal) : other LDAP attributes
+ """
attrs = others
attrs['objectClass'] = OgpLDAPConsts.OBJECTCLASS_MACHINE
attrs[OgpLDAPConsts.ATTR_OGPSOA] = OgpLDAPConsts.VALUE_OGPSOA
@@ -91,10 +101,44 @@
attrs[OgpLDAPConsts.ATTR_CONFIG] = OgpLDAPConsts.VALUE_CONFIG
self.__add(dn, attrs)
- def __pullConf(self, dn):
- return fromstring(self.l.search_s(dn, ldap.SCOPE_BASE, attrlist=[OgpLDAPConsts.ATTR_CONFIG])[0][1][OgpLDAPConsts.ATTR_CONFIG][0], OGP_PARSER)
+ def deleteDN(self, dn, fullTree=False):
+ """
+ Deletes an LDAP object
+ dn: the distinguished name of the targeted object
+ fullTree: if set to True, deletion is recursive
+ """
+ if fullTree: #recursively delete direct children before deleting dn itself
+ tree = self.l.search_s(dn, ldap.SCOPE_SUBTREE, '(objectclass=*)' ,[''])
+ for e in tree:
+ if len(str2dn(e[0])) == (len(str2dn(dn)) + 1):
+ self.deleteDN(e[0], fullTree=True)
+ self.__delete(dn)
+ def pullAttributes(self, dn, attrs):
+ """
+ Returns a dict containing the requested attributes
+ dn: the distinguished name of the targeted object
+ attrs: a list containing the requested attributes' names
+ """
+ return self.l.search_s(dn, ldap.SCOPE_BASE, attrlist=attrs)[0][1]
+
+ def pushDescription(self, dn, description):
+ """
+ Sets the OgpLDAPConsts.ATTR_DESCRIPTION LDAP attribute
+ dn: the distinguished name of the targeted object
+ description: the description
+ """
+ mods = [(ldap.MOD_REPLACE, OgpLDAPConsts.ATTR_DESCRIPTION, description)]
+ self.__modify(dn, mods)
+
def pullPluginConf(self, dn, pluginName, fullTree=False):
+ """
+ Retrieves the XML tree containing the configuration for a given plugin and a given DN.
+ The root of the returned XML tree is <plugin name="[pluginName]">.
+ dn: the distinguished name of the targeted object
+ pluginName: the targeted plugin name
+ fullTree: if set to True, merges the conf from the baseDN up to the given DN
+ """
pConf = None
if fullTree:
dn=str2dn(dn)
@@ -121,8 +165,63 @@
break
return pConf
- def pushPluginConf(self, dn, conf):
- pass
+ def pushPluginConf(self, dn, pluginConf):
+ """
+ Stores the plugin configuration in the LDAP and updates the SOA.
+ dn: the distinguished name of the targeted object
+ pluginConf: a XML tree reprensenting the XML configuration. Its root must be <plugin name="[pluginName]">
+ """
+ #replace current <plugin name="..." /> entry
+ pluginName = pluginConf.get(OgpXmlConsts.ATTR_PLUGIN_NAME)
+ currentConf = self.__pullConf(dn)
+ for p in currentConf:
+ if p.get(OgpXmlConsts.ATTR_PLUGIN_NAME) == pluginName:
+ currentConf.remove(p)
+ break
+ currentConf.append(pluginConf)
+ strConf = currentConf.toString()
+ #get SOA
+ currentSOA = self.__pullSOA(dn)
+ #commit Changes
+ mods = [
+ (ldap.MOD_REPLACE, OgpLDAPConsts.ATTR_CONFIG, strConf),
+ (ldap.MOD_REPLACE, OgpLDAPConsts.ATTR_OGPSOA, str(currentSOA + 1))
+ ]
+ self.__modify(dn, mods)
+
def pullSOAs(self, dn):
- pass
+ """
+ Returns a dict containing all the oGPSOA attributes from the baseDN up to the targeted object.
+ dn: the distinguished name of the targeted object
+ """
+ SOAs = {}
+ dn=str2dn(dn)
+ dn.reverse()
+ loopDn=[]
+ for obj in dn:
+ loopDn.insert(0, obj)
+ print dn2str(loopDn)
+ try:
+ SOAs[dn2str(loopDn)] = self.__pullSOA(dn2str(loopDn))
+ print dn2str(loopDn) + SOAs[dn2str(loopDn)]
+ except:
+ pass
+ return SOAs
+
+ def __add(self, dn, attrs):
+ ldif = modlist.addModlist(attrs)
+ self.l.add_s(dn,ldif)
+
+ def __modify(self, dn, mods):
+ self.l.modify_s(dn,mods)
+
+ def __delete(self, dn):
+ self.l.delete_s(dn)
+
+ def __pullConf(self, dn):
+ return fromstring(self.pullAttributes(dn,[OgpLDAPConsts.ATTR_CONFIG])[OgpLDAPConsts.ATTR_CONFIG], OGP_PARSER)
+
+ def __pullSOA(self, dn):
+ return int(self.pullAttributes(dn, [OgpLDAPConsts.ATTR_OGPSOA])[OgpLDAPConsts.ATTR_OGPSOA])
+
Modified: trunk/src/lib/ogp/core/ogpldapconsts.py
===================================================================
--- trunk/src/lib/ogp/core/ogpldapconsts.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/core/ogpldapconsts.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -2,6 +2,10 @@
# -*- coding: utf-8 -*
class OgpLDAPConsts:
+ """
+ Provides LDAP object classes and attributes names
+ and attributes default values.
+ """
OBJECTCLASS_OU = "oGPOrganizationalUnit"
OBJECTCLASS_MACHINE = "oGPComputer"
Modified: trunk/src/lib/ogp/etree/elementmethods.py
===================================================================
--- trunk/src/lib/ogp/etree/elementmethods.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/etree/elementmethods.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -6,19 +6,35 @@
from ogpxmlconsts import *
class OgpElement(ElementBase):
-
+ """
+ lxml Element class providing redefined secure methods, compliant with the merge algorithm :
+ def delElements(self):
+ def append(self, newChild):
+ def insert(self, index, newChild):
+ def extend(self, elements):
+ def set(self, name, value):
+ def merge(self, peer):
+ def toString(self, xsl=None, params=None):
+ attributes = property(__getAttributes)
+ You should NOT use other methods or modify attributes because it may crash the merge algorithm.
+ """
+
def __setattr__(self, item, value):
+ """
+ If target attribute is text, deletes all subelements.
+ If targeted attribue is tail, forces value to None
+ """
if item == "text" and value is not None:
- #print "Deleting all subelements..."
- #print "self.tag: " + self.tag + " text: " + value
self.delElements()
if item == "tail":
- #print "Tail must be none"
value = None
ElementBase.__setattr__(self, item, value)
def __getAttributes(self):
+ """
+ Returns the attributes dict(), without the OgpXmlConsts.ATTR_BLOCK ('block') attribute.
+ """
res = dict()
for key in self.attrib:
if key != OgpXmlConsts.ATTR_BLOCK:
@@ -28,7 +44,7 @@
def __getBlocking(self):
"""
- A more convenient way to access the 'block' special attribute.
+ A more convenient way to access the OgpXmlConsts.ATTR_BLOCK ('block') special attribute.
"""
b = self.get(OgpXmlConsts.ATTR_BLOCK)
if b is None:
@@ -38,7 +54,7 @@
def __setBlocking(self, blocking):
"""
- Sets the 'block' special attribute.
+ Sets the OgpXmlConsts.ATTR_BLOCK ('block') special attribute.
"""
assert isinstance(blocking, bool)
if blocking:
@@ -52,7 +68,7 @@
def delElements(self):
"""
- Removes any Text or CDATASection child
+ Removes any child
"""
for e in self:
self.remove(e)
@@ -69,9 +85,8 @@
def append(self, newChild):
"""
- Works as the standard function, but :
- - If newChild is an Element, checks unicity before adding, and deletes every Text or CDATASection
- - If newChild is a Text , deletes every Element child, adds it and the normalize().
+ Works as the standard function, but if newChild is an Element,
+ checks unicity before adding, and deletes text
"""
assert isinstance(newChild, OgpElement)
if (not self.__checkUnicity(newChild)):raise OgpXmlError('append: element is not unique')
@@ -80,9 +95,8 @@
def insert(self, index, newChild):
"""
- Works as the standard function, but :
- - If newChild is an Element, checks unicity before adding, and deletes every Text or CDATASection
- - If newChild is a Text , deletes every Element child, adds it and the normalize().
+ Works as the standard function, but if newChild is an Element, but
+ checks unicity before adding, and deletes text.
"""
assert isinstance(newChild, OgpElement)
assert isinstance(index, int)
@@ -92,12 +106,21 @@
ElementBase.insert(self, index, element)
def extend(self, elements):
+ """
+ Works as the standard function, but if newChild is an Element, but
+ checks unicity before adding, and deletes text.
+ """
for element in elements:
assert isinstance(element, OgpElement)
if (not self.__checkUnicity(element)):raise OgpXmlError('extend: element is not unique')
- ElementBase.append(self, element)
+ self.text = None
+ ElementBase.extend(self, elements)
def set(self, name, value):
+ """
+ Works as the standard function, but if newChild is an Element, but
+ checks that self will still be unique after setting the attribute.
+ """
assert isinstance(name, str)
assert isinstance(value, str)
#Computation of new attribute list
@@ -114,7 +137,7 @@
def merge(self, peer):
"""
- Merges self with peer. peer is considered as the "child" conf (LDAP speaking), so that it has precendence on self.
+ Merges self with peer. Peer is considered as the "child" conf (LDAP speaking), so that it has precendence on self.
See plugin documentation for further details on the algorithm
"""
assert isinstance(peer, OgpElement)
@@ -194,6 +217,9 @@
return str(transform(self), params)
class OgpXmlError(Exception):
+ """
+ OGP XML error class.
+ """
def __init__(self, value):
assert isinstance(value, str)
self.value = value
@@ -202,6 +228,8 @@
return repr("OgpXmlError: " + self.value)
class OgpElementClassLookup(PythonElementClassLookup):
+ """
+ Standard OgpElement "factory"
+ """
def lookup(self, document, element):
- return OgpElement # defined elsewhere
-
+ return OgpElement
Modified: trunk/src/lib/ogp/etree/ogpxmlconsts.py
===================================================================
--- trunk/src/lib/ogp/etree/ogpxmlconsts.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/etree/ogpxmlconsts.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -1,4 +1,7 @@
class OgpXmlConsts:
+ """
+ Provides XML tags and attributes names
+ """
ATTR_BLOCK = "block"
ATTR_ID = "id"
ATTR_PLUGIN_NAME = "name"
Modified: trunk/src/lib/ogp/plugins/__init__.py
===================================================================
--- trunk/src/lib/ogp/plugins/__init__.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/plugins/__init__.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -7,10 +7,12 @@
from os import listdir
from imp import *
from sys import stderr
+
+# Load plugins
path = dirname(resource_filename(__name__, '__init.py__'))
for d in listdir(path):
if isdir(join(path,d)):
try:
load_package('ogp.plugins.' + d,join(path,d))
except:
- stderr.write("ogp.plugins warning: failed to load plugin '" + d + "'.")
+ stderr.write("ogp.plugins warning: failed to load plugin '" + d + "'.\n")
Deleted: trunk/src/lib/ogp/plugins/abstractmethod.py
===================================================================
--- trunk/src/lib/ogp/plugins/abstractmethod.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/plugins/abstractmethod.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -1,52 +0,0 @@
-# See http://code.activestate.com/recipes/266468/ for further details
-#
-# Note for further versions: http://docs.python.org/library/abc.html
-# Abstract base classes will be supported in Python 2.6
-
-
-class AbstractMethod (object):
- """Defines a class to create abstract methods
-
- @example:
- class Foo:
- foo = AbstractMethod('foo')
- """
-
- def __init__(self, func):
- """Constructor
-
- @params func: name of the function (used when raising an
- exception).
- @type func: str
- """
- self._function = func
-
- def __get__(self, obj, type):
- """Get callable object
-
- @returns An instance of AbstractMethodHelper.
-
- This trickery is needed to get the name of the class for which
- an abstract method was requested, otherwise it would be
- sufficient to include a __call__ method in the AbstractMethod
- class itself.
- """
- return self.AbstractMethodHelper(self._function, type)
-
-class AbstractMethodHelper (object):
- """Abstract method helper class
-
- An AbstractMethodHelper instance is a callable object that
- represents an abstract method.
- """
- def __init__(self, func, cls):
- self._function = func
- self._class = cls
-
- def __call__(self, *args, **kwargs):
- """Call abstract method
-
- Raises a TypeError, because abstract methods can not be
- called.
- """
- raise TypeError('Abstract method `' + self._class.__name__ + '.' + self._function + '\' called')
Deleted: trunk/src/lib/ogp/plugins/metaclass.py
===================================================================
--- trunk/src/lib/ogp/plugins/metaclass.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/plugins/metaclass.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -1,51 +0,0 @@
-# For more informations, see http://code.activestate.com/recipes/266468/
-
-from abstractmethod import AbstractMethod
-
-class Metaclass (type):
-
- def __init__(cls, name, bases, *args, **kwargs):
- """Configure a new class
-
- @param cls: Class object
- @param name: Name of the class
- @param bases: All base classes for cls
- """
- super(Metaclass, cls).__init__(cls, name, bases, *args, **kwargs)
-
- # Detach cls.new() from class Metaclass, and make it a method
- # of cls.
- cls.__new__ = staticmethod(cls.new)
-
- # Find all abstract methods, and assign the resulting list to
- # cls.__abstractmethods__, so we can read that variable when a
- # request for allocation (__new__) is done.
- abstractmethods = []
- ancestors = list(cls.__mro__)
- ancestors.reverse() # Start with __builtin__.object
- for ancestor in ancestors:
- for clsname, clst in ancestor.__dict__.items():
- if isinstance(clst, AbstractMethod):
- abstractmethods.append(clsname)
- else:
- if clsname in abstractmethods:
- abstractmethods.remove(clsname)
-
- abstractmethods.sort()
- setattr(cls, '__abstractmethods__', abstractmethods)
-
- def new(self, cls, *args, **kwargs):
- """Allocator for class cls
-
- @param self: Class object for which an instance should be
- created.
-
- @param cls: Same as self.
- """
- if len(cls.__abstractmethods__):
- raise NotImplementedError('Can\'t instantiate class `' + \
- cls.__name__ + '\';\n' + \
- 'Abstract methods: ' + \
- ", ".join(cls.__abstractmethods__))
-
- return object.__new__(self)
Modified: trunk/src/lib/ogp/plugins/plugin.py
===================================================================
--- trunk/src/lib/ogp/plugins/plugin.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/plugins/plugin.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -1,20 +1,28 @@
#!/usr/bin/python
# -*- coding: utf-8 -*
-from metaclass import Metaclass
-from abstractmethod import AbstractMethod
-
class Plugin(object):
- #__metaclass__ = Metaclass
+ """
+ Provides plugins' base class and plugin registration mechanism
+ """
def __init__(self, dn):
self.__dn = dn
+ name = None # the plugin name
__registeredPlugins = dict()
+
def __getPluginFromName(name):
+ """
+ returns a plugin class from a name.
+ """
return Plugin.__registeredPlugins[name]
getPluginFromName = staticmethod(__getPluginFromName)
def __registerPlugin(pluginClass):
+ """
+ Registers a plugin class.
+ Plugins should register themselves in their __init__.py using Plugin.registerPlugin([pluginClass]).
+ """
try:
Plugin.__registeredPlugins[pluginClass.name]
raise OgpPluginError("registerPlugin: duplicated plugin name '" + pluginClass.name + "'")
@@ -24,46 +32,73 @@
registerPlugin = staticmethod(__registerPlugin)
def __getRegisteredPlugins():
+ """
+ Returns a dict() containing all the registered plugin classes
+ """
return Plugin.__registeredPlugins.copy()
getRegisteredPlugins = staticmethod(__getRegisteredPlugins)
- # Abstract methods
- #mergeDescription = AbstractMethod('mergeDescription')
-
- name = None
-
- def installConf(self):
- pass
-
- def help(self, cmd):
- pass
-
- def runCommand(self, argv):
- pass
-
def update(self):
"""
- Commit changes
+ Commit changes to LDAP
"""
pass
def cancel(self):
"""
- Do not commit and delete changes.
+ Do not commit and discard changes.
"""
pass
+
+ # Abstract methods
+ def installConf(self):
+ """
+ Computes the configuration files and install them
+ """
+ raise NotImplementedError('This method should be overriden in derived classes.')
+
+ def help(self, cmdName=None):
+ """
+ provides informations about the plugin user interface
+ plugin.help() should return all available commands as a dict {cmdName: description}
+ plugin.help(cmdName) should return all available arguments as a dict {argName: description}
+ """
+ raise NotImplementedError('This method should be overriden in derived classes.')
+ def runCommand(self, cmdName, argv):
+ """
+ runs a command on the conf
+ Usage:
+ plugin.runCommand(cmdName, argv)
+ where argv is a dict {argName: argVal}
+ """
+ raise NotImplementedError('This method should be overriden in derived classes.')
+
def pullFile(self, file, fullTree=False):
- pass
+ """
+ builds the content of a file from the XML tree, for preview purposes.
+ Arguments:
+ file : the logical name of the targeted file
+ fullTree=False: if set to true, merges the conf from the baseDN up to the current DN before building
+ """
+ raise NotImplementedError('This method should be overriden in derived classes.')
def pushFile(self, file, content):
- pass
+ """
+ builds XML configuration from a string content and loads it in the corresponding <file> Element
+ Arguments:
+ file : the logical name of the targeted file
+ content: the content of the file
+ """
+ raise NotImplementedError('This method should be overriden in derived classes.')
class OgpPluginError(Exception):
+ """
+ OGP plugin error class.
+ """
def __init__(self, value):
assert isinstance(value, str)
self.value = value
def __str__(self):
- return repr("OgpXmlError: " + self.value)
-
+ return repr("OgpPluginError: " + self.value)
Modified: trunk/src/lib/ogp/plugins/test/test.py
===================================================================
--- trunk/src/lib/ogp/plugins/test/test.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/lib/ogp/plugins/test/test.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -5,3 +5,5 @@
class Test(Plugin):
name = "test"
+ def test(self):
+ print "toto" + self.name
Modified: trunk/src/lib/ogp/plugins/test/test.pyc
===================================================================
(Binary files differ)
Modified: trunk/src/tests/etree-test.py
===================================================================
--- trunk/src/tests/etree-test.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/tests/etree-test.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -1,7 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*
from lxml.etree import *
-#from DomElementMethods import *
from ogp.etree import *
obj = Element("rootelement")
Modified: trunk/src/tests/ogp-test.py
===================================================================
--- trunk/src/tests/ogp-test.py 2009-03-19 16:06:32 UTC (rev 66)
+++ trunk/src/tests/ogp-test.py 2009-03-19 16:11:10 UTC (rev 67)
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*
from ogp.core import *
+from ogp.etree import *
uri = "ldap://localhost:389"
dn = "cn=admin,dc=nodomain"
@@ -22,5 +23,9 @@
toto = OgpCore.getInstance()
-#toto.createOU("ou=test2,ou=test,dc=nodomain")
-print toto.pullPluginConf("ou=test2,ou=test,dc=nodomain", "test", True).toString()
+#toto.createOU("ou=test,dc=nodomain")
+#print toto.pullPluginConf("ou=test2,ou=test,dc=nodomain", "test", True).toString()
+#conf = fromstring('<plugin name="test">test pushConf le retour</plugin>', OGP_PARSER)
+#toto.pushPluginConf("ou=test2,ou=test,dc=nodomain", conf)
+#toto.deleteDN("dc=nodomain")
+print toto.pushDescription("ou=test,dc=nodomain", "test")