This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository 389-ds-base.
commit fa2361ffc54719b14f2e0c304ee15940df229028
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Thu Jul 10 09:36:17 2014 -0400
Ticket 47845 - Add backup/restore/fixup tombstone tasks to lib389
Description: Add backup/restore/fixup tombstone tasks to lib389.
Also improved the task time stamp value to be always
unique.
https://fedorahosted.org/389/ticket/47845
Reviewed by: tbordaz(Thanks!!)
---
src/lib389/lib389/_constants.py | 13 +--
src/lib389/lib389/_entry.py | 6 +-
src/lib389/lib389/replica.py | 8 +-
src/lib389/lib389/tasks.py | 181 ++++++++++++++++++++++++++++++++++++++--
4 files changed, 189 insertions(+), 19 deletions(-)
diff --git a/src/lib389/lib389/_constants.py b/src/lib389/lib389/_constants.py
index 6fe9af9..c259ca8 100644
--- a/src/lib389/lib389/_constants.py
+++ b/src/lib389/lib389/_constants.py
@@ -93,11 +93,14 @@ DN_CHANGELOG = "cn=changelog5,%s" % DN_CONFIG
DN_LDBM = "cn=ldbm database,%s" % DN_PLUGIN
DN_CHAIN = "cn=chaining database,%s" % DN_PLUGIN
-DN_TASKS = "cn=tasks,%s" % DN_CONFIG
-DN_INDEX_TASK = "cn=index,%s" % DN_TASKS
-DN_MBO_TASK = "cn=memberOf task,%s" % DN_TASKS
-DN_EXPORT_TASK = "cn=export,%s" % DN_TASKS
-DN_IMPORT_TASK = "cn=import,%s" % DN_TASKS
+DN_TASKS = "cn=tasks,%s" % DN_CONFIG
+DN_INDEX_TASK = "cn=index,%s" % DN_TASKS
+DN_EXPORT_TASK = "cn=export,%s" % DN_TASKS
+DN_IMPORT_TASK = "cn=import,%s" % DN_TASKS
+DN_BACKUP_TASK = "cn=backup,%s" % DN_TASKS
+DN_RESTORE_TASK = "cn=restore,%s" % DN_TASKS
+DN_MBO_TASK = "cn=memberOf task,%s" % DN_TASKS
+DN_TOMB_FIXUP_TASK = "cn=fixup tombstones,%s" % DN_TASKS
RDN_REPLICA = "cn=replica"
diff --git a/src/lib389/lib389/_entry.py b/src/lib389/lib389/_entry.py
index f993a36..b97b6e7 100644
--- a/src/lib389/lib389/_entry.py
+++ b/src/lib389/lib389/_entry.py
@@ -118,14 +118,16 @@ class Entry(object):
return val.lower() in [x.lower() for x in self.data.get(name)]
def setValue(self, name, *value):
- """Value passed in may be a single value, several values, or a
single sequence.
+ """
+ Value passed in may be a single value, several values, or a single sequence.
For example:
ent.setValue('name', 'value')
ent.setValue('name', 'value1', 'value2', ...,
'valueN')
ent.setValue('name', ['value1', 'value2', ...,
'valueN'])
ent.setValue('name', ('value1', 'value2', ...,
'valueN'))
Since *value is a tuple, we may have to extract a list or tuple from that
- tuple as in the last two examples above"""
+ tuple as in the last two examples above
+ """
if isinstance(value[0], list) or isinstance(value[0], tuple):
self.data[name] = value[0]
else:
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
index 00d765f..5357207 100644
--- a/src/lib389/lib389/replica.py
+++ b/src/lib389/lib389/replica.py
@@ -173,7 +173,7 @@ class Replica(object):
InvalidArgumentError: If missing mandatory parameter
'''
-
+
# No properties provided
if len(properties) == 0:
return
@@ -198,10 +198,10 @@ class Replica(object):
# that is fine, now set the values
for prop in properties:
val = rawProperty(prop)
-
+
# for Entry update it is a replace
replica_entry.update({REPLICA_PROPNAME_TO_ATTRNAME[val]:
properties[prop]})
-
+
return
# If it provides the suffix or the replicaDN, replica.list will
@@ -213,7 +213,7 @@ class Replica(object):
else:
raise ValueError("invalid suffix: %s" % suffix)
-
+
# build the MODS
mods = []
for prop in properties:
diff --git a/src/lib389/lib389/tasks.py b/src/lib389/lib389/tasks.py
index f61f935..819f7c5 100644
--- a/src/lib389/lib389/tasks.py
+++ b/src/lib389/lib389/tasks.py
@@ -8,19 +8,23 @@ from lib389._constants import *
from lib389.properties import *
import ldap
import time
+import os.path
class Tasks(object):
proxied_methods = 'search_s getEntry'.split()
+
def __init__(self, conn):
"""@param conn - a DirSrv instance"""
self.conn = conn
self.log = conn.log
+
def __getattr__(self, name):
if name in Tasks.proxied_methods:
return DirSrv.__getattr__(self.conn, name)
+
def checkTask(self, entry, dowait=False):
'''check task status - task is complete when the nsTaskExitCode attr
is set
return a 2 tuple (true/false,code) first is false if task is running, true if
@@ -44,6 +48,7 @@ class Tasks(object):
break
return (done, exitCode)
+
def importLDIF(self, suffix=None, benamebase=None, input_file=None, args=None):
'''
Import from a LDIF format a given 'suffix' (or 'benamebase' that
stores that suffix).
@@ -68,12 +73,15 @@ class Tasks(object):
# Checking the parameters
if not benamebase and not suffix:
raise ValueError("Specify either bename or suffix")
-
+
if not input_file:
raise ValueError("input_file is mandatory")
-
+
+ if not os.path.exists(input_file):
+ raise ValueError("Import file (%s) does not exist" % input_file)
+
# Prepare the task entry
- cn = "import" + str(int(time.time()))
+ cn = "import_" + time.strftime("%m%d%Y_%H%M%S",
time.localtime())
dn = "cn=%s,%s" % (cn, DN_IMPORT_TASK)
entry = Entry(dn)
entry.setValues('objectclass', 'top',
'extensibleObject')
@@ -103,6 +111,7 @@ class Tasks(object):
cn, input_file))
return exitCode
+
def exportLDIF(self, suffix=None, benamebase=None, output_file=None, args=None):
'''
Export in a LDIF format a given 'suffix' (or 'benamebase' that
stores that suffix).
@@ -134,7 +143,7 @@ class Tasks(object):
raise ValueError("output_file is mandatory")
# Prepare the task entry
- cn = "export%d" % time.time()
+ cn = "export_" + time.strftime("%m%d%Y_%H%M%S",
time.localtime())
dn = "cn=%s,%s" % (cn, DN_EXPORT_TASK)
entry = Entry(dn)
entry.update({
@@ -166,6 +175,111 @@ class Tasks(object):
return exitCode
+ def db2bak(self, backup_dir=None, args=None):
+ '''
+ Perform a backup by creating a db2bak task
+
+ @param backup_dir - backup directory
+ @param args - is a dictionary that contains modifier of the task
+ wait: True/[False] - If True, waits for the completion of the task
before to return
+
+ @return exit code
+
+ @raise ValueError: if bename name does not exist
+ '''
+
+ # Checking the parameters
+ if not backup_dir:
+ raise ValueError("You must specify a backup directory.")
+ if not os.path.exists(backup_dir):
+ raise ValueError("Backup file (%s) does not exist" % backup_dir)
+
+ # build the task entry
+ cn = "backup_" + time.strftime("%m%d%Y_%H%M%S",
time.localtime())
+ dn = "cn=%s,%s" % (cn, DN_BACKUP_TASK)
+ entry = Entry(dn)
+ entry.update({
+ 'objectclass': ['top', 'extensibleObject'],
+ 'cn': cn,
+ 'nsArchiveDir': backup_dir,
+ 'nsDatabaseType': 'ldbm database'
+ })
+
+ # start the task and possibly wait for task completion
+ try:
+ self.conn.add_s(entry)
+ except ldap.ALREADY_EXISTS:
+ self.log.error("Fail to add the backup task (%s)" % dn)
+ return -1
+
+ exitCode = 0
+ if args and args.get(TASK_WAIT, False):
+ (done, exitCode) = self.conn.tasks.checkTask(entry, True)
+
+ if exitCode:
+ self.log.error("Error: backup task %s exited with %d" % (cn,
exitCode))
+ else:
+ self.log.info("Backup task %s completed successfully" % (cn))
+ return exitCode
+
+
+ def bak2db(self, bename=None, backup_dir=None, args=None):
+ '''
+ Restore a backup by creating a bak2db task
+
+ @param bename - 'commonname'/'cn' of the backend (e.g.
'userRoot')
+ @param backup_dir - backup directory
+ @param args - is a dictionary that contains modifier of the task
+ wait: True/[False] - If True, waits for the completion of the task
before to return
+
+ @return exit code
+
+ @raise ValueError: if bename name does not exist
+ '''
+
+ # Checking the parameters
+ if not backup_dir:
+ raise ValueError("You must specify a backup directory")
+ if not os.path.exists(backup_dir):
+ raise ValueError("Backup file (%s) does not exist" % backup_dir)
+
+ # If a backend name was provided then verify it
+ if bename:
+ ents = self.conn.mappingtree.list(bename=bename)
+ if len(ents) != 1:
+ raise ValueError("invalid backend name: %s" % bename)
+
+ # build the task entry
+ cn = "restore_" + time.strftime("%m%d%Y_%H%M%S",
time.localtime())
+ dn = "cn=%s,%s" % (cn, DN_RESTORE_TASK)
+ entry = Entry(dn)
+ entry.update({
+ 'objectclass': ['top', 'extensibleObject'],
+ 'cn': cn,
+ 'nsArchiveDir': backup_dir,
+ 'nsDatabaseType': 'ldbm database'
+ })
+ if bename:
+ entry.update({'nsInstance': bename})
+
+ # start the task and possibly wait for task completion
+ try:
+ self.conn.add_s(entry)
+ except ldap.ALREADY_EXISTS:
+ self.log.error("Fail to add the backup task (%s)" % dn)
+ return -1
+
+ exitCode = 0
+ if args and args.get(TASK_WAIT, False):
+ (done, exitCode) = self.conn.tasks.checkTask(entry, True)
+
+ if exitCode:
+ self.log.error("Error: restore task %s exited with %d" % (cn,
exitCode))
+ else:
+ self.log.info("Restore task %s completed successfully" % (cn))
+ return exitCode
+
+
def reindex(self, suffix=None, benamebase=None, attrname=None, args=None):
'''
Reindex a 'suffix' (or 'benamebase' that stores that suffix) for
a given 'attrname'.
@@ -201,7 +315,7 @@ class Tasks(object):
suffix = ents[0].getValue(attr_suffix)
entries_backend = self.conn.backend.list(suffix=suffix)
- cn = "index_%s_%d" % (attrname, time.time())
+ cn = "index_%s_%s" % (attrname,
time.strftime("%m%d%Y_%H%M%S", time.localtime()))
dn = "cn=%s,%s" % (cn, DN_INDEX_TASK)
entry = Entry(dn)
entry.update({
@@ -231,6 +345,7 @@ class Tasks(object):
cn))
return exitCode
+
def fixupMemberOf(self, suffix=None, benamebase=None, filt=None, args=None):
'''
Trigger a fixup task on 'suffix' (or 'benamebase' that stores
that suffix) related to the
@@ -268,7 +383,7 @@ class Tasks(object):
suffix = ents[0].getValue(attr)
- cn = "fixupmemberof%d" % time.time()
+ cn = "fixupmemberof_" + time.strftime("%m%d%Y_%H%M%S",
time.localtime())
dn = "cn=%s,%s" % (cn, DN_MBO_TASK)
entry = Entry(dn)
entry.setValues('objectclass', 'top',
'extensibleObject')
@@ -278,11 +393,15 @@ class Tasks(object):
entry.setValues('filter', filt)
# start the task and possibly wait for task completion
- self.conn.add_s(entry)
+ try:
+ self.conn.add_s(entry)
+ except ldap.ALREADY_EXISTS:
+ self.log.error("Fail to add the memberOf fixup task")
+ return -1
+
exitCode = 0
if args and args.get(TASK_WAIT, False):
(done, exitCode) = self.conn.tasks.checkTask(entry, True)
-
if exitCode:
self.log.error("Error: fixupMemberOf task %s for basedn %s exited with
%d" % (cn, suffix, exitCode))
@@ -290,3 +409,49 @@ class Tasks(object):
self.log.info("fixupMemberOf task %s for basedn %s completed
successfully" % (cn, suffix))
return exitCode
+
+ def fixupTombstones(self, bename=None, args=None):
+ '''
+ Trigger a tombstone fixup task on the specified backend
+
+ @param bename - 'commonname'/'cn' of the backend (e.g.
'userRoot'). Optional.
+ @param args - is a dictionary that contains modifier of the task
+ wait: True/[False] - If True, waits for the completion of the task
before to return
+
+ @return exit code
+
+ @raise ValueError: if bename name does not exist
+ '''
+
+ if not bename:
+ bename = DEFAULT_BENAME
+
+ # Verify the backend name
+ if bename:
+ ents = self.conn.mappingtree.list(bename=bename)
+ if len(ents) != 1:
+ raise ValueError("invalid backend name: %s" % bename)
+
+ cn = "fixupTombstone_" + time.strftime("%m%d%Y_%H%M%S",
time.localtime())
+ dn = "cn=%s,%s" % (cn, DN_TOMB_FIXUP_TASK)
+ entry = Entry(dn)
+ entry.setValues('objectclass', 'top',
'extensibleObject')
+ entry.setValues('cn', cn)
+ entry.setValues('backend', bename)
+
+ # start the task and possibly wait for task completion
+ try:
+ self.conn.add_s(entry)
+ except ldap.ALREADY_EXISTS:
+ self.log.error("Fail to add the fixup tombstone task")
+ return -1
+
+ exitCode = 0
+ if args and args.get(TASK_WAIT, False):
+ (done, exitCode) = self.conn.tasks.checkTask(entry, True)
+
+ if exitCode:
+ self.log.error("Error: tombstone fixup task %s for backend %s exited
with %d" % (cn, bename, exitCode))
+ else:
+ self.log.info("tombstone fixup task %s for backend %s completed
successfully" % (cn, bename))
+ return exitCode
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.