Backup and restore depend on DS tasks succeeding, but they only waited
for the task to end and didn't check the result.
This patch adds a new helper, run_task, that runs a task, waits for it,
and checks the result. Backup and restore are changed to use this helper.
--
PetrĀ³
From a557d1e816280719404f7e2dbabad4f425b21e5e Mon Sep 17 00:00:00 2001
From: Petr Viktorin
Date: Tue, 18 Nov 2014 14:05:59 +0100
Subject: [PATCH] Raise an error when a critical DS task fails
The backup/restore ignored results of backup/restore task,
because the helper used does not check for errors.
Add an error-checking helper to installutils, and have
backup/restore use it.
The wait_for_task helper is moved to installutils as well. Its
return value is changed. (The return value was always ignored
in current code.)
---
ipaserver/install/installutils.py | 55 +++
ipaserver/install/ipa_backup.py | 19 ++
ipaserver/install/ipa_restore.py | 20 +++---
ipaserver/install/replication.py | 21 +--
4 files changed, 61 insertions(+), 54 deletions(-)
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index d3f09eccb161cc9371fa7c1109816f47194d9cb6..955fa348c6f519b5145288dd58cf8b8ffe997903 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -29,6 +29,7 @@
import traceback
import textwrap
from contextlib import contextmanager
+import time
from dns import resolver, rdatatype
from dns.exception import DNSException
@@ -46,6 +47,8 @@
from ipaplatform import services
from ipaplatform.paths import paths
+log = log_mgr.get_logger(__name__)
+
# Used to determine install status
IPA_MODULES = [
'httpd', 'kadmin', 'dirsrv', 'pki-cad', 'pki-tomcatd', 'install',
@@ -1035,3 +1038,55 @@ def load_external_cert(files, subject_base):
ca_file.flush()
return cert_file, ca_file
+
+
+def wait_for_task(conn, dn):
+"""Check task status
+
+Task is complete when the nsTaskExitCode attr is set.
+
+:return: the task's entry
+
+Usually you will want to call run_task, which checks the task's exit code
+"""
+assert isinstance(dn, DN)
+attrlist = [
+'nsTaskLog', 'nsTaskStatus', 'nsTaskExitCode', 'nsTaskCurrentItem',
+'nsTaskTotalItems']
+while True:
+entry = conn.get_entry(dn, attrlist)
+if entry.single_value.get('nsTaskExitCode'):
+break
+time.sleep(1)
+for log_item in entry.get('nsTaskLog', []):
+log.debug('DS task log: %s', log_item)
+return entry
+
+
+def run_task(conn, entry, task_name, raiseonerr=True):
+"""Run a DS task, wait for it to finish, check it completed successfully
+
+:param conn: connected LDAPClient
+:param entry: task to run, as an LDAP entry
+:param task_name: name to use in messages
+:param raiseonerr: if true (default), raise RuntimeError if the task failed
+"""
+dn = entry.dn
+try:
+conn.add_entry(entry)
+except Exception as e:
+raise admintool.ScriptError(
+'Unable to add %s task: %s' % (task_name, e))
+
+log.info("Waiting for %s task to finish", task_name)
+entry = wait_for_task(conn, dn)
+exit_code = int(entry.single_value['nsTaskExitCode'])
+if exit_code:
+result = entry.single_value.get('nsTaskStatus', 'no status')
+if raiseonerr:
+raise RuntimeError('%s task failed: %s: %s' % (
+task_name, exit_code, result))
+else:
+log.warn('%s task failed: %s: %s',
+ task_name, exit_code, result)
+log.info("%s task finished", task_name)
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 682b626e55b9ead5424369e1bab85109ff6b8207..4dc715cf880ee97bc0c27760e5ce3f082b0cef4f 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -35,7 +35,6 @@
from ipapython.config import IPAOptionParser
from ipapython.dn import DN
from ipaserver.install.dsinstance import realm_to_serverid, DS_USER
-from ipaserver.install.replication import wait_for_task
from ipaserver.install import installutils
from ipapython import ipaldap
from ipalib.session import ISO8601_DATETIME_FMT
@@ -403,14 +402,7 @@ def db2ldif(self, instance, backend, online=True):
}
)
-try:
-conn.add_entry(ent)
-except Exception, e:
-raise admintool.ScriptError('Unable to add LDIF task: %s'
-% e)
-
-self.log.info("Waiting for LDIF to finish")
-wait_for_task(conn, dn)
+installutils.run_task(conn, ent, 'LDIF export')
else:
args = ['%s/db2ldif' % self.__find_scripts_dir(instance),
'-r',
@@ -450,14 +442,7 @@ def db2bak(self, instance, online=True):
}
)
-try:
-conn.add_entry