summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
authorPetr Vobornik <pvoborni@redhat.com>2015-06-17 13:50:32 +0200
committerPetr Vobornik <pvoborni@redhat.com>2015-06-29 17:11:53 +0200
commit5397150979a474f6df82e6df5287e1cc678a3479 (patch)
tree1a271252be9322a62c87eb889e45723565bbede8 /ipalib
parent659b88b8205ef403aa9162453472e4731d93d13b (diff)
downloadfreeipa-5397150979a474f6df82e6df5287e1cc678a3479.tar.gz
freeipa-5397150979a474f6df82e6df5287e1cc678a3479.tar.xz
freeipa-5397150979a474f6df82e6df5287e1cc678a3479.zip
Verify replication topology for a suffix
Checks done: 1. check if the topology is not disconnected. In other words if there are replication paths between all servers. 2. check if servers don't have more than a recommended number of replication agreements(4) https://fedorahosted.org/freeipa/ticket/4302 Reviewed-By: David Kupka <dkupka@redhat.com>
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/constants.py4
-rw-r--r--ipalib/plugins/topology.py83
2 files changed, 87 insertions, 0 deletions
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 330f9df74..a062505c3 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -170,6 +170,10 @@ DEFAULT_CONFIG = (
# KRA plugin
('kra_host', FQDN), # Set in Env._finalize_core()
+ # Topology plugin
+ ('recommended_max_agmts', 4), # Recommended maximum number of replication
+ # agreements
+
# Special CLI:
('prompt_all', False),
('interactive', True),
diff --git a/ipalib/plugins/topology.py b/ipalib/plugins/topology.py
index 494d3bb0a..49060d672 100644
--- a/ipalib/plugins/topology.py
+++ b/ipalib/plugins/topology.py
@@ -10,6 +10,7 @@ from ipalib.plugins.baseldap import (
LDAPRetrieve)
from ipalib import _, ngettext
from ipalib import output
+from ipalib.util import create_topology_graph, get_topology_connection_errors
from ipapython.dn import DN
@@ -401,3 +402,85 @@ class topologysuffix_mod(LDAPUpdate):
@register()
class topologysuffix_show(LDAPRetrieve):
__doc__ = _('Show managed suffix.')
+
+
+@register()
+class topologysuffix_verify(LDAPQuery):
+ __doc__ = _('''
+Verify replication topology for suffix.
+
+Checks done:
+ 1. check if a topology is not disconnected. In other words if there are
+ replication paths between all servers.
+ 2. check if servers don't have more than the recommended number of
+ replication agreements
+''')
+
+ def execute(self, *keys, **options):
+
+ validate_domain_level(self.api)
+
+ masters = self.api.Command.server_find('', sizelimit=0)['result']
+ segments = self.api.Command.topologysegment_find(
+ keys[0], sizelimit=0)['result']
+ graph = create_topology_graph(masters, segments)
+ master_cns = [m['cn'][0] for m in masters]
+ master_cns.sort()
+
+ # check if each master can contact others
+ connect_errors = get_topology_connection_errors(graph)
+
+ # check if suggested maximum number of agreements per replica
+ max_agmts_errors = []
+ for m in master_cns:
+ # chosen direction doesn't matter much given that 'both' is the
+ # only allowed direction
+ suppliers = graph.get_tails(m)
+ if len(suppliers) > self.api.env.recommended_max_agmts:
+ max_agmts_errors.append((m, suppliers))
+
+ return dict(
+ result={
+ 'in_order': not connect_errors and not max_agmts_errors,
+ 'connect_errors': connect_errors,
+ 'max_agmts_errors': max_agmts_errors,
+ 'max_agmts': self.api.env.recommended_max_agmts
+ },
+ )
+
+ def output_for_cli(self, textui, output, *args, **options):
+
+ in_order = output['result']['in_order']
+ connect_errors = output['result']['connect_errors']
+ max_agmts_errors = output['result']['max_agmts_errors']
+
+ if in_order:
+ header = _('Replication topology of suffix "%(suffix)s" '
+ 'is in order.')
+ else:
+ header = _('Replication topology of suffix "%(suffix)s" contains '
+ 'errors.')
+ textui.print_h1(header % {'suffix': args[0]})
+
+ if connect_errors:
+ textui.print_dashed(unicode(_('Topology is disconnected')))
+ for err in connect_errors:
+ msg = _("Server %(srv)s can't contact servers: %(replicas)s")
+ msg = msg % {'srv': err[0], 'replicas': ', '.join(err[2])}
+ textui.print_indented(msg)
+
+ if max_agmts_errors:
+ textui.print_dashed(unicode(_('Recommended maximum number of '
+ 'agreements per replica exceeded')))
+ textui.print_attribute(
+ unicode(_("Maximum number of agreements per replica")),
+ [output['result']['max_agmts']]
+ )
+ for err in max_agmts_errors:
+ msg = _('Server "%(srv)s" has %(n)d agreements with servers:')
+ msg = msg % {'srv': err[0], 'n': len(err[1])}
+ textui.print_indented(msg)
+ for replica in err[1]:
+ textui.print_indented(replica, 2)
+
+ return 0