Upgrade: fix replica agreement

The upgrade checks the replication agreements to ensure that
some attributes are excluded from replication. The agreements
are stored in entries like
cn=serverToreplica,cn=replica,cn=_suffix_,cn=mapping tree,cn=config
but those entries are managed by the replication topology plugin
and should not be updated directly. The consequence is that the update
of the attributes fails and ipa-server-update prints an error message:

Error caught updating nsDS5ReplicatedAttributeList: Server is unwilling
to perform: Entry and attributes are managed by topology plugin.No direct
modifications allowed.
Error caught updating nsDS5ReplicatedAttributeListTotal: Server is
unwilling to perform: Entry and attributes are managed by topology
plugin.No direct modifications allowed.

The upgrade continues but the replication is not excluding
passwordgraceusertime.

Instead of editing the agreements, perform the modifications on
the topology segments.

Fixes: https://pagure.io/freeipa/issue/9385
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Florence Blanc-Renaud 2023-06-19 10:36:29 +02:00
parent d76f8fceda
commit 143c3eb161

View File

@ -22,6 +22,7 @@ import logging
from ipaserver.install import replication
from ipalib import Registry
from ipalib import Updater
from ipalib import errors
logger = logging.getLogger(__name__)
@ -41,35 +42,42 @@ class update_replica_attribute_lists(Updater):
def execute(self, **options):
# We need an LDAPClient connection to the backend
logger.debug("Start replication agreement exclude list update task")
conn = self.api.Backend.ldap2
repl = replication.ReplicationManager(self.api.env.realm,
self.api.env.host,
None, conn=conn)
# We need to update only IPA replica agreements, not winsync
ipa_replicas = repl.find_ipa_replication_agreements()
logger.debug("Found %d agreement(s)", len(ipa_replicas))
for replica in ipa_replicas:
for desc in replica.get('description', []):
logger.debug('%s', desc)
self._update_attr(repl, replica,
'nsDS5ReplicatedAttributeList',
replication.EXCLUDES, template=EXCLUDE_TEMPLATE)
self._update_attr(repl, replica,
'nsDS5ReplicatedAttributeListTotal',
replication.TOTAL_EXCLUDES, template=EXCLUDE_TEMPLATE)
self._update_attr(repl, replica,
'nsds5ReplicaStripAttrs', replication.STRIP_ATTRS)
# Find suffixes
suffixes = self.api.Command.topologysuffix_find()['result']
for suffix in suffixes:
suffix_name = suffix['cn'][0]
# Find segments
sgmts = self.api.Command.topologysegment_find(
suffix_name, all=True)['result']
for segment in sgmts:
updates = {}
updates = self._update_attr(
segment, updates,
'nsds5replicatedattributelist',
replication.EXCLUDES, template=EXCLUDE_TEMPLATE)
updates = self._update_attr(
segment, updates,
'nsds5replicatedattributelisttotal',
replication.TOTAL_EXCLUDES, template=EXCLUDE_TEMPLATE)
updates = self._update_attr(
segment, updates,
'nsds5replicastripattrs', replication.STRIP_ATTRS)
if updates:
try:
self.api.Command.topologysegment_mod(
suffix_name, segment['cn'][0],
**updates)
except errors.EmptyModlist:
# No update done
logger.debug("No update required for the segment %s",
segment['cn'][0])
logger.debug("Done updating agreements")
return False, [] # No restart, no updates
def _update_attr(self, repl, replica, attribute, values, template='%s'):
def _update_attr(self, segment, updates, attribute, values, template='%s'):
"""Add or update an attribute of a replication agreement
If the attribute doesn't already exist, it is added and set to
@ -77,27 +85,21 @@ class update_replica_attribute_lists(Updater):
If the attribute does exist, `values` missing from it are just
appended to the end, also space-separated.
:param repl: Replication manager
:param replica: Replica agreement
:param: updates: dict containing the updates
:param segment: dict containing segment information
:param attribute: Attribute to add or update
:param values: List of values the attribute should hold
:param template: Template to use when adding attribute
"""
attrlist = replica.single_value.get(attribute)
attrlist = segment.get(attribute)
if attrlist is None:
logger.debug("Adding %s", attribute)
# Need to add it altogether
replica[attribute] = [template % " ".join(values)]
try:
repl.conn.update_entry(replica)
logger.debug("Updated")
except Exception as e:
logger.error("Error caught updating replica: %s", str(e))
updates[attribute] = template % " ".join(values)
else:
attrlist_normalized = attrlist.lower().split()
attrlist_normalized = attrlist[0].lower().split()
missing = [a for a in values
if a.lower() not in attrlist_normalized]
@ -105,14 +107,8 @@ class update_replica_attribute_lists(Updater):
logger.debug("%s needs updating (missing: %s)", attribute,
', '.join(missing))
replica[attribute] = [
'%s %s' % (attrlist, ' '.join(missing))]
updates[attribute] = '%s %s' % (attrlist[0], ' '.join(missing))
try:
repl.conn.update_entry(replica)
logger.debug("Updated %s", attribute)
except Exception as e:
logger.error("Error caught updating %s: %s",
attribute, str(e))
else:
logger.debug("%s: No update necessary", attribute)
return updates