Enable and start oddjobd after ipa-restore if it's not running.

If after ipa-restore the service oddjobd is not running,
domain-level1 replica installation will fail during
ipa-replica-conncheck because this step is using oddjob
to start the process ipa-replica-conncheck on the master.

This patch fixes it. Also added regression test.

https://pagure.io/freeipa/issue/7234

Reviewed-By: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Aleksei Slaikovskii 2018-01-16 10:24:30 +01:00 committed by Christian Heimes
parent e6c707b168
commit 93b7c40158
4 changed files with 116 additions and 36 deletions

View File

@ -423,6 +423,12 @@ class Restore(admintool.AdminTool):
logger.info('Restarting SSSD')
sssd = services.service('sssd', api)
sssd.restart()
logger.info('Restarting oddjobd')
oddjobd = services.service('oddjobd', api)
if not oddjobd.is_enabled():
logger.info("Enabling oddjobd")
oddjobd.enable()
oddjobd.start()
http.remove_httpd_ccaches()
# have the daemons pick up their restored configs
run([paths.SYSTEMCTL, "--system", "daemon-reload"])

View File

@ -29,8 +29,10 @@ from ipapython.dn import DN
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.test_dnssec import wait_until_record_is_signed
from ipatests.test_integration.test_simple_replication import check_replication
from ipatests.util import assert_deepequal
logger = logging.getLogger(__name__)
@ -434,3 +436,58 @@ class TestBackupReinstallRestoreWithKRA(BaseBackupAndRestoreWithKRA):
def test_full_backup_reinstall_restore_with_vault(self):
"""backup, uninstall, reinstall, restore"""
self._full_backup_restore_with_vault(reinstall=True)
class TestBackupAndRestoreWithReplica(IntegrationTest):
"""Regression test for https://pagure.io/freeipa/issue/7234"""
num_replicas = 1
topology = "star"
@classmethod
def install(cls, mh):
if cls.domain_level is None:
domain_level = cls.master.config.domain_level
else:
domain_level = cls.domain_level
if cls.topology is None:
return
else:
tasks.install_topo(
cls.topology, cls.master, [],
cls.clients, domain_level
)
def test_full_backup_and_restore_with_replica(self):
replica = self.replicas[0]
with restore_checker(self.master):
backup_path = backup(self.master)
logger.info("Backup path for %s is %s", self.master, backup_path)
self.master.run_command([
"ipa-server-install", "--uninstall", "-U"
])
logger.info("Stopping and disabling oddjobd service")
self.master.run_command([
"systemctl", "stop", "oddjobd"
])
self.master.run_command([
"systemctl", "disable", "oddjobd"
])
dirman_password = self.master.config.dirman_password
self.master.run_command(
["ipa-restore", backup_path],
stdin_text=dirman_password + '\nyes'
)
status = self.master.run_command([
"systemctl", "status", "oddjobd"
])
assert "active (running)" in status.stdout_text
tasks.install_replica(self.master, replica)
check_replication(self.master, replica, "testuser1")

View File

@ -22,8 +22,36 @@ from __future__ import print_function
import pytest
from ipapython.dn import DN
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_plugins.integration import tasks
from ipatests.test_integration.base import IntegrationTest
def check_replication(source_host, dest_host, login):
source_host.run_command([
"ipa", "user-add", login,
"--first", "test",
"--last", "user"
])
source_ldap = source_host.ldap_connect()
tasks.wait_for_replication(source_ldap)
ldap = dest_host.ldap_connect()
tasks.wait_for_replication(ldap)
# Check using LDAP
basedn = dest_host.domain.basedn
user_dn = DN(
("uid", login), ("cn", "users"),
("cn", "accounts"), basedn
)
entry = ldap.get_entry(user_dn)
assert entry.dn == user_dn
assert entry["uid"] == [login]
# Check using CLI
result = dest_host.run_command(['ipa', 'user-show', login])
assert "User login: {}".format(login) in result.stdout_text
@pytest.mark.ds_acceptance
@ -36,37 +64,13 @@ class TestSimpleReplication(IntegrationTest):
num_replicas = 1
topology = 'star'
def check_replication(self, source_host, dest_host, login):
source_host.run_command(['ipa', 'user-add', login,
'--first', 'test',
'--last', 'user'])
source_ldap = source_host.ldap_connect()
tasks.wait_for_replication(source_ldap)
ldap = dest_host.ldap_connect()
tasks.wait_for_replication(ldap)
# Check using LDAP
basedn = dest_host.domain.basedn
user_dn = DN(('uid', login), ('cn', 'users'), ('cn', 'accounts'),
basedn)
entry = ldap.get_entry(user_dn)
print(entry)
assert entry.dn == user_dn
assert entry['uid'] == [login]
# Check using CLI
result = dest_host.run_command(['ipa', 'user-show', login])
assert 'User login: %s' % login in result.stdout_text
def test_user_replication_to_replica(self):
"""Test user replication master -> replica"""
self.check_replication(self.master, self.replicas[0], 'testuser1')
check_replication(self.master, self.replicas[0], 'testuser1')
def test_user_replication_to_master(self):
"""Test user replication replica -> master"""
self.check_replication(self.replicas[0], self.master, 'testuser2')
check_replication(self.replicas[0], self.master, 'testuser2')
def test_replica_removal(self):
"""Test replica removal"""

View File

@ -184,8 +184,8 @@ class Fuzzy(object):
"""
Perform a fuzzy (non-strict) equality tests.
`Fuzzy` instances will likely be used when comparing nesting data-structures
using `assert_deepequal()`.
`Fuzzy` instances will likely be used when comparing nesting
data-structures using `assert_deepequal()`.
By default a `Fuzzy` instance is equal to everything. For example, all of
these evaluate to ``True``:
@ -363,7 +363,8 @@ def assert_deepequal(expected, got, doc='', stack=tuple()):
>>> got = [u'Hello', dict(world=1.0)]
>>> expected == got
True
>>> assert_deepequal(expected, got, doc='Testing my nested data') # doctest:+ELLIPSIS
>>> assert_deepequal(
... expected, got, doc='Testing my nested data') # doctest: +ELLIPSIS
Traceback (most recent call last):
...
AssertionError: assert_deepequal: type(expected) is not type(got).
@ -396,7 +397,11 @@ def assert_deepequal(expected, got, doc='', stack=tuple()):
if isinstance(expected, DN):
if isinstance(got, six.string_types):
got = DN(got)
if not (isinstance(expected, Fuzzy) or callable(expected) or type(expected) is type(got)):
if (
not (isinstance(expected, Fuzzy)
or callable(expected)
or type(expected) is type(got))
):
raise AssertionError(
TYPE % (doc, type(expected), type(got), expected, got, stack)
)
@ -686,22 +691,30 @@ class DummyClass(object):
def __process(self, name_, args_, kw_):
if self.__i >= len(self.__calls):
raise AssertionError(
'extra call: %s, %r, %r' % (name_, args_, kw_)
"extra call: {name!s}, {args!r}, {kwargs!r}".format(
name=name_, args=args_, kwargs=kw_
)
)
(name, args, kw, result) = self.__calls[self.__i]
self.__i += 1
i = self.__i
if name_ != name:
raise AssertionError(
'call %d should be to method %r; got %r' % (i, name, name_)
"call {0:d} should be to method {1!r}; got {2!r}".format(
i, name, name_
)
)
if args_ != args:
raise AssertionError(
'call %d to %r should have args %r; got %r' % (i, name, args, args_)
"call {0:d} to {1!r} should have args {2!r}; got {3!r}".format(
i, name, args, args_
)
)
if kw_ != kw:
raise AssertionError(
'call %d to %r should have kw %r, got %r' % (i, name, kw, kw_)
"call {0:d} to {1!r} should have kw {2!r}, got {3!r}".format(
i, name, kw, kw_
)
)
if isinstance(result, Exception):
raise result
@ -799,7 +812,6 @@ def change_principal(principal, password=None, client=None, path=None,
if client is None:
client = api
client.Backend.rpcclient.disconnect()
try:
@ -863,5 +875,6 @@ def host_keytab(hostname, options=None):
def get_group_dn(cn):
return DN(('cn', cn), api.env.container_group, api.env.basedn)
def get_user_dn(uid):
return DN(('uid', uid), api.env.container_user, api.env.basedn)