this patch is based on top of my patch #856 and tbabej'
s 325-9.

Obsoletes Ludwig's 0006.

ipalib part of topology management

Design:
- http://www.freeipa.org/page/V4/Manage_replication_topology

https://fedorahosted.org/freeipa/ticket/4302
--
Petr Vobornik
From 6b58f60e4948f9b1556d835aa9970efcb71a71b5 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Fri, 22 May 2015 09:50:09 +0200
Subject: [PATCH] topology: ipa management commands

ipalib part of topology management

Design:
- http://www.freeipa.org/page/V4/Manage_replication_topology

https://fedorahosted.org/freeipa/ticket/4302
---
 API.txt                    | 155 ++++++++++++++++++
 ipalib/constants.py        |   1 +
 ipalib/plugins/topology.py | 382 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 538 insertions(+)
 create mode 100644 ipalib/plugins/topology.py

diff --git a/API.txt b/API.txt
index 6ee7411c6f6bebfaa76a015560d9e87f3d662462..c2f0bc3e2abab5634d142821e08e3fa7de8b7b74 100644
--- a/API.txt
+++ b/API.txt
@@ -4341,6 +4341,161 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: topologysegment_add
+args: 2,13,3
+arg: Str('topologysuffixcn', cli_name='topologysuffix', multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, primary_key=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: StrEnum('iparepltoposegmentdirection', attribute=True, cli_name='direction', default=u'both', multivalue=False, required=True, values=(u'both', u'left-right', u'right-left', u'none'))
+option: Str('iparepltoposegmentleftnode', attribute=True, cli_name='leftnode', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$', required=True)
+option: Str('iparepltoposegmentrightnode', attribute=True, cli_name='rightnode', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$', required=True)
+option: StrEnum('nsds5replicaenabled', attribute=True, cli_name='enabled', multivalue=False, required=False, values=(u'on', u'off'))
+option: Str('nsds5replicastripattrs', attribute=True, cli_name='stripattrs', multivalue=False, required=False)
+option: Str('nsds5replicatedattributelist', attribute=True, cli_name='replattrs', multivalue=False, required=False)
+option: Str('nsds5replicatedattributelisttotal', attribute=True, cli_name='replattrstotal', multivalue=False, required=False)
+option: Int('nsds5replicatimeout', attribute=True, cli_name='timeout', minvalue=0, multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: topologysegment_del
+args: 2,2,3
+arg: Str('topologysuffixcn', cli_name='topologysuffix', multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=True, primary_key=True, query=True, required=True)
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'dict'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: ListOfPrimaryKeys('value', None, None)
+command: topologysegment_find
+args: 2,15,4
+arg: Str('topologysuffixcn', cli_name='topologysuffix', multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('criteria?', noextrawhitespace=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('cn', attribute=True, autofill=False, cli_name='name', maxlength=255, multivalue=False, primary_key=True, query=True, required=False)
+option: StrEnum('iparepltoposegmentdirection', attribute=True, autofill=False, cli_name='direction', default=u'both', multivalue=False, query=True, required=False, values=(u'both', u'left-right', u'right-left', u'none'))
+option: Str('iparepltoposegmentleftnode', attribute=True, autofill=False, cli_name='leftnode', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$', query=True, required=False)
+option: Str('iparepltoposegmentrightnode', attribute=True, autofill=False, cli_name='rightnode', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$', query=True, required=False)
+option: StrEnum('nsds5replicaenabled', attribute=True, autofill=False, cli_name='enabled', multivalue=False, query=True, required=False, values=(u'on', u'off'))
+option: Str('nsds5replicastripattrs', attribute=True, autofill=False, cli_name='stripattrs', multivalue=False, query=True, required=False)
+option: Str('nsds5replicatedattributelist', attribute=True, autofill=False, cli_name='replattrs', multivalue=False, query=True, required=False)
+option: Str('nsds5replicatedattributelisttotal', attribute=True, autofill=False, cli_name='replattrstotal', multivalue=False, query=True, required=False)
+option: Int('nsds5replicatimeout', attribute=True, autofill=False, cli_name='timeout', minvalue=0, multivalue=False, query=True, required=False)
+option: Flag('pkey_only?', autofill=True, default=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Int('sizelimit?', autofill=False, minvalue=0)
+option: Int('timelimit?', autofill=False, minvalue=0)
+option: Str('version?', exclude='webui')
+output: Output('count', <type 'int'>, None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('truncated', <type 'bool'>, None)
+command: topologysegment_mod
+args: 2,15,3
+arg: Str('topologysuffixcn', cli_name='topologysuffix', multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, primary_key=True, query=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: StrEnum('iparepltoposegmentdirection', attribute=True, autofill=False, cli_name='direction', default=u'both', multivalue=False, required=False, values=(u'both', u'left-right', u'right-left', u'none'))
+option: Str('iparepltoposegmentleftnode', attribute=True, autofill=False, cli_name='leftnode', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$', required=False)
+option: Str('iparepltoposegmentrightnode', attribute=True, autofill=False, cli_name='rightnode', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$', required=False)
+option: StrEnum('nsds5replicaenabled', attribute=True, autofill=False, cli_name='enabled', multivalue=False, required=False, values=(u'on', u'off'))
+option: Str('nsds5replicastripattrs', attribute=True, autofill=False, cli_name='stripattrs', multivalue=False, required=False)
+option: Str('nsds5replicatedattributelist', attribute=True, autofill=False, cli_name='replattrs', multivalue=False, required=False)
+option: Str('nsds5replicatedattributelisttotal', attribute=True, autofill=False, cli_name='replattrstotal', multivalue=False, required=False)
+option: Int('nsds5replicatimeout', attribute=True, autofill=False, cli_name='timeout', minvalue=0, multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: topologysegment_refresh
+args: 2,4,3
+arg: Str('topologysuffixcn', cli_name='topologysuffix', multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('left?', autofill=True, default=False)
+option: Flag('right?', autofill=True, default=False)
+option: Flag('stop?', autofill=True, default=False)
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'bool'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: topologysegment_show
+args: 2,4,3
+arg: Str('topologysuffixcn', cli_name='topologysuffix', multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: topologysuffix_add
+args: 1,6,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('iparepltopoconfroot', attribute=True, cli_name='suffix', maxlength=255, multivalue=False, required=True)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: topologysuffix_del
+args: 1,2,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=True, primary_key=True, query=True, required=True)
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'dict'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: ListOfPrimaryKeys('value', None, None)
+command: topologysuffix_find
+args: 1,8,4
+arg: Str('criteria?', noextrawhitespace=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('cn', attribute=True, autofill=False, cli_name='name', multivalue=False, primary_key=True, query=True, required=False)
+option: Str('iparepltopoconfroot', attribute=True, autofill=False, cli_name='suffix', maxlength=255, multivalue=False, query=True, required=False)
+option: Flag('pkey_only?', autofill=True, default=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Int('sizelimit?', autofill=False, minvalue=0)
+option: Int('timelimit?', autofill=False, minvalue=0)
+option: Str('version?', exclude='webui')
+output: Output('count', <type 'int'>, None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('truncated', <type 'bool'>, None)
+command: topologysuffix_mod
+args: 1,8,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Str('iparepltopoconfroot', attribute=True, autofill=False, cli_name='suffix', maxlength=255, multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: topologysuffix_show
+args: 1,4,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
 command: trust_add
 args: 1,13,3
 arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, required=True)
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 95dec54a51f38ae63eba667daacf35dcd7500cf3..75db702d1eed7b0a59f29c1d3b3ca4a47d15b090 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -118,6 +118,7 @@ DEFAULT_CONFIG = (
     ('container_radiusproxy', DN(('cn', 'radiusproxy'))),
     ('container_views', DN(('cn', 'views'), ('cn', 'accounts'))),
     ('container_masters', DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'))),
+    ('container_topology', DN(('cn', 'topology'), ('cn', 'ipa'), ('cn', 'etc'))),
 
     # Ports, hosts, and URIs:
     ('xmlrpc_uri', 'http://localhost:8888/ipa/xml'),
diff --git a/ipalib/plugins/topology.py b/ipalib/plugins/topology.py
new file mode 100644
index 0000000000000000000000000000000000000000..f300287dd1b7f1df1bf2a6b920b2e8d7c165ac3f
--- /dev/null
+++ b/ipalib/plugins/topology.py
@@ -0,0 +1,382 @@
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+import string
+import os
+
+from ipalib import api, errors
+from ipalib import Int, Str, Bool, StrEnum
+from ipalib.plugable import Registry
+from ipalib.plugins.baseldap import *
+from ipalib.plugins import baseldap
+from ipalib import _, ngettext
+from ipalib import output
+from ipaplatform.paths import paths
+
+
+__doc__ = _("""
+Topology
+
+Management of a replication topology.
+
+Requires minimum domain level 1.
+""")
+
+register = Registry()
+
+MINIMUM_DOMAIN_LEVEL = 1
+
+
+def validate_domain_level(api):
+    current = int(api.Command.domainlevel_show()['result'])
+    if current < MINIMUM_DOMAIN_LEVEL:
+        raise errors.InvalidDomainLevelError(
+            _('Topology management requires minimum domain level {0} '
+              .format(MINIMUM_DOMAIN_LEVEL))
+        )
+
+
+@register()
+class topologysegment(LDAPObject):
+    """
+    Topology segment.
+    """
+    parent_object = 'topologysuffix'
+    container_dn = api.env.container_topology
+    object_name = _('segment')
+    object_name_plural = _('segments')
+    object_class = ['iparepltoposegment']
+    default_attributes = [
+        'cn',
+        'ipaReplTopoSegmentdirection', 'ipaReplTopoSegmentrightNode',
+        'ipaReplTopoSegmentLeftNode', 'nsds5beginreplicarefresh',
+        'nsds5replicastripattrs',
+    ]
+    search_display_attributes = [
+        'cn', 'ipaReplTopoSegmentdirection', 'ipaReplTopoSegmentrightNode',
+        'ipaReplTopoSegmentLeftNode'
+    ]
+
+    label = _('Topology Segments')
+    label_singular = _('Topology Segment')
+
+    takes_params = (
+        Str(
+            'cn',
+            maxlength=255,
+            cli_name='name',
+            primary_key=True,
+            label=_('Segment name'),
+            default_from=lambda iparepltoposegmentleftnode, iparepltoposegmentrightnode:
+                         '%s-%s' % (iparepltoposegmentleftnode, iparepltoposegmentrightnode),
+            normalizer=lambda value: value.lower(),
+            doc=_('Arbitrary string identifying the segment'),
+        ),
+        Str(
+            'iparepltoposegmentleftnode',
+            pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$',
+            pattern_errmsg='may only include letters, numbers, -, . and $',
+            maxlength=255,
+            cli_name='leftnode',
+            label=_('Left node'),
+            normalizer=lambda value: value.lower(),
+            doc=_('Left replication node - an IPA server'),
+        ),
+        Str(
+            'iparepltoposegmentrightnode',
+            pattern='^[a-zA-Z0-9.][a-zA-Z0-9.-]{0,252}[a-zA-Z0-9.$-]?$',
+            pattern_errmsg='may only include letters, numbers, -, . and $',
+            maxlength=255,
+            cli_name='rightnode',
+            label=_('Right node'),
+            normalizer=lambda value: value.lower(),
+            doc=_('Right replication node - an IPA server'),
+        ),
+        StrEnum(
+            'iparepltoposegmentdirection',
+            cli_name='direction',
+            label=_('Connectivity'),
+            values=(u'both', u'left-right', u'right-left', u'none'),
+            default=u'both',
+            doc=_('Direction of replication between left and right replication '
+                  'node'),
+        ),
+        Str(
+            'nsds5replicastripattrs?',
+            cli_name='stripattrs',
+            label=_('Attributes to strip'),
+            normalizer=lambda value: value.lower(),
+            doc=_('A space separated list of attributes which are removed from '
+                  'replication updates.')
+        ),
+        Str(
+            'nsds5replicatedattributelist?',
+            cli_name='replattrs',
+            label='Attributes to replicate',
+            doc=_('Attributes that are not replicated to a consumer server '
+                  'during a fractional update. E.g., `(objectclass=*) '
+                  '$ EXCLUDE accountlockout memberof'),
+        ),
+        Str(
+            'nsds5replicatedattributelisttotal?',
+            cli_name='replattrstotal',
+            label=_('Attributes for total update'),
+            doc=_('Attributes that are not replicated to a consumer server '
+                  'during a total update. E.g. (objectclass=*) $ EXCLUDE '
+                  'accountlockout'),
+        ),
+        Int(
+            'nsds5replicatimeout?',
+            cli_name='timeout',
+            label=_('Session timeout'),
+            minvalue=0,
+            doc=_('Number of seconds outbound LDAP operations waits for a '
+                  'response from the remote replica before timing out and '
+                  'failing'),
+        ),
+        StrEnum(
+            'nsds5replicaenabled?',
+            cli_name='enabled',
+            label=_('Replication agreement enabled'),
+            doc=_('Whether a replication agreement is active, meaning whether '
+                  'replication is occurring per that agreement'),
+            values=(u'on', u'off'),
+        ),
+    )
+
+    def validate_nodes(self, ldap, dn, entry_attrs):
+        leftnode = entry_attrs.get('iparepltoposegmentleftnode')
+        rightnode = entry_attrs.get('iparepltoposegmentrightnode')
+
+        if not leftnode and not rightnode:
+            return  # nothing to check
+
+        # check if nodes are IPA servers
+        masters = self.api.Command.server_find('', sizelimit=0)['result']
+        m_hostnames = [master['cn'][0].lower() for master in masters]
+
+        if leftnode and leftnode not in m_hostnames:
+            raise errors.ValidationError(
+                name='leftnode',
+                error=_('left node is not a topology node: %(leftnode)s') %
+                     dict(leftnode=leftnode)
+            )
+
+        if rightnode and rightnode not in m_hostnames:
+            raise errors.ValidationError(
+                name='rightnode',
+                error=_('right node is not a topology node: %(rightnode)s') %
+                     dict(rightnode=rightnode)
+            )
+
+        # prevent creation of reflexive relation
+        key = 'leftnode'
+        if not leftnode or not rightnode:  # get missing end
+            _entry_attrs = ldap.get_entry(dn, ['*'])
+            if not leftnode:
+                key = 'rightnode'
+                leftnode = _entry_attrs['iparepltoposegmentleftnode'][0]
+            else:
+                rightnode = _entry_attrs['iparepltoposegmentrightnode'][0]
+
+        if leftnode == rightnode:
+            raise errors.ValidationError(
+                name=key,
+                error=_('left node and right node must not be the same')
+            )
+
+
+@register()
+class topologysegment_find(LDAPSearch):
+    __doc__ = _('Search for topology segments.')
+
+    msg_summary = ngettext(
+        '%(count)d segment matched',
+        '%(count)d segments matched', 0
+    )
+
+
+@register()
+class topologysegment_add(LDAPCreate):
+    __doc__ = _('Add a new segment.')
+
+    msg_summary = _('Added segment "%(value)s"')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        assert isinstance(dn, DN)
+        validate_domain_level(self.api)
+        self.obj.validate_nodes(ldap, dn, entry_attrs)
+        return dn
+
+
+@register()
+class topologysegment_del(LDAPDelete):
+    __doc__ = _('Delete a segment.')
+
+    msg_summary = _('Deleted segment "%(value)s"')
+
+    def pre_callback(self, ldap, dn, *keys, **options):
+        assert isinstance(dn, DN)
+        validate_domain_level(self.api)
+        return dn
+
+
+@register()
+class topologysegment_mod(LDAPUpdate):
+    __doc__ = _('Modify a segment.')
+
+    msg_summary = _('Modified segment "%(value)s"')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        assert isinstance(dn, DN)
+        validate_domain_level(self.api)
+        self.obj.validate_nodes(ldap, dn, entry_attrs)
+        return dn
+
+
+@register()
+class topologysegment_refresh(LDAPQuery):
+    __doc__ = _('Request a replication refresh of specified node.')
+
+    has_output = output.standard_value
+    msg_summary = _('%(value)s')
+
+    takes_options = (
+        Flag(
+            'left?',
+            doc=_('Initialize left node'),
+            default=False,
+        ),
+        Flag(
+            'right?',
+            doc=_('Initialize right node'),
+            default=False,
+        ),
+        Flag(
+            'stop?',
+            doc=_('Stop already started refresh of chosen node(s)'),
+            default=False,
+        ),
+    )
+
+    def execute(self, *keys, **options):
+        dn = self.obj.get_dn(*keys, **options)
+        validate_domain_level(self.api)
+
+        entry = self.obj.backend.get_entry(
+            dn, ['nsds5beginreplicarefresh;left', 'nsds5beginreplicarefresh;right'])
+
+        left = options.get('left')
+        right = options.get('right')
+        stop = options.get('stop')
+        action = u'start'
+        msg = _('Replication refresh for segment: "%(pkey)s" requested.')
+        if stop:
+            action = u'stop'
+            msg = _('Stopping of replication refresh for segment: "%(pkey)s" requested.')
+
+        if not left and not right:
+            raise errors.OptionError(
+                _('at least one node has to be specified')
+            )
+
+        if left:
+            entry['nsds5beginreplicarefresh;left'] = [action]
+        if right:
+            entry['nsds5beginreplicarefresh;right'] = [action]
+
+        self.obj.backend.update_entry(entry)
+
+        msg = msg % {'pkey': keys[-1]}
+        return dict(
+            result=True,
+            value=msg,
+        )
+
+
+@register()
+class topologysegment_show(LDAPRetrieve):
+    __doc__ = _('Display a segment.')
+
+
+@register()
+class topologysuffix(LDAPObject):
+    """
+    Suffix managed by the topology plugin.
+    """
+    container_dn = api.env.container_topology
+    object_name = _('suffix')
+    object_name_plural = _('suffices')
+    object_class = ['iparepltopoconf']
+    default_attributes = ['cn', 'ipaReplTopoConfRoot']
+    search_display_attributes = ['cn', 'ipaReplTopoConfRoot']
+    label = _('Topology suffices')
+    label_singular = _('Topology suffix')
+
+    takes_params = (
+        Str(
+            'cn',
+            cli_name='name',
+            primary_key=True,
+            label=_('Suffix name'),
+        ),
+        Str(
+            'iparepltopoconfroot',
+            maxlength=255,
+            cli_name='suffix',
+            label=_('Suffix to be managed'),
+            normalizer=lambda value: value.lower(),
+        ),
+    )
+
+
+@register()
+class topologysuffix_find(LDAPSearch):
+    __doc__ = _('Search for topology suffices.')
+
+    msg_summary = ngettext(
+        '%(count)d topology suffix matched',
+        '%(count)d topology suffices matched', 0
+    )
+
+
+@register()
+class topologysuffix_del(LDAPDelete):
+    __doc__ = _('Delete a topology suffix.')
+
+    msg_summary = _('Deleted topology suffix "%(value)s"')
+
+    def pre_callback(self, ldap, dn, *keys, **options):
+        assert isinstance(dn, DN)
+        validate_domain_level(self.api)
+        return dn
+
+
+@register()
+class topologysuffix_add(LDAPCreate):
+    __doc__ = _('Add a new topology suffix to be managed.')
+
+    msg_summary = _('Added topology suffix "%(value)s"')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        assert isinstance(dn, DN)
+        validate_domain_level(self.api)
+        return dn
+
+
+@register()
+class topologysuffix_mod(LDAPUpdate):
+    __doc__ = _('Modify a topology suffix.')
+
+    msg_summary = _('Modified topology suffix "%(value)s"')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        assert isinstance(dn, DN)
+        validate_domain_level(self.api)
+        return dn
+
+
+@register()
+class topologysuffix_show(LDAPRetrieve):
+    __doc__ = _('Show managed suffix.')
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to