Hello,

this is the current status of my work on #4302, and there are a few pieces still missing, eg the management command needs more input checking and error handling, but - I wanted to give people interested a chance to have a look again and get feedback
- there came up the following questions I would like to get an opinion.

when thinking how to move from an existing deployment with direct management of replication agreement to the new way, there should not be any intermediate disconnects, and if possible transition should be made easy. So I think we should have defined a few modes of operation for the plugin: - initial/bootstrap [optional] - the plugin detects existing agreements and transforms it to segments in the shared tree - pending - the plugin handles and propagates segments in the shared tree, but does not enforce teh deletion or creation of replication agreements - active - directe management of replicatio agreements is rejected, existing segments ond their modifications are applied

I did run my tests of the management command as directory manager since admin did not have permissions to read plugin configuration in cn=config, I can add permissions, probably will also need permissions for the part in the shared tree, so what is the expected operation mode, which user needs access to the shared config data and configuration ?


>From 31ffd14feab45753599df968722f88151eb45497 Mon Sep 17 00:00:00 2001
From: Ludwig Krispenz <lkris...@redhat.com>
Date: Fri, 10 Oct 2014 17:29:58 +0200
Subject: [PATCH] move replication topology to shared tree

Implementation of ticket 4302, still work in progress
---
 daemons/configure.ac                               |   1 +
 daemons/ipa-slapi-plugins/Makefile.am              |   1 +
 daemons/ipa-slapi-plugins/topology/Makefile.am     |  50 ++
 .../topology/ipa-topology-conf.ldif                |  22 +
 daemons/ipa-slapi-plugins/topology/topology.h      | 192 ++++++
 daemons/ipa-slapi-plugins/topology/topology_agmt.c | 254 ++++++++
 daemons/ipa-slapi-plugins/topology/topology_cfg.c  | 406 +++++++++++++
 daemons/ipa-slapi-plugins/topology/topology_init.c | 196 ++++++
 daemons/ipa-slapi-plugins/topology/topology_post.c | 181 ++++++
 daemons/ipa-slapi-plugins/topology/topology_pre.c  | 119 ++++
 daemons/ipa-slapi-plugins/topology/topology_util.c | 662 +++++++++++++++++++++
 freeipa.spec.in                                    |   3 +
 install/share/60ipaconfig.ldif                     |   6 +
 install/tools/Makefile.am                          |   1 +
 install/tools/ipa-topology-manage                  |  23 +
 ipaserver/install/dsinstance.py                    |   4 +
 ipaserver/install/ipa_topology_manage.py           | 387 ++++++++++++
 17 files changed, 2508 insertions(+)
 create mode 100644 daemons/ipa-slapi-plugins/topology/Makefile.am
 create mode 100755 daemons/ipa-slapi-plugins/topology/ipa-topology-conf.ldif
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology.h
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology_agmt.c
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology_cfg.c
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology_init.c
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology_post.c
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology_pre.c
 create mode 100644 daemons/ipa-slapi-plugins/topology/topology_util.c
 create mode 100755 install/tools/ipa-topology-manage
 create mode 100644 ipaserver/install/ipa_topology_manage.py

diff --git a/daemons/configure.ac b/daemons/configure.ac
index b4507a6d972f854331925e72869898576bdfd76f..afc94069e3b0d8a9e0f6d654642dd95727a86dac 100644
--- a/daemons/configure.ac
+++ b/daemons/configure.ac
@@ -323,6 +323,7 @@ AC_CONFIG_FILES([
     ipa-slapi-plugins/ipa-modrdn/Makefile
     ipa-slapi-plugins/ipa-sidgen/Makefile
     ipa-slapi-plugins/ipa-range-check/Makefile
+    ipa-slapi-plugins/topology/Makefile
 ])
 
 AC_OUTPUT
diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am
index 06e6ee8b86f138cce05f2184ac98c39ffaf9757f..c5412ef6e051fa59fcf084304bf66099832223f0 100644
--- a/daemons/ipa-slapi-plugins/Makefile.am
+++ b/daemons/ipa-slapi-plugins/Makefile.am
@@ -15,6 +15,7 @@ SUBDIRS =			\
 	ipa-winsync		\
 	ipa-sidgen		\
 	ipa-range-check		\
+	topology		\
 	$(NULL)
 
 EXTRA_DIST =			\
diff --git a/daemons/ipa-slapi-plugins/topology/Makefile.am b/daemons/ipa-slapi-plugins/topology/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..d3a555e88381d3dda409c6e5a5854c8156c02000
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/Makefile.am
@@ -0,0 +1,50 @@
+NULL =
+
+PLUGIN_COMMON_DIR=../common
+
+AM_CPPFLAGS =							\
+	-I.							\
+	-I$(srcdir)						\
+	-I$(PLUGIN_COMMON_DIR)					\
+	-I/usr/include/dirsrv					\
+	-DPREFIX=\""$(prefix)"\" 				\
+	-DBINDIR=\""$(bindir)"\"				\
+	-DLIBDIR=\""$(libdir)"\" 				\
+	-DLIBEXECDIR=\""$(libexecdir)"\"			\
+	-DDATADIR=\""$(datadir)"\"				\
+	$(LDAP_CFLAGS)					\
+	$(WARN_CFLAGS)						\
+	$(NULL)
+
+plugindir = $(libdir)/dirsrv/plugins
+plugin_LTLIBRARIES = 			\
+	libtopology.la		\
+	$(NULL)
+
+libtopology_la_SOURCES = 		\
+	topology_agmt.c	\
+	topology_init.c	\
+	topology_post.c	\
+	topology_pre.c	\
+	topology_util.c	\
+	$(NULL)
+
+libtopology_la_LDFLAGS = -avoid-version
+
+#libtopology_la_LIBADD = 		\
+#	$(LDAP_LIBS)			\
+#	$(NULL)
+
+appdir = $(IPA_DATA_DIR)
+app_DATA =			\
+	ipa-topology-conf.ldif	\
+	$(NULL)
+
+EXTRA_DIST =			\
+	README			\
+	$(app_DATA)		\
+	$(NULL)
+
+MAINTAINERCLEANFILES =		\
+	*~			\
+	Makefile.in
diff --git a/daemons/ipa-slapi-plugins/topology/ipa-topology-conf.ldif b/daemons/ipa-slapi-plugins/topology/ipa-topology-conf.ldif
new file mode 100755
index 0000000000000000000000000000000000000000..4a3b94823b152f04731e794546c6647800701643
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/ipa-topology-conf.ldif
@@ -0,0 +1,22 @@
+dn: cn=IPA Toplogy Configuration,cn=plugins,cn=config
+changetype: add
+objectClass: top
+objectClass: nsSlapdPlugin
+objectClass: extensibleObject
+cn: IPA Toplogy Configuration
+nsslapd-pluginPath: libtopology
+nsslapd-pluginInitfunc: ipa_topo_init
+nsslapd-pluginType: object
+nsslapd-pluginEnabled: on
+nsslapd-topo-plugin-shared-config-base: cn=topology,$SUFFIX
+nsslapd-topo-plugin-shared-replica-root: $SUFFIX
+nsslapd-pluginId: none
+nsslapd-plugin-depends-on-named: ldbm database
+nsslapd-pluginVersion: none
+nsslapd-pluginVendor: none
+nsslapd-pluginDescription: none
+
+dn: cn=Multimaster Replication Plugin,cn=plugins,cn=config
+changetype: modify
+add: nsslapd-plugin-depends-on-named
+nsslapd-plugin-depends-on-named: IPA Toplogy Configuration
diff --git a/daemons/ipa-slapi-plugins/topology/topology.h b/daemons/ipa-slapi-plugins/topology/topology.h
new file mode 100644
index 0000000000000000000000000000000000000000..80c5714d50c4a80f8f863c08fa96f512d53612ba
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology.h
@@ -0,0 +1,192 @@
+
+/**
+ * IPA Replication Topology Plugin
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "slapi-plugin.h"
+
+#define PLUGIN_NAME            "ipa-topology-plugin"
+#define PLUGIN_VENDOR          "freeipa"
+#define PLUGIN_VERSION         "0.1"
+
+#define IPA_TOPO_PLUGIN_SUBSYSTEM  "ipa-topology-plugin"
+#define IPA_TOPO_PREOP_DESC        "ipa-topology-preop-subplugin"
+#define IPA_TOPO_POSTOP_DESC       "ipa-topology-postop-subplugin"
+
+#define AGMT_TIMEOUT "300"
+#define REPL_MAN_DN "cn=replman,cn=config"
+#define REPL_MAN_PASSWD "replman"
+#define REPL_ATTR_LIST "(objectclass=*) $ EXCLUDE memberof idnssoaserial " \
+                       "entryusn krblastsuccessfulauth krblastfailedauth "\
+                       "krbloginfailedcount"
+#define REPL_ATTR_STRIP "modifiersName modifyTimestamp internalModifiersName "\
+                        "internalModifyTimestamp"
+#define REPL_ATTR_LIST_TOTAL "(objectclass=*) $ EXCLUDE entryusn "\
+                             "krblastsuccessfulauth krblastfailedauth "\
+                             "krbloginfailedcount"
+
+#define SEGMENT_DIR_BOTH "both"
+#define SEGMENT_DIR_LEFT_ORIGIN "left-right"
+#define SEGMENT_DIR_RIGHT_ORIGIN "right-left"
+#define TOPO_CONFIG_ENTRY  1
+#define TOPO_SEGMENT_ENTRY 2
+#define TOPO_HOST_ENTRY    3
+#define TOPO_IGNORE_ENTRY  4
+
+typedef struct topo_replica_agmt {
+    char *origin;    /* supplier side of agmt */
+    char *target;    /* consumer side of agmt */
+    char *enabled;
+    char *repl_root;
+    char *strip_attrs;
+    char *total_attrs;
+    char *repl_attrs;
+    char *repl_pause;
+    char *repl_timeout;
+    char *repl_refresh;
+    char *repl_transport;
+    char *repl_bind_dn;
+    char *repl_bind_cred;
+    char *repl_bind_method;
+} TopoReplicaAgmt;
+
+typedef struct topo_replica_segment {
+    struct topo_replica_segment *next;
+    TopoReplicaAgmt    *left;
+    TopoReplicaAgmt    *right;
+    int visited;
+} TopoReplicaSegment;
+
+typedef struct topo_replica_root {
+    struct topo_replica_root *next;
+    Slapi_Mutex *repl_lock;
+    char *shared_config_base;
+    Slapi_DN *shared_config_sdn;
+    char *repl_root;
+    TopoReplicaSegment *repl_segments;
+} TopoReplicaRoot;
+
+typedef struct topo_replica_host {
+    struct topo_replica_host *next;
+    char *hostname;
+} TopoReplicaHost;
+
+typedef struct topo_replica_conf {
+    Slapi_Mutex *conf_lock;
+    TopoReplicaRoot *replicas;
+    TopoReplicaHost *hosts;
+} TopoReplicaConf;
+
+typedef struct topo_plugin_config {
+    Slapi_Mutex *plg_lock;
+    void *identity;
+    char *hostname;
+    char *shared_config_base;
+    char *shared_topo;
+    Slapi_DN *shared_topo_sdn;
+    char *shared_hosts;
+    Slapi_DN *shared_hosts_sdn;
+    char **shared_replica_root;
+    char **managed_attrs;
+/*  int ipa_topo_plugin_activated; NOT USED YET*/
+} TopoPluginConf;
+
+#define CONFIG_ATTR_SHARED_BASE "nsslapd-topo-plugin-shared-config-base"
+#define CONFIG_ATTR_REPLICA_ROOT "nsslapd-topo-plugin-shared-replica-root"
+#define CONFIG_ATTR_PLUGIN_ACTIVE "nsslapd-topo-plugin-activated"
+
+/* functions to manage config and global variables */
+int ipa_topo_init_plugin_config(Slapi_PBlock *pb);
+void ipa_topo_init_shared_config(void);
+int ipa_topo_init_config(Slapi_PBlock *pb);
+void *ipa_topo_get_plugin_id(void);
+char *ipa_topo_get_plugin_shared_config(void);
+Slapi_DN *ipa_topo_get_plugin_shared_topo_dn(void);
+Slapi_DN *ipa_topo_get_plugin_shared_hosts_dn(void);
+char *ipa_topo_get_plugin_shared_topo(void);
+char *ipa_topo_get_plugin_shared_hosts(void);
+char *ipa_topo_get_plugin_hostname(void);
+char **ipa_topo_get_plugin_replica_root(void);
+char **ipa_topo_get_plugin_managed_attrs(void);
+void ipa_topo_set_plugin_id(void *plg_id);
+void ipa_topo_set_plugin_shared_config(char *);
+void ipa_topo_set_plugin_hostname(char *hostname);
+void ipa_topo_set_plugin_replica_root(char **roots);
+void ipa_topo_set_plugin_managed_attrs(char **mattrs);
+void ipa_topo_cfg_host_add(Slapi_Entry *hostentry);
+void ipa_topo_cfg_host_del(Slapi_Entry *hostentry);
+TopoReplicaHost *ipa_topo_cfg_host_find(char *host, int lock);
+TopoReplicaHost *ipa_topo_cfg_host_new(char *newhost);
+TopoReplicaSegment *ipa_topo_cfg_find_segment(char *repl_root, char *leftHost,
+                                              char *rightHost);
+TopoReplicaSegment *ipa_topo_cfg_replica_find_segment(TopoReplicaRoot *tconf, char *leftHost,
+                                              char *rightHost);
+void ipa_topo_cfg_segment_add(TopoReplicaRoot *tconf, TopoReplicaSegment *tsegm);
+void ipa_topo_cfg_segment_del(TopoReplicaRoot *tconf, TopoReplicaSegment *tsegm);
+TopoReplicaRoot *ipa_topo_cfg_replica_new(void);
+void ipa_topo_cfg_replica_add(TopoReplicaRoot *tconf);
+void ipa_topo_cfg_replica_del(TopoReplicaRoot *tconf);
+TopoReplicaRoot *ipa_topo_cfg_replica_find(char *repl_root, int lock);
+
+/* postop plugin functions */
+int ipa_topo_post_add(Slapi_PBlock *pb);
+int ipa_topo_post_mod(Slapi_PBlock *pb);
+int ipa_topo_post_del(Slapi_PBlock *pb);
+
+/* preop plugin functions */
+int ipa_topo_pre_add(Slapi_PBlock *pb);
+int ipa_topo_pre_mod(Slapi_PBlock *pb);
+int ipa_topo_pre_del(Slapi_PBlock *pb);
+
+/* functions to modify agreements */
+int ipa_topo_agmt_new(char *hostname, TopoReplicaRoot *repl_conf,
+                      TopoReplicaAgmt *agmt);
+int ipa_topo_agmt_del(char *hostname, TopoReplicaRoot *conf,
+                      TopoReplicaAgmt *agmt);
+int ipa_topo_agmt_mod(char *hostname, TopoReplicaRoot *conf,
+                      LDAPMod **mod, char *direction);
+int ipa_topo_agmt_setup(char *hostname, TopoReplicaRoot *repl_conf,
+                        TopoReplicaAgmt *agmt, int isgssapi);
+int ipa_topo_setup_std_agmt(char *hostname, TopoReplicaRoot *repl_conf,
+                            TopoReplicaAgmt *agmt);
+int ipa_topo_setup_gssapi_agmt(char *hostname, TopoReplicaRoot *repl_conf,
+                               TopoReplicaAgmt *agmt);
+int ipa_topo_util_update_agmts(TopoReplicaRoot *repl_conf,
+                               TopoReplicaSegment *repl_segments);
+char *ipa_topo_agreement_dn(TopoReplicaRoot *conf, char *hostname);
+void ipa_topo_util_missing_agmts_add(TopoReplicaRoot *repl_conf,
+                                     TopoReplicaSegment *repl_segments,
+                                     char *fromHost);
+void ipa_topo_util_existing_agmts_del(TopoReplicaRoot *repl_conf,
+                                      TopoReplicaSegment *repl_segments,
+                                      char *fromHost);
+void ipa_topo_util_existing_agmts_update(TopoReplicaRoot *repl_conf,
+                                         TopoReplicaSegment *repl_segments,
+                                         LDAPMod **mods ,char *fromHost);
+TopoReplicaAgmt *ipa_topo_util_agmt_from_entry(Slapi_Entry *entry,
+                                               char* replRoot, char *fromHost,
+                                               char *toHost, char *direction);
+TopoReplicaAgmt *ipa_topo_util_find_segment_agmt(TopoReplicaSegment *repl_segments,
+                                                 char *fromHost, char *toHost);
+char *ipa_topo_agmt_attr_is_managed(char *type, char *direction);
+int ipa_topo_util_setup_servers(void);
+
+int ipa_topo_util_entry_is_candidate(Slapi_Entry *e);
+int ipa_topo_util_target_is_managed(Slapi_Entry *e);
+char * ipa_topo_util_get_segm_attr(TopoReplicaAgmt *agmt, char *attr_type);
+void ipa_topo_util_set_segm_attr(TopoReplicaAgmt *agmt, char *attr_type,
+                                 char *attr_val);
+TopoReplicaSegment *ipa_topo_util_segment_from_entry(TopoReplicaRoot *conf,
+                                                     Slapi_Entry *entry);
+TopoReplicaSegment *ipa_topo_util_find_segment(TopoReplicaRoot *conf,
+                                               Slapi_Entry *entry);
+TopoReplicaRoot *ipa_topo_util_conf_from_entry(Slapi_Entry *entry);
+TopoReplicaRoot *ipa_topo_util_get_conf_for_segment(Slapi_Entry *segment_entry);
+Slapi_Entry *ipa_topo_util_get_entry(char *dn);
+int ipa_topo_util_modify(Slapi_DN *entrySDN, LDAPMod **mods);
+char *ipa_topo_util_get_pluginhost(void);
+TopoReplicaRoot *ipa_topo_util_get_replica_conf(char *repl_root);
+TopoReplicaSegment *ipa_topo_util_get_replica_segments(TopoReplicaRoot *replica);
diff --git a/daemons/ipa-slapi-plugins/topology/topology_agmt.c b/daemons/ipa-slapi-plugins/topology/topology_agmt.c
new file mode 100644
index 0000000000000000000000000000000000000000..c940184c4b66bc26c264320d4812b1a0318274d6
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology_agmt.c
@@ -0,0 +1,254 @@
+#include "topology.h"
+
+
+/* generate the dn for a replication agreement by providing replroot and host */
+char *
+ipa_topo_agreement_dn(TopoReplicaRoot *conf, char *hostname)
+{
+    char *dn;
+    char *filter;
+    Slapi_PBlock *pb;
+    Slapi_Entry **entries;
+    int ret;
+
+    pb = slapi_pblock_new();
+    filter = slapi_ch_smprintf("(&(objectclass=nsds5replica)(nsds5replicaroot=%s))",
+                               conf->repl_root);
+    slapi_search_internal_set_pb(pb, "cn=config", LDAP_SCOPE_SUB,
+                                 filter, NULL, 0, NULL, NULL,
+                                 ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+    if (ret != 0) {
+        dn = NULL;
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, ipa_topo_get_plugin_id(),
+                            "ipa_topo_agreement_dn: no replica found\n");
+            dn = NULL;
+        } else {
+            dn = slapi_ch_smprintf("cn=meTo%s,%s", hostname,
+                                   slapi_entry_get_dn_const(entries[0]));
+        }
+    }
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    return dn;
+}
+int
+ipa_topo_agmt_new(char *hostname, TopoReplicaRoot *conf, TopoReplicaAgmt *agmt)
+{
+    int ret = 0;
+    if (strcasecmp(agmt->repl_bind_method,"SASL/GSSAPI") == 0) {
+        ret = ipa_topo_agmt_setup(hostname, conf, agmt, 1);
+    } else {
+        ret = ipa_topo_agmt_setup(hostname, conf, agmt, 0);
+    }
+    return ret;
+}
+
+int ipa_topo_agmt_mod(char *hostname, TopoReplicaRoot *conf, LDAPMod **mods,
+                      char *direction)
+{
+    int ret;
+    Slapi_PBlock *pb;
+    char *dn = NULL;
+    Slapi_Entry **entries;
+    int i;
+    LDAPMod *tmp;
+    Slapi_Mods *smods;
+
+    dn = ipa_topo_agreement_dn(conf, hostname);
+    if (dn  == NULL)
+        return 1;
+
+    pb = slapi_pblock_new();
+    slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_BASE,
+                                 "objectclass=*", NULL, 0, NULL, NULL,
+                                 ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+    if (ret != 0) {
+        /* search failed */
+        goto done;
+    }
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+    if (NULL == entries || NULL == entries[0]) {
+        /* no entry */
+        ret = 1;
+        goto done;
+    }
+    /* apply mods to entry */
+
+    smods = slapi_mods_new();
+    for (i = 0; (mods != NULL) && (mods[i] != NULL); i++) {
+        char *type = ipa_topo_agmt_attr_is_managed(mods[i]->mod_type,direction);
+        if (type) {
+            tmp = mods[i];
+            switch (tmp->mod_op & ~LDAP_MOD_BVALUES) {
+            case LDAP_MOD_DELETE:
+                break;
+            case LDAP_MOD_ADD:
+            case LDAP_MOD_REPLACE:
+                slapi_mods_add_modbvps(smods, LDAP_MOD_REPLACE,
+                                       type, tmp->mod_bvalues);
+                break;
+            }
+            slapi_ch_free_string(&type);
+        }
+    }
+    if (slapi_mods_get_num_mods(smods) > 0) {
+        LDAPMod **lmods  = NULL;
+        Slapi_DN *sdn = slapi_sdn_new_normdn_byref(dn);
+        lmods = (slapi_mods_get_ldapmods_passout(smods));
+        ipa_topo_util_modify(sdn, lmods);
+        slapi_sdn_free(&sdn);
+    }
+done:
+    if (ret) slapi_ch_free_string(&dn);
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    return ret;
+}
+
+int
+ipa_topo_agmt_del(char *hostname, TopoReplicaRoot *conf, TopoReplicaAgmt *agmt)
+{
+    int ret = 0;
+    Slapi_PBlock *pb;
+    char *dn = NULL;
+
+    dn = ipa_topo_agreement_dn(conf, hostname);
+    if (dn  == NULL)
+        return (-1);
+
+    pb = slapi_pblock_new();
+    slapi_delete_internal_set_pb(pb, dn, NULL, NULL,
+                                 ipa_topo_get_plugin_id(), 0);
+
+    slapi_delete_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+    slapi_pblock_destroy(pb);
+
+    slapi_ch_free_string(&dn);
+    return ret;
+}
+int
+ipa_topo_agmt_setup(char *hostname, TopoReplicaRoot *conf,
+                    TopoReplicaAgmt *agmt, int isgssapi)
+{
+    Slapi_Entry *e = NULL;
+    Slapi_PBlock *pb;
+    char *dn = NULL;
+    Slapi_DN *sdn = NULL;
+    char *cn;
+    char port[] = "389";
+    char *description;
+    int ret;
+    /* Set up the new replication agreement entry */
+    dn = ipa_topo_agreement_dn(conf, hostname);
+    if (dn  == NULL)
+        return -1;
+    sdn = slapi_sdn_new_normdn_byref(dn);
+    e = slapi_entry_alloc();
+    /* the entry now owns the dup'd dn */
+    slapi_entry_init_ext(e, sdn, NULL); /* sdn is copied into e */
+    slapi_sdn_free(&sdn);
+
+    slapi_entry_add_string(e, SLAPI_ATTR_OBJECTCLASS, "nsds5replicationagreement");
+    cn = slapi_ch_smprintf("meTo%s",hostname);
+    slapi_entry_add_string(e, "cn",cn);
+    slapi_entry_add_string(e, "nsds5replicahost",hostname);
+    slapi_entry_add_string(e, "nsds5replicaport",port);
+    slapi_entry_add_string(e, "nsds5replicatimeout",AGMT_TIMEOUT);
+    slapi_entry_add_string(e, "nsds5replicaroot",agmt->repl_root);
+    description = slapi_ch_smprintf("me to %s",hostname);
+    slapi_entry_add_string(e, "description",description);
+
+    if (agmt->repl_attrs)
+        slapi_entry_add_string(e, "nsDS5ReplicatedAttributeList",agmt->repl_attrs);
+    if (isgssapi) {
+        slapi_entry_add_string(e, "nsds5replicatransportinfo","LDAP");
+        slapi_entry_add_string(e, "nsds5replicabindmethod","SASL/GSSAPI");
+    } else {
+        slapi_entry_add_string(e, "nsds5replicabinddn",REPL_MAN_DN);
+        slapi_entry_add_string(e, "nsds5replicacredentials",REPL_MAN_PASSWD);
+        slapi_entry_add_string(e, "nsds5replicatransportinfo","TLS");
+        slapi_entry_add_string(e, "nsds5replicabindmethod","simple");
+    }
+    if (agmt->strip_attrs)
+        slapi_entry_add_string(e, "nsds5ReplicaStripAttrs", agmt->strip_attrs);
+    if (agmt->total_attrs)
+        slapi_entry_add_string(e, "nsDS5ReplicatedAttributeListTotal", agmt->total_attrs);
+
+    pb = slapi_pblock_new();
+    slapi_pblock_init(pb);
+
+    /* e will be consumed by slapi_add_internal() */
+    slapi_add_entry_internal_set_pb(pb, e, NULL, ipa_topo_get_plugin_id(), 0);
+    slapi_add_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+    slapi_pblock_destroy(pb);
+
+    return ret;
+}
+
+
+int
+ipa_topo_agmt_initialize_replication(char *hostname,
+                                     TopoReplicaRoot *conf, TopoReplicaAgmt *agmt)
+{
+    int ret = 0;
+    char *dn;
+    Slapi_Mods *smods = slapi_mods_new();
+
+    slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
+                          "nsds5ReplicaEnabled", "on");
+    slapi_mods_add_string(smods, LDAP_MOD_ADD,
+                          "nsds5BeginReplicaRefresh", "start");
+    if (slapi_mods_get_num_mods(smods) > 0) {
+        LDAPMod **mods  = NULL;
+        mods = (slapi_mods_get_ldapmods_passout(smods));
+        dn = ipa_topo_agreement_dn(conf, hostname);
+        Slapi_DN *sdn = slapi_sdn_new_normdn_byref(dn);
+        ipa_topo_util_modify(sdn, mods);
+        slapi_sdn_free(&sdn);
+    }
+    return ret;
+}
+
+char *
+ipa_topo_agmt_attr_is_managed(char *type, char *direction)
+{
+    char *mtype = NULL;
+    char **mattrs = NULL;
+    char *subtype;
+    char *ctype = slapi_ch_strdup(type);
+    int i;
+
+    /* segment attrs have the form
+     * attrtype od attrtype;direction
+     * find the attrtype and return the corresponding 
+     * repl agreeement attribute type
+     */ 
+    subtype = strchr(ctype,';');
+    if (subtype) {
+        /* attr is handling specific direction, 
+         * check if interested
+         */
+        if (strstr(ctype,direction)) {
+            *subtype = '\0';
+        } else {
+            return NULL;
+        }
+    }
+    mattrs = ipa_topo_get_plugin_managed_attrs();
+    for (i=0; mattrs[i]; i++) {
+        if(0 == strcasecmp(mattrs[i], ctype)) {
+            mtype = slapi_ch_strdup(mattrs[i]);
+            break;
+        }
+    }
+    return mtype;
+}
diff --git a/daemons/ipa-slapi-plugins/topology/topology_cfg.c b/daemons/ipa-slapi-plugins/topology/topology_cfg.c
new file mode 100644
index 0000000000000000000000000000000000000000..03d0296250aea14c84eac3147f73bc3769b1d46c
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology_cfg.c
@@ -0,0 +1,406 @@
+
+#include "topology.h"
+
+/* two static data structures to hold the 
+ * plugin configuration and the information
+ * stored in the shared tree.
+ * They will be initialized at plugin init/start,
+ * updated when the shared config is modified
+ * and accessed via set/get functions
+ */
+static TopoPluginConf topo_plugin_conf;
+static TopoReplicaConf topo_shared_conf;
+
+char *ipa_topo_plugin_managed_attrs[] = {
+        "nsds5ReplicaStripAttrs",
+        "nsds5ReplicatedAttributeList",
+        "nsDS5ReplicatedAttributeListTotal",
+        "nsds5BeginReplicaRefresh",
+        "nsds5replicaTimeout",
+        "nsds5ReplicaEnabled",
+        "nsds5replicaSessionPauseTime",
+        "nsds5replicabinddn",
+        "nsds5replicacredentials",
+        "nsds5replicatransportinfo",
+        "nsds5replicabindmethod",
+        NULL };
+
+void *
+ipa_topo_get_plugin_id(void)
+{
+    return topo_plugin_conf.identity;
+}
+
+char *
+ipa_topo_get_plugin_hostname(void)
+{
+    return topo_plugin_conf.hostname;
+}
+
+char **
+ipa_topo_get_plugin_managed_attrs(void)
+{
+    return topo_plugin_conf.managed_attrs;
+}
+
+char *
+ipa_topo_get_plugin_shared_config(void)
+{
+    return topo_plugin_conf.shared_config_base;
+}
+char *
+ipa_topo_get_plugin_shared_topo(void)
+{
+    return topo_plugin_conf.shared_topo;
+}
+
+Slapi_DN *
+ipa_topo_get_plugin_shared_topo_dn(void)
+{
+    return topo_plugin_conf.shared_topo_sdn;
+}
+
+char *
+ipa_topo_get_plugin_shared_hosts(void)
+{
+    return topo_plugin_conf.shared_hosts;
+}
+
+Slapi_DN *
+ipa_topo_get_plugin_shared_hosts_dn(void)
+{
+    return topo_plugin_conf.shared_hosts_sdn;
+}
+char **
+ipa_topo_get_plugin_replica_root(void)
+{
+    return topo_plugin_conf.shared_replica_root;
+}
+
+void
+ipa_topo_set_plugin_id(void *plg_id)
+{
+    topo_plugin_conf.identity = plg_id;
+}
+
+void
+ipa_topo_set_plugin_shared_config(char *cfg)
+{
+    char *topo;
+    char *hosts;
+    topo_plugin_conf.shared_config_base = cfg;
+    topo = slapi_ch_smprintf("%s,%s","cn=topology",cfg);
+    hosts = slapi_ch_smprintf("%s,%s","cn=masters",cfg);
+    topo_plugin_conf.shared_topo = topo;
+    topo_plugin_conf.shared_topo_sdn = slapi_sdn_new_normdn_byref(topo);
+    topo_plugin_conf.shared_hosts = hosts;
+    topo_plugin_conf.shared_hosts_sdn = slapi_sdn_new_normdn_byref(hosts);
+
+}
+void 
+ipa_topo_set_plugin_hostname(char *hostname)
+{
+    topo_plugin_conf.hostname = hostname;
+}
+
+void
+ipa_topo_init_shared_config(void)
+{
+    topo_shared_conf.hosts = NULL;
+    topo_shared_conf.replicas = NULL;
+    topo_shared_conf.conf_lock = slapi_new_mutex();
+}
+
+void
+ipa_topo_set_plugin_managed_attrs(char **attrs)
+{
+    if (attrs) {
+        topo_plugin_conf.managed_attrs = attrs;
+    } else {
+        topo_plugin_conf.managed_attrs = ipa_topo_plugin_managed_attrs;
+    }
+}
+
+void
+ipa_topo_set_plugin_replica_root(char **root)
+{
+    topo_plugin_conf.shared_replica_root = root;
+}
+
+int
+ipa_topo_init_plugin_config(Slapi_PBlock * pb)
+{
+    Slapi_Entry *plugin_entry = NULL;
+    char *hostname;
+    char *config_base;
+    char **replica_root;
+
+    /* get the local hostname */
+    hostname = ipa_topo_util_get_pluginhost();
+    if (hostname == NULL) {
+        /* log error */
+        return -1;
+    } else {
+        ipa_topo_set_plugin_hostname(hostname); 
+    }
+    /* get the args */
+    /* slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry); */
+    slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &plugin_entry);
+
+    if(plugin_entry == NULL){
+        return -1;
+    }
+
+    config_base = slapi_entry_attr_get_charptr(plugin_entry,
+                                               CONFIG_ATTR_SHARED_BASE);
+    if(config_base){
+        ipa_topo_set_plugin_shared_config(config_base);
+    }
+    replica_root = slapi_entry_attr_get_charray(plugin_entry,
+                                                CONFIG_ATTR_REPLICA_ROOT);
+    if(replica_root){
+        ipa_topo_set_plugin_replica_root(replica_root);
+    }
+    ipa_topo_set_plugin_managed_attrs(NULL); /* use defaults */
+    return 0;
+
+}
+
+TopoReplicaHost *
+ipa_topo_cfg_host_find(char *findhost, int lock)
+{
+    TopoReplicaHost *host = NULL;
+
+    if (topo_shared_conf.hosts == NULL) return NULL;
+
+    if (lock) slapi_lock_mutex(topo_shared_conf.conf_lock);
+    for (host=topo_shared_conf.hosts;host;host=host->next) {
+        if (!strcasecmp(host->hostname,findhost)) {
+           break;
+        }
+    }
+    if (lock) slapi_unlock_mutex(topo_shared_conf.conf_lock);
+    return host;
+}
+
+TopoReplicaHost *
+ipa_topo_cfg_host_new(char *newhost)
+{
+    TopoReplicaHost *newnode;
+    newnode = (TopoReplicaHost *)slapi_ch_malloc(sizeof(TopoReplicaHost));
+    newnode->next = NULL;
+    newnode->hostname = newhost;
+    return newnode;
+}
+
+void
+ipa_topo_cfg_host_add(Slapi_Entry *hostentry)
+{
+    char *newhost;
+    TopoReplicaHost *hostnode = NULL;
+
+    newhost = slapi_entry_attr_get_charptr(hostentry,"cn");
+
+    if (newhost == NULL) return;
+
+    slapi_lock_mutex(topo_shared_conf.conf_lock);
+    if (ipa_topo_cfg_host_find(newhost, 0)) {
+        /* log error */
+        goto done;
+    }
+    hostnode = ipa_topo_cfg_host_new(newhost);
+    if (hostnode == NULL) {
+        /* log error */
+        goto done;
+    }
+
+    hostnode->next = topo_shared_conf.hosts;
+    topo_shared_conf.hosts = hostnode;
+
+done:
+    slapi_unlock_mutex(topo_shared_conf.conf_lock);
+    return;
+}
+void
+ipa_topo_cfg_host_free(TopoReplicaHost **node)
+{
+    slapi_ch_free((void **)&((*node)->hostname));
+    slapi_ch_free((void **)node);
+}
+
+void
+ipa_topo_cfg_host_del(Slapi_Entry *hostentry)
+{
+    char *delhost;
+    TopoReplicaHost *hostnode = NULL;
+    TopoReplicaHost *prevnode = NULL;
+
+    delhost = slapi_entry_attr_get_charptr(hostentry,"cn");
+
+    if (delhost == NULL) return;
+
+    slapi_lock_mutex(topo_shared_conf.conf_lock);
+    hostnode = topo_shared_conf.hosts;
+    while (hostnode) {
+        if (!strcasecmp(hostnode->hostname,delhost)) {
+           /*remove from list and free*/
+           if (prevnode) {
+               prevnode->next = hostnode->next;
+           } else {
+               topo_shared_conf.hosts = hostnode->next;
+           }
+           ipa_topo_cfg_host_free(&hostnode);
+           break;
+        } else {
+           prevnode = hostnode;
+           hostnode = hostnode->next;
+        }
+
+    }
+
+    slapi_unlock_mutex(topo_shared_conf.conf_lock);
+    return;
+}
+
+TopoReplicaSegment *
+ipa_topo_cfg_replica_segment_find(TopoReplicaRoot *replica, char *leftHost, char *rightHost)
+{
+    TopoReplicaSegment *tsegm = NULL;
+    TopoReplicaSegment *segment = NULL;
+           
+    slapi_lock_mutex(replica->repl_lock);
+    segment = replica->repl_segments;
+    while (segment) {
+        if ((segment->left && 
+            !strcasecmp(leftHost,segment->left->origin) && 
+            !strcasecmp(rightHost,segment->left->target)) ||
+            (segment->right && 
+            !strcasecmp(leftHost,segment->right->origin) &&
+            !strcasecmp(rightHost,segment->right->target))) {
+           tsegm = segment;
+           break;
+        }
+        segment = segment->next;
+    }
+    slapi_unlock_mutex(replica->repl_lock);
+
+    return tsegm;
+}
+
+TopoReplicaSegment *
+ipa_topo_cfg_segment_find(char *repl_root, char *leftHost, char *rightHost)
+{
+    TopoReplicaSegment *tsegm = NULL;
+    TopoReplicaRoot *replica = NULL;
+
+    slapi_lock_mutex(topo_shared_conf.conf_lock);
+
+    replica = ipa_topo_cfg_replica_find(repl_root, 0);
+    if (replica) {
+        tsegm = ipa_topo_cfg_replica_segment_find(replica,leftHost,rightHost);
+    }
+    slapi_unlock_mutex(topo_shared_conf.conf_lock);
+    return tsegm;
+}
+
+void
+ipa_topo_cfg_segment_add(TopoReplicaRoot *replica, TopoReplicaSegment *tsegm)
+{
+    slapi_lock_mutex(replica->repl_lock);
+    if (replica->repl_segments == NULL) {
+        replica->repl_segments = tsegm;
+    } else if (ipa_topo_cfg_replica_segment_find(replica,
+                                                 tsegm->left->origin,
+                                                 tsegm->right->origin)){
+        /* already exists: log error */
+    } else {
+        tsegm->next = replica->repl_segments;
+        replica->repl_segments = tsegm;
+    }
+    slapi_unlock_mutex(replica->repl_lock);
+}
+
+void
+ipa_topo_cfg_segment_del(TopoReplicaRoot *tconf, TopoReplicaSegment *tsegm)
+{
+    TopoReplicaSegment *segment = NULL;
+    TopoReplicaSegment *prev = NULL;
+           
+    slapi_lock_mutex(tconf->repl_lock);
+    segment = tconf->repl_segments;
+    while (segment) {
+        if (segment == tsegm) {
+            if (prev == NULL) {
+                tconf->repl_segments = segment->next;
+            } else {
+                prev->next = segment->next;
+            }
+            /* free segment */
+            break;
+        }
+        prev = segment;
+        segment = segment->next;
+    }
+    slapi_unlock_mutex(tconf->repl_lock);
+}
+
+TopoReplicaRoot *
+ipa_topo_cfg_replica_new(void)
+{
+    TopoReplicaRoot *topoRepl;
+    topoRepl = (TopoReplicaRoot *)slapi_ch_malloc(sizeof(TopoReplicaRoot));
+    if (topoRepl) {
+        topoRepl->repl_segments = NULL;
+        topoRepl->repl_root = NULL;
+        topoRepl->shared_config_base = NULL;
+        topoRepl->repl_lock = slapi_new_mutex();
+    }
+    return topoRepl;
+
+}
+
+void
+ipa_topo_cfg_replica_add(TopoReplicaRoot *tconf)
+{
+    slapi_lock_mutex(topo_shared_conf.conf_lock);
+    if (topo_shared_conf.replicas == NULL) {
+        topo_shared_conf.replicas = tconf;
+    } else if (ipa_topo_cfg_replica_find(tconf->repl_root,0)) {
+        /* log error: already exists */
+    } else {
+        tconf->next = topo_shared_conf.replicas;
+        topo_shared_conf.replicas = tconf;
+    }
+    slapi_unlock_mutex(topo_shared_conf.conf_lock);
+}
+
+void
+ipa_topo_cfg_replica_del(TopoReplicaRoot *tconf)
+{
+/* TBD */
+}
+
+TopoReplicaRoot *
+ipa_topo_cfg_replica_find(char *repl_root, int lock)
+{
+    TopoReplicaRoot *tconf = NULL;
+
+    if (lock) {
+        slapi_lock_mutex(topo_shared_conf.conf_lock);
+    }
+    if (topo_shared_conf.replicas == NULL) goto done;
+
+    tconf = topo_shared_conf.replicas;
+    while (tconf) {
+        if (!strcasecmp(repl_root,tconf->repl_root)) {
+           break;
+        }
+        tconf = tconf->next;
+    }
+
+done:
+    if (lock) {
+        slapi_unlock_mutex(topo_shared_conf.conf_lock);
+    }
+    return tconf;
+}
diff --git a/daemons/ipa-slapi-plugins/topology/topology_init.c b/daemons/ipa-slapi-plugins/topology/topology_init.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ecf175da24cf006456fcfda788302b43eccf4c9
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology_init.c
@@ -0,0 +1,196 @@
+
+#include "topology.h"
+
+char *ipa_topo_plugin_hostname;
+char *ipa_topo_plugin_shared_config_base;
+int ipa_topo_plugin_activated;
+
+static Slapi_PluginDesc pdesc = { PLUGIN_NAME, PLUGIN_VENDOR, PLUGIN_VERSION,
+                                  IPA_TOPO_PLUGIN_SUBSYSTEM };
+
+static int ipa_topo_start(Slapi_PBlock * pb);
+static int ipa_topo_close(Slapi_PBlock * pb);
+static int ipa_topo_preop_init(Slapi_PBlock *pb);
+static int ipa_topo_postop_init(Slapi_PBlock *pb);
+static int ipa_topo_apply_shared_replica_config(char *replica_root);
+
+int ipa_topo_init(Slapi_PBlock *pb)
+{
+    int rc = 0;
+    void *ipa_topo_plugin_identity = NULL;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_init\n");
+
+    /**
+     * Store the plugin identity for later use.
+     * Used for internal operations
+     */
+
+    slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ipa_topo_plugin_identity);
+    ipa_topo_set_plugin_id(ipa_topo_plugin_identity);
+
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01) != 0
+        || slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)ipa_topo_start) != 0
+        || slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void *)ipa_topo_close) != 0
+        || slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &pdesc) != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                        "ipa_topo_init: failed to register plugin\n");
+        rc = 1;
+    }
+
+    if (rc == 0) {
+        char *plugin_type = "bepreoperation";
+        if (slapi_register_plugin(plugin_type, 1, "ipa_topo_init",
+                                  ipa_topo_preop_init, IPA_TOPO_PREOP_DESC,
+                                  NULL, ipa_topo_get_plugin_id())) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                            "ipa_topo_init: failed to register preop plugin\n");
+            rc = 1;
+        }
+    }
+
+    if (rc == 0) {
+        char *plugin_type = "postoperation";
+        if (slapi_register_plugin(plugin_type, 1, "ipa_topo_init",
+                                  ipa_topo_postop_init, IPA_TOPO_POSTOP_DESC,
+                                  NULL, ipa_topo_get_plugin_id())) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                            "ipa_topo_init: failed to register postop plugin\n");
+            rc = 1;
+        }
+    }
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_init\n");
+    return(rc);
+}
+
+static int
+ipa_topo_preop_init(Slapi_PBlock *pb)
+{
+    int rc;
+
+    rc = slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN, (void *)ipa_topo_pre_mod);
+    rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_ADD_FN, (void *)ipa_topo_pre_add);
+    rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_DELETE_FN, (void *)ipa_topo_pre_del);
+
+    return(rc);
+    
+}
+
+static int
+ipa_topo_postop_init(Slapi_PBlock *pb)
+{
+    int rc;
+    rc = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipa_topo_post_add);
+    rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)ipa_topo_post_del);
+    rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipa_topo_post_mod);
+    return(rc);
+}
+
+static int
+ipa_topo_setup_managed_servers(void)
+{
+    int rc = 0;
+    
+    /* initially only read the entries below cn=masters 
+     * and build the list of hostnames
+     */
+    rc = ipa_topo_util_setup_servers();
+
+    return rc;
+}
+static int
+ipa_topo_apply_shared_config(void)
+{
+    int i = 0;
+    int rc = 0;
+    char **ipa_topo_plugin_shared_replica_root = ipa_topo_get_plugin_replica_root();
+
+    while (rc == 0 &&  ipa_topo_plugin_shared_replica_root[i]) {
+        rc = ipa_topo_apply_shared_replica_config(ipa_topo_plugin_shared_replica_root[i]);
+        i++;
+    }
+    return (rc);
+}
+
+static int
+ipa_topo_apply_shared_replica_config(char *replica_root)
+{
+    TopoReplicaRoot *replica_config = NULL;
+    TopoReplicaSegment *replica_segments = NULL;
+    int rc = 0;
+
+
+    
+    /* step 1. get replica onfig entry from shared tree 
+     *    search replica entry for replcia root below shared config base
+     */
+    replica_config = ipa_topo_util_get_replica_conf(replica_root);
+    if (replica_config) {
+        /* step 2. get all segments for the replica from the shared config */
+        replica_segments = ipa_topo_util_get_replica_segments(replica_config);
+        if (replica_segments) {
+            /* step 3. get all replication agreements for replica root */
+            rc = ipa_topo_util_update_agmts(replica_config, replica_segments);
+        }
+    }
+    return (rc);
+}
+
+static int
+ipa_topo_start(Slapi_PBlock * pb)
+{
+    int    argc;
+    char    **argv;
+    int rc = 0;
+
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+        "--> ipa_topo_start\n");
+
+    if (slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc) != 0 ||
+         slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv) != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                        "unable to get arguments\n");
+        return(-1);
+    }
+
+    /* init plugin config data from the plugin entry in cn=config */
+    rc = ipa_topo_init_plugin_config(pb);
+    if (rc != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                        "unable to get configuration\n");
+        return (rc);
+    }
+
+    ipa_topo_init_shared_config();
+    /* initialize the list of managed servers */
+    if (rc != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                        "unable to setuo managed servers\n");
+        return (rc);
+    }
+    rc = ipa_topo_setup_managed_servers();
+    /* initialize the config data from the shared tree and apply to
+     * the managed data under cn=config 
+     */
+    rc = ipa_topo_apply_shared_config();
+    if (rc != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                        "unable to apply configuration\n");
+        return (rc);
+    }
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+        "<-- ipa_topo_start\n");
+    return (rc);
+}
+
+static int
+ipa_topo_close(Slapi_PBlock * pb)
+{
+
+    return (0);
+}
diff --git a/daemons/ipa-slapi-plugins/topology/topology_post.c b/daemons/ipa-slapi-plugins/topology/topology_post.c
new file mode 100644
index 0000000000000000000000000000000000000000..48c8147bf14b008e06dd826ff4b979ede23c0305
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology_post.c
@@ -0,0 +1,181 @@
+#include "topology.h"
+
+/*
+ * detect if the plugin should handle this entry and return the entry type 
+ */
+int
+ipa_topo_check_entry_type(Slapi_Entry *entry)
+{
+    int ret = TOPO_IGNORE_ENTRY;
+    Slapi_DN *add_dn = NULL;
+    char **ocs;
+
+    add_dn = slapi_entry_get_sdn(entry);
+    if (slapi_sdn_issuffix(add_dn,ipa_topo_get_plugin_shared_topo_dn())) {
+        /* check if it is a toplogy or a segment */
+        /* check if segment's left or right node is the local server*/
+        int i;
+        ocs = slapi_entry_attr_get_charray(entry,"objectclass");
+
+        for (i=0; ocs && ocs[i]; i++) {
+            if (strcasecmp(ocs[i],"ipaReplTopoConf") == 0) {
+		ret = TOPO_CONFIG_ENTRY;
+                break;
+            } else if (strcasecmp(ocs[i],"ipaReplTopoSegment")) {
+		ret = TOPO_SEGMENT_ENTRY;
+                break;
+            }
+        }
+    } else if (slapi_sdn_issuffix(add_dn,ipa_topo_get_plugin_shared_hosts_dn())) {
+        ret = TOPO_HOST_ENTRY;
+    }
+
+    return ret;
+}
+int 
+ipa_topo_post_add(Slapi_PBlock *pb)
+{
+    int result = SLAPI_PLUGIN_SUCCESS;
+    Slapi_Entry *add_entry = NULL;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_post_add\n");
+    /* 1. get entry  */
+    slapi_pblock_get(pb,SLAPI_ENTRY_POST_OP,&add_entry);
+
+    if (add_entry == NULL) {
+        slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, "no entry");
+        return 1;
+    }
+    /* 2. check if it is in scope and type*/
+    switch (ipa_topo_check_entry_type(add_entry)) {
+    case TOPO_CONFIG_ENTRY:
+        /* initialize the shared topology data for a replica */ 
+        break;
+    case TOPO_SEGMENT_ENTRY: {
+        /* a new segment is added 
+         * verify that the segment does not yet exist 
+         * and that one node is the current server and
+         * that the other node is also managed by the
+         * shared config.
+         * If all checks pass create the replication agreement
+         */
+        /* TBD: do we know if the replica already has been initialized ?
+         *        should the agreement be enabled ?
+         *        For now assume everything is ok and enable 
+         */
+         TopoReplicaRoot *tconf = ipa_topo_util_get_conf_for_segment(add_entry);
+         TopoReplicaSegment *tsegm;
+         tsegm =  ipa_topo_util_segment_from_entry(tconf, add_entry);
+         ipa_topo_util_missing_agmts_add(tconf, tsegm, ipa_topo_get_plugin_hostname());
+         /* keep the new segment in tconf data */
+         ipa_topo_cfg_segment_add(tconf, tsegm);
+         break;
+    }
+    case TOPO_HOST_ENTRY: {
+        /* add to list of managed hosts */
+        ipa_topo_cfg_host_add(add_entry);
+        /* we are adding a new master, there could be
+         * a segment which so far was inactive since
+         * the host was not managed
+         */ 
+        break;
+    }
+    case TOPO_IGNORE_ENTRY:
+        break;
+    }
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_post_add\n");
+    return result;
+}
+int
+ipa_topo_post_mod(Slapi_PBlock *pb)
+{
+    int result = SLAPI_PLUGIN_SUCCESS;
+    Slapi_Entry *mod_entry = NULL;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_post_mod\n");
+
+    /* 1. get entry  */
+    slapi_pblock_get(pb,SLAPI_ENTRY_POST_OP,&mod_entry);
+
+    if (mod_entry == NULL) {
+        slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, "no entry\n");
+        return (1);
+    }
+    /* 2. check if it is in scope */
+    switch (ipa_topo_check_entry_type(mod_entry)) {
+    case TOPO_CONFIG_ENTRY:
+        break;
+    case TOPO_SEGMENT_ENTRY: {
+        LDAPMod **mods;
+        TopoReplicaRoot *tconf = ipa_topo_util_get_conf_for_segment(mod_entry);
+        TopoReplicaSegment *tsegm;
+        tsegm = ipa_topo_util_find_segment(tconf, mod_entry);
+        if (tsegm == NULL) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "segment to be modified does not exist\n");
+            break;
+        }
+        slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+        ipa_topo_util_existing_agmts_update(tconf, tsegm, mods,
+                                            ipa_topo_get_plugin_hostname());
+        /* also update local segment in tconf */
+        break;
+        }
+    case TOPO_HOST_ENTRY:
+    case TOPO_IGNORE_ENTRY:
+        break;
+    }
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_post_mod\n");
+    return result;
+}
+int
+ipa_topo_post_del(Slapi_PBlock *pb)
+{
+    int result = SLAPI_PLUGIN_SUCCESS;
+    Slapi_Entry *del_entry = NULL;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_post_del\n");
+    /* 1. get entry  */
+    slapi_pblock_get(pb,SLAPI_ENTRY_PRE_OP,&del_entry);
+
+    if (del_entry == NULL) {
+        slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, "no entry");
+        return 1;
+    }
+    /* 2. check if it is in scope */
+    switch (ipa_topo_check_entry_type(del_entry)) {
+    case TOPO_CONFIG_ENTRY:
+        break;
+    case TOPO_SEGMENT_ENTRY: {
+        /* check if corresponding agreement exists and delete */
+        TopoReplicaRoot *tconf = ipa_topo_util_get_conf_for_segment(del_entry);
+        TopoReplicaSegment *tsegm;
+        tsegm = ipa_topo_util_find_segment(tconf, del_entry);
+        if (tsegm == NULL) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "segment to be deleted does not exist\n");
+            break;
+        }
+        ipa_topo_util_existing_agmts_del(tconf, tsegm,
+                                         ipa_topo_get_plugin_hostname());
+        /* also remove segment from local topo conf */
+        ipa_topo_cfg_segment_del(tconf, tsegm);
+        break;
+        }
+    case TOPO_HOST_ENTRY:
+        ipa_topo_cfg_host_del(del_entry);
+        break;
+    case TOPO_IGNORE_ENTRY:
+        break;
+    }
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_post_del\n");
+    return result;
+}
diff --git a/daemons/ipa-slapi-plugins/topology/topology_pre.c b/daemons/ipa-slapi-plugins/topology/topology_pre.c
new file mode 100644
index 0000000000000000000000000000000000000000..5324c83b1ab9eadb6dfa7d82c2d85fa60adfd030
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology_pre.c
@@ -0,0 +1,119 @@
+#include "topology.h"
+
+/* the preoperation plugins check if the managed replication config
+ * is attempted to be directly modified.
+ * This is only allowed for internal operations triggerd by the
+ * topology plugin itself
+ */
+
+static int ipa_topo_pre_entry_in_scope(Slapi_PBlock *pb)
+{
+    Slapi_DN *dn;
+    static Slapi_DN *config_dn =  NULL;;
+
+    slapi_pblock_get(pb, SLAPI_TARGET_SDN, &dn);
+    if (config_dn == NULL) {
+        config_dn = slapi_sdn_new_dn_byval("cn=mapping tree,cn=config");
+        /* this rules out entries in regular backends and most of
+         * cn=config entries. 
+         */
+    }
+    return slapi_sdn_issuffix(dn,config_dn);
+
+}
+int ipa_topo_is_entry_managed(Slapi_PBlock *pb)
+{
+    Slapi_Entry *e;
+    char *pi;
+    int op_type;
+
+    if (!ipa_topo_pre_entry_in_scope(pb)) {
+        /* we don't care for general mods, only specific 
+         * entries in the mapping tree 
+         */
+        return 0;
+    }
+    slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
+    if (op_type == SLAPI_OPERATION_ADD) {
+        slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+    } else { 
+        slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e);
+    }
+    if (!ipa_topo_util_entry_is_candidate(e)) {
+        /* entry has no objectclass the plugin controls */
+        return 0;
+    }
+
+    /* we have to check if the operation is triggered by the 
+     * topology plugin itself - allow it 
+     */
+    slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY,&pi);
+    if (pi && 0 == strcasecmp(pi, ipa_topo_get_plugin_id())) {
+        return 0;
+    }
+    /* last check: is the endpoint of the agreement amanaged host ? */
+    if (ipa_topo_util_target_is_managed(e)) {
+        return 1;
+    } else {
+        return 0;
+    }
+
+}
+int ipa_topo_pre_add(Slapi_PBlock *pb)
+{
+    int result = SLAPI_PLUGIN_SUCCESS;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_pre_add\n");
+    if (ipa_topo_is_entry_managed(pb)) {
+        int rc = LDAP_UNWILLING_TO_PERFORM;
+        char *errtxt;
+        errtxt = slapi_ch_smprintf("Entry is managed by topology plugin."
+                                   " Adding of entry not allowed.\n");
+        slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
+        slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
+        result = SLAPI_PLUGIN_FAILURE;
+    }
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_pre_add\n");
+    return result;
+}
+int ipa_topo_pre_mod(Slapi_PBlock *pb)
+{
+
+    int result = SLAPI_PLUGIN_SUCCESS;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_pre_mod\n");
+    if (ipa_topo_is_entry_managed(pb)) {
+        int rc = LDAP_UNWILLING_TO_PERFORM;
+        char *errtxt;
+        errtxt = slapi_ch_smprintf("Entry is managed by topology plugin."
+                                   "No direct modifications allowed.\n");
+        slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
+        slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
+        result = SLAPI_PLUGIN_FAILURE;
+    }
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_pre_mod\n");
+    return result;
+}
+
+int ipa_topo_pre_del(Slapi_PBlock *pb)
+{
+    int result = SLAPI_PLUGIN_SUCCESS;
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "--> ipa_topo_pre_del\n");
+    if (ipa_topo_is_entry_managed(pb)) {
+        int rc = LDAP_UNWILLING_TO_PERFORM;
+        char *errtxt;
+        errtxt = slapi_ch_smprintf("Entry is managed by topology plugin."
+                                   "Deletion not allowed.\n");
+        slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
+        slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
+        result = SLAPI_PLUGIN_FAILURE;
+    }
+    slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
+                    "<-- ipa_topo_pre_del\n");
+    return result;
+}
diff --git a/daemons/ipa-slapi-plugins/topology/topology_util.c b/daemons/ipa-slapi-plugins/topology/topology_util.c
new file mode 100644
index 0000000000000000000000000000000000000000..f48a79980af885b20ea6bcfcf3216241eb8dd5a9
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/topology/topology_util.c
@@ -0,0 +1,662 @@
+#include "topology.h"
+
+
+int
+ipa_topo_util_modify(Slapi_DN *entrySDN, LDAPMod **mods)
+{
+    int rc = 0;
+    Slapi_PBlock *mod_pb;
+
+    mod_pb = slapi_pblock_new();
+    slapi_pblock_init(mod_pb);
+
+    slapi_modify_internal_set_pb_ext(mod_pb, entrySDN, mods, NULL, NULL,
+                                     ipa_topo_get_plugin_id(), 0);
+    slapi_modify_internal_pb(mod_pb);
+    slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    slapi_pblock_destroy(mod_pb);
+    return rc;
+
+}
+
+Slapi_Entry *
+ipa_topo_util_get_entry (char *dn)
+{
+    int rc = 0;
+    Slapi_Entry *res_entry = NULL;
+    Slapi_Entry **entries;
+    Slapi_PBlock *pb = NULL;
+
+    pb = slapi_pblock_new();
+
+    slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_BASE,
+                                 "objectclass=*", NULL, 0, NULL, NULL,
+                                 ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != 0)
+    {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_get_entry: "
+                        "unable to read entry (%s): error %d\n", dn, rc);
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_get_entry: entry not found: %s\n", dn);
+        } else {
+            res_entry = entries[0];
+        }
+    }
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    return res_entry;
+}
+
+/* 
+ * the plugin needs to determine if segments in the shared topology 
+ * affect the instance it is running in. There are many ways to determine
+ * this "pluginhost":
+ * - get the machines hostname
+ * - define hostname in plugin conf
+ * - use nsslapd-localhost from cn=config
+ * - ...
+ * This first version will use the nsslapd-localhost
+ */
+char *
+ipa_topo_util_get_pluginhost(void)
+{
+    int rc = 0;
+    Slapi_Entry **entries;
+    Slapi_PBlock *pb = NULL;
+    char *host = NULL;
+    char *host_attrs[] = {"nsslapd-localhost", NULL};
+
+    pb = slapi_pblock_new();
+
+    slapi_search_internal_set_pb(pb, "cn=config", LDAP_SCOPE_BASE,
+        "objectclass=*", host_attrs, 0, NULL, NULL,
+        ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != 0)
+    {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_get_localhost: "
+                        "unable to read server configuration: error %d\n", rc);
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_get_localhost: server configuration missing\n");
+        } else {
+            host = slapi_entry_attr_get_charptr(entries[0], "nsslapd-localhost");
+        }
+    }
+
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    return host;
+}
+
+
+TopoReplicaRoot *
+ipa_topo_util_get_replica_conf(char *repl_root)
+{
+    int rc = 0;
+    Slapi_Entry **entries;
+    Slapi_PBlock *pb = NULL;
+    char *filter;
+    TopoReplicaRoot *topoRepl = NULL;
+
+    pb = slapi_pblock_new();
+    filter = slapi_ch_smprintf("(ipaReplTopoConfRoot=%s)",repl_root);
+    slapi_search_internal_set_pb(pb, ipa_topo_get_plugin_shared_topo(), 
+                                  LDAP_SCOPE_ONELEVEL,
+                                  filter, NULL, 0, NULL, NULL,
+                                  ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != 0)
+    {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_get_replica_conf: "
+                        "no replica configuration found: error %d\n", rc);
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_get_replica_conf: "
+                            "server configuration missing\n");
+        } else {
+            topoRepl = ipa_topo_cfg_replica_new();
+            topoRepl->shared_config_base =
+                slapi_ch_strdup(slapi_entry_get_dn_const(entries[0]));
+            topoRepl->repl_root = slapi_ch_strdup(repl_root);
+        }
+    }
+    ipa_topo_cfg_replica_add(topoRepl);
+
+    slapi_ch_free_string(&filter);
+    slapi_pblock_destroy(pb);
+
+    return topoRepl;
+}
+
+TopoReplicaSegment *
+ipa_topo_util_get_replica_segments(TopoReplicaRoot *replica)
+{
+    TopoReplicaSegment *repl_segment = NULL;
+    int rc = 0;
+    Slapi_Entry **entries;
+    Slapi_PBlock *pb = NULL;
+    char *filter;
+
+    pb = slapi_pblock_new();
+    filter = "objectclass=*";
+    slapi_search_internal_set_pb(pb, replica->shared_config_base,
+                                 LDAP_SCOPE_ONELEVEL, filter, NULL, 0, NULL, NULL,
+                                 ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != 0)
+    {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_get_replica_segments: "
+                        "no replica configuration found: error %d\n", rc);
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_get_replica_segments: no segments found\n");
+        } else {
+            /* get number of segments and allocate */
+            int i = 0;
+            for (i=0;entries[i];i++) {
+                slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                                "ipa_topo_util_get_replica_segments: "
+                                "adding segment %s\n", slapi_entry_get_dn_const(entries[i]));
+                repl_segment = ipa_topo_util_segment_from_entry(replica, entries[i]);
+                ipa_topo_cfg_segment_add(replica, repl_segment);
+            }
+        }
+    }
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    return replica->repl_segments;
+}
+
+int
+ipa_topo_util_setup_servers(void)
+{
+    int rc = 0;
+    Slapi_Entry **entries;
+    Slapi_PBlock *pb = NULL;
+    char *filter;
+
+    pb = slapi_pblock_new();
+    filter = "objectclass=*";
+    slapi_search_internal_set_pb(pb,ipa_topo_get_plugin_shared_hosts(),
+                                  LDAP_SCOPE_ONELEVEL,
+                                  filter, NULL, 0, NULL, NULL,
+                                  ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != 0)
+    {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_setup_servers: "
+                        "search for servers failed: error %d\n", rc);
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_setup_servers: no servers found\n");
+        } else {
+            int i = 0;
+            for (i=0;entries[i];i++) {
+                ipa_topo_cfg_host_add(entries[i]);
+            }
+        }
+    }
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    return rc;
+
+}
+
+TopoReplicaAgmt *
+ipa_topo_util_agmt_from_entry(Slapi_Entry *entry, char *replRoot, char *fromHost,
+                              char *toHost, char *direction)
+{
+    TopoReplicaAgmt *agmt = NULL;
+    char **mattrs;
+    char *mattr;
+    char *mval;
+    int i;
+
+    agmt = (TopoReplicaAgmt *) slapi_ch_calloc(1,sizeof(TopoReplicaAgmt));
+    agmt->origin = slapi_ch_strdup(fromHost);
+    agmt->target = slapi_ch_strdup(toHost);
+    agmt->repl_root = slapi_ch_strdup(replRoot);
+
+    mattrs = ipa_topo_get_plugin_managed_attrs();
+    for (i=0; mattrs[i]; i++) {
+        mattr = slapi_ch_smprintf("%s;%s",mattrs[i],direction);
+        mval = slapi_entry_attr_get_charptr(entry,mattr);
+        if (mval == 0) {
+            mval = slapi_entry_attr_get_charptr(entry,mattrs[i]);
+        }
+        if (mval) {
+            ipa_topo_util_set_segm_attr(agmt, mattrs[i], mval);
+        }
+    }
+    if (agmt->repl_bind_method == NULL) {
+        agmt->repl_bind_method = slapi_ch_strdup("SASL/GSSAPI");
+    }
+    return agmt;
+}
+TopoReplicaSegment *
+ipa_topo_util_find_segment(TopoReplicaRoot *conf, Slapi_Entry *entry)
+{
+    char *leftHost;
+    char *rightHost;
+    TopoReplicaSegment *segment = NULL;
+
+    leftHost = slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentLeftNode");
+    rightHost = slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentRightNode");
+
+    segment = ipa_topo_cfg_find_segment(conf->repl_root, leftHost, rightHost);
+
+    slapi_ch_free((void **)&leftHost);
+    slapi_ch_free((void **)&rightHost);
+    return segment;
+}
+
+TopoReplicaSegment *
+ipa_topo_util_segment_from_entry(TopoReplicaRoot *conf, Slapi_Entry *entry)
+{
+    char *leftHost;
+    char *rightHost;
+    char *direction;
+
+    TopoReplicaSegment *segment = NULL;
+    segment = (TopoReplicaSegment *) slapi_ch_calloc(1,sizeof(TopoReplicaSegment));
+    leftHost = slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentLeftNode");
+    rightHost = slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentRightNode");
+    direction =  slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentDirection");
+    if (strcasecmp(direction,SEGMENT_DIR_BOTH) == 0){
+        segment->left = ipa_topo_util_agmt_from_entry(entry,conf->repl_root,
+                                                      leftHost,rightHost, "left");
+        segment->right = ipa_topo_util_agmt_from_entry(entry,conf->repl_root,
+                                                       rightHost,leftHost, "right");
+    } else if (strcasecmp(direction,SEGMENT_DIR_LEFT_ORIGIN) == 0) {
+        segment->left = ipa_topo_util_agmt_from_entry(entry,conf->repl_root,
+                                                      leftHost,rightHost, "left");
+    } else if (strcasecmp(direction,SEGMENT_DIR_RIGHT_ORIGIN) == 0) {
+        segment->right = ipa_topo_util_agmt_from_entry(entry,conf->repl_root,
+                                                       rightHost,leftHost, "right");
+    }
+    slapi_ch_free((void **)&leftHost);
+    slapi_ch_free((void **)&rightHost);
+    slapi_ch_free((void **)&direction);
+
+    return segment;
+}
+
+TopoReplicaRoot *
+ipa_topo_util_get_conf_for_segment(Slapi_Entry *segment_entry)
+{
+    /* we have a segment entry and need to determine the corresponding
+     * replica conf, to get the replica root */   
+    TopoReplicaRoot *tconf = NULL;
+    char *parent = slapi_dn_parent(slapi_entry_get_dn_const(segment_entry));
+
+    Slapi_Entry *conf = ipa_topo_util_get_entry(parent);
+    tconf = ipa_topo_util_conf_from_entry(conf);
+
+    return tconf;
+}
+
+TopoReplicaRoot *
+ipa_topo_util_conf_from_entry(Slapi_Entry *entry)
+{
+    TopoReplicaRoot *conf = NULL;
+    char *repl_root = NULL;
+    repl_root = slapi_entry_attr_get_charptr(entry,"ipaReplTopoConfRoot");
+    conf = ipa_topo_cfg_replica_find(repl_root, 1);
+    if (conf) {
+        slapi_ch_free((void **)&repl_root);
+        return conf;
+    } else {
+        conf = (TopoReplicaRoot *) slapi_ch_calloc(1,sizeof(TopoReplicaRoot));
+        conf->repl_root = repl_root;
+        /* TBD read defined managed attrs as defaults */
+        return conf;
+    }
+}
+
+int
+ipa_topo_util_update_agmts(TopoReplicaRoot *conf, TopoReplicaSegment *repl_segments)
+{
+    int rc = 0;
+    int i;
+    int nentries;
+    Slapi_Entry **entries;
+    Slapi_Entry *repl_agmt;
+    Slapi_PBlock *pb = NULL;
+    char *filter;
+    
+    /* find all replication agreements */
+
+    pb = slapi_pblock_new();
+    filter = slapi_ch_smprintf("(&(objectclass=nsds5replicationagreement)(nsds5replicaroot=%s))",
+                               conf->repl_root);
+    slapi_search_internal_set_pb(pb, "cn=config", LDAP_SCOPE_SUB,
+                                 filter, NULL, 0, NULL, NULL,
+                                 ipa_topo_get_plugin_id(), 0);
+    slapi_search_internal_pb(pb);
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != 0)
+    {
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_update_agmts: "
+                        "cannot read replication agreeements: error %d\n", rc);
+        goto error_return;
+    } else {
+        slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+        if (NULL == entries || NULL == entries[0]) {
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_update_agmts: "
+                            "no agrements found\n");
+            goto error_return;
+        }
+    }
+
+    /* for each agreement find segment */
+    nentries = 0;
+    repl_agmt = entries[0];
+    while (repl_agmt) {
+        char *targetHost;
+        TopoReplicaAgmt *topo_agmt;
+
+        slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                        "ipa_topo_util_update_agmts: processing agreement: %s\n", 
+                        slapi_entry_get_dn_const(repl_agmt));
+        
+        targetHost = slapi_entry_attr_get_charptr(repl_agmt,"nsDS5ReplicaHost");
+        if(!targetHost){
+            slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, 
+                            "ipa_topo_util_update_agmts: "
+                             "cannot read targethost: error %d\n", rc);
+            continue;
+        }
+        topo_agmt = ipa_topo_util_find_segment_agmt(repl_segments,
+                                                    ipa_topo_get_plugin_hostname(),
+                                                    targetHost);
+        if (topo_agmt) {
+            /* if segment found update agreement params */
+            char * segm_attr_val;
+            char * agmt_attr_val;
+            Slapi_Mods *smods = slapi_mods_new();
+            char **mattrs = ipa_topo_get_plugin_managed_attrs();
+            for (i=0; mattrs[i]; i++) {
+                segm_attr_val = ipa_topo_util_get_segm_attr(topo_agmt,mattrs[i]);
+                if (segm_attr_val) {
+                    agmt_attr_val =  slapi_entry_attr_get_charptr(repl_agmt,mattrs[i]);
+                    if (agmt_attr_val == NULL ||
+                        strcasecmp(agmt_attr_val,segm_attr_val)) {
+                        /* value does not exist in agmt or 
+                         * is different from segment: replace
+                         */
+                        slapi_mods_add_string(smods,
+                                              LDAP_MOD_REPLACE,
+                                              mattrs[i],
+                                              segm_attr_val);
+                    }
+                                        
+                }
+            }
+            if (slapi_mods_get_num_mods(smods) > 0) {
+                LDAPMod **mods  = NULL;
+                mods = (slapi_mods_get_ldapmods_passout(smods));
+                ipa_topo_util_modify((Slapi_DN *)slapi_entry_get_sdn_const(repl_agmt),
+                                     mods);
+            }
+        } else {
+            /* if not found disable or delete agreement */
+        }
+        repl_agmt = entries[++nentries];
+    }
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+
+    /* check if segments not covered by agreement exist
+     * add agreeement
+     */
+    ipa_topo_util_missing_agmts_add(conf, repl_segments,
+                                    ipa_topo_get_plugin_hostname());
+
+error_return:
+    return rc;
+}
+
+TopoReplicaAgmt *
+ipa_topo_util_find_segment_agmt(TopoReplicaSegment *repl_segments,
+                                char *fromHost, char *toHost)
+{
+    TopoReplicaAgmt *agmt = NULL;
+    TopoReplicaAgmt *agmtfound = NULL;
+    TopoReplicaSegment *segment = repl_segments;
+
+    while (segment) {
+        agmt = segment->left;
+        if (agmt && (0 == strcasecmp(agmt->origin, fromHost)) &&
+                (0 == strcasecmp(agmt->target, toHost))) {
+            agmtfound = agmt;
+            break;
+        }
+        agmt = segment->right;
+        if (agmt && (0 == strcasecmp(agmt->origin, fromHost)) &&
+                (0 == strcasecmp(agmt->target, toHost))) {
+            agmtfound = agmt;
+            break;
+        }
+        segment = segment->next;
+    }
+    return agmtfound;
+}
+void
+ipa_topo_util_missing_agmts_add(TopoReplicaRoot *repl_conf,
+                                TopoReplicaSegment *repl_segments,
+                                char *fromHost)
+{
+    TopoReplicaAgmt *l_agmt = NULL;
+    TopoReplicaAgmt *r_agmt = NULL;
+    TopoReplicaSegment *segment = repl_segments;
+
+    while (segment) {
+        if (segment->visited) {
+            segment = segment->next;
+            continue;
+        }
+        l_agmt = segment->left;
+        r_agmt = segment->right;
+        if (l_agmt && r_agmt) {
+            if (0 == strcasecmp(l_agmt->origin, fromHost)) {
+                ipa_topo_agmt_new(r_agmt->origin,repl_conf, l_agmt); 
+            } else if (0 == strcasecmp(r_agmt->origin, fromHost)) {
+                ipa_topo_agmt_new(l_agmt->origin,repl_conf, r_agmt); 
+            }
+        }
+        segment = segment->next;
+    }
+}
+void
+ipa_topo_util_existing_agmts_del(TopoReplicaRoot *repl_conf,
+                                 TopoReplicaSegment *repl_segments,
+                                 char *fromHost)
+{
+    TopoReplicaAgmt *l_agmt = NULL;
+    TopoReplicaAgmt *r_agmt = NULL;
+    TopoReplicaSegment *segment = repl_segments;
+
+    while (segment) {
+        if (segment->visited) {
+            segment = segment->next;
+            continue;
+        }
+        l_agmt = segment->left;
+        r_agmt = segment->right;
+        if (l_agmt && r_agmt) {
+            if (0 == strcasecmp(l_agmt->origin, fromHost)) {
+                ipa_topo_agmt_del(r_agmt->origin,repl_conf, l_agmt); 
+            } else if (0 == strcasecmp(r_agmt->origin, fromHost)) {
+                ipa_topo_agmt_del(l_agmt->origin,repl_conf, r_agmt); 
+            }
+        }
+        segment = segment->next;
+    }
+}
+void
+ipa_topo_util_existing_agmts_update(TopoReplicaRoot *repl_conf,
+                                    TopoReplicaSegment *repl_segments,
+                                    LDAPMod **mods ,char *fromHost)
+{
+    TopoReplicaAgmt *l_agmt = NULL;
+    TopoReplicaAgmt *r_agmt = NULL;
+    TopoReplicaSegment *segment = repl_segments;
+
+    while (segment) {
+        if (segment->visited) {
+            segment = segment->next;
+            continue;
+        }
+        l_agmt = segment->left;
+        r_agmt = segment->right;
+        if (l_agmt && r_agmt) {
+            if (0 == strcasecmp(l_agmt->origin, fromHost)) {
+                ipa_topo_agmt_mod(r_agmt->origin,repl_conf, mods, "left"); 
+            } else if (0 == strcasecmp(r_agmt->origin, fromHost)) {
+                ipa_topo_agmt_mod(l_agmt->origin,repl_conf, mods, "right"); 
+            }
+        }
+        segment = segment->next;
+    
+    }
+}
+
+char *
+ipa_topo_util_get_segm_attr(TopoReplicaAgmt *agmt, char *attr_type)
+{
+    char *attr = NULL;
+    if (strcasecmp(attr_type, "nsds5ReplicaEnabled") == 0) {
+        attr = agmt->enabled;
+    } else if (strcasecmp(attr_type, "nsds5ReplicaStripAttrs") == 0){
+        attr = agmt->strip_attrs;
+    } else if (strcasecmp(attr_type, "nsds5ReplicatedAttributeList") == 0){
+        attr = agmt->repl_attrs;
+    } else if (strcasecmp(attr_type, "nsDS5ReplicatedAttributeListTotal") == 0) {
+        attr = agmt->total_attrs;
+    } else if (strcasecmp(attr_type, "nsds5BeginReplicaRefresh") == 0){
+        attr = agmt->repl_refresh;
+    } else if (strcasecmp(attr_type, "nsds5replicaTimeout") == 0) {
+        attr = agmt->repl_timeout;
+    } else if (strcasecmp(attr_type, "nsds5ReplicaEnabled") == 0) {
+        attr = agmt->enabled;
+    } else if (strcasecmp(attr_type, "nsds5replicaSessionPauseTime") == 0) {
+        attr = agmt->repl_pause;
+    } else if (strcasecmp(attr_type, "nsds5replicabinddn") == 0) {
+        attr = agmt->repl_bind_dn;
+    } else if (strcasecmp(attr_type, "nsds5replicacredentials") == 0) {
+        attr = agmt->repl_bind_cred;
+    } else if (strcasecmp(attr_type, "nsds5replicatransportinfo") == 0) {
+        attr = agmt->repl_transport;
+    } else if (strcasecmp(attr_type, "nsds5replicabindmethod") == 0) {
+        attr = agmt->repl_bind_method;
+    }
+    return attr;
+}
+
+void
+ipa_topo_util_set_segm_attr(TopoReplicaAgmt *agmt, char *attr_type, char *attr_val)
+{
+    if (strcasecmp(attr_type, "nsds5ReplicaEnabled") == 0) {
+        agmt->enabled = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5ReplicaStripAttrs") == 0){
+        agmt->strip_attrs = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5ReplicatedAttributeList") == 0){
+        agmt->repl_attrs = attr_val;
+    } else if (strcasecmp(attr_type, "nsDS5ReplicatedAttributeListTotal") == 0) {
+        agmt->total_attrs = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5BeginReplicaRefresh") == 0){
+        agmt->repl_refresh = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5replicaTimeout") == 0) {
+        agmt->repl_timeout = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5ReplicaEnabled") == 0) {
+        agmt->enabled = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5replicaSessionPauseTime") == 0) {
+        agmt->repl_pause = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5replicabinddn") == 0) {
+        agmt->repl_bind_dn = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5replicacredentials") == 0) {
+        agmt->repl_bind_cred = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5replicatransportinfo") == 0) {
+        agmt->repl_transport = attr_val;
+    } else if (strcasecmp(attr_type, "nsds5replicabindmethod") == 0) {
+        agmt->repl_bind_method = attr_val;
+    }
+}
+
+/* check if the entry is a standard replication agreement (ignore winsync)
+ * and if the replication suffix is in the list of managed replication roots.
+ * This qualifies the entry as a candidate, further checks have to be done.
+ */
+int
+ipa_topo_util_entry_is_candidate(Slapi_Entry *e)
+{
+    char **ocs;
+    char *oc = NULL;
+    char *repl_root;
+    char **shared_root;
+    int rc = 0;
+    int i;
+
+    ocs = slapi_entry_attr_get_charray(e,"objectclass");
+
+    for (i=0; ocs && ocs[i]; i++) {
+        if (strcasecmp(ocs[i],"nsds5ReplicationAgreement") == 0) {
+            oc = ocs[i];
+            break;
+        }
+    }
+
+    if (oc) {
+        repl_root = slapi_entry_attr_get_charptr(e,"nsDS5ReplicaRoot");
+        shared_root = ipa_topo_get_plugin_replica_root();
+        for (i=0; shared_root && shared_root[i]; i++) {
+            if (strcasecmp(repl_root,shared_root[i]) == 0) {
+                rc = 1;
+                break;
+            }
+        }
+        slapi_ch_free((void **) &repl_root);
+    }
+    slapi_ch_array_free(ocs);
+    return rc;
+}
+int ipa_topo_util_target_is_managed(Slapi_Entry *e)
+{
+    char *targethost;
+    int ret = 0;
+
+    targethost = slapi_entry_attr_get_charptr(e,"nsDS5ReplicaHost");
+
+    if (targethost &&
+        (ipa_topo_cfg_host_find(targethost, 1) >= 0)) {
+        ret = 1;
+    }
+    slapi_ch_free_string(&targethost);
+
+    return ret;
+}
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 1138299143b6e8aa31d238d5b8c0fae28fa56024..e55f3f9e108917c2aa172a60107b1dcd111dd264 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -349,6 +349,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_sidgen_task.la
 rm %{buildroot}/%{plugin_dir}/libipa_extdom_extop.la
 rm %{buildroot}/%{plugin_dir}/libipa_range_check.la
 rm %{buildroot}/%{plugin_dir}/libipa_otp_lasttoken.la
+rm %{buildroot}/%{plugin_dir}/libtopology.la
 rm %{buildroot}/%{_libdir}/krb5/plugins/kdb/ipadb.la
 rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la
 
@@ -578,6 +579,7 @@ fi
 %{_sbindir}/ipa-replica-install
 %{_sbindir}/ipa-replica-prepare
 %{_sbindir}/ipa-replica-manage
+%{_sbindir}/ipa-topology-manage
 %{_sbindir}/ipa-csreplica-manage
 %{_sbindir}/ipa-server-certinstall
 %{_sbindir}/ipa-ldap-updater
@@ -696,6 +698,7 @@ fi
 %attr(755,root,root) %{plugin_dir}/libipa_dns.so
 %attr(755,root,root) %{plugin_dir}/libipa_range_check.so
 %attr(755,root,root) %{plugin_dir}/libipa_otp_lasttoken.so
+%attr(755,root,root) %{plugin_dir}/libtopology.so
 %dir %{_localstatedir}/lib/ipa
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/backup
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore
diff --git a/install/share/60ipaconfig.ldif b/install/share/60ipaconfig.ldif
index 692690714aabad6b5e34328fe24cfab62bc0d70c..ff9656a7fee1524ef64d9a7a219b6c6e3b6fc0c6 100644
--- a/install/share/60ipaconfig.ldif
+++ b/install/share/60ipaconfig.ldif
@@ -43,6 +43,10 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.23 NAME 'ipaCertificateSubjectBase' SY
 attributeTypes: (2.16.840.1.113730.3.8.3.16 NAME 'ipaConfigString' DESC 'Generic configuration stirng' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
 attributeTypes: ( 2.16.840.1.113730.3.8.3.26 NAME 'ipaSELinuxUserMapDefault' DESC 'Default SELinux user' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
 attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC 'Available SELinux user context ordering' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
+attributetypes: ( ipaReplTopoConfRoot-oid NAME 'ipaReplTopoConfRoot' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' )
+attributetypes: ( ipaReplTopoSegmentDirection-oid NAME 'ipaReplTopoSegmentDirection' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' )
+attributetypes: ( ipaReplTopoSegmentLeftNode-oid NAME 'ipaReplTopoSegmentLeftNode' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' )
+attributetypes: ( ipaReplTopoSegmentRightNode-oid NAME 'ipaReplTopoSegmentRightNode' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' )
 ###############################################
 ##
 ## ObjectClasses
@@ -51,3 +55,5 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC
 objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData ) )
 ## ipaConfigObject - Generic config strings object holder
 objectClasses: (2.16.840.1.113730.3.8.4.13 NAME 'ipaConfigObject' DESC 'generic config object for IPA' AUXILIARY MAY ( ipaConfigString ) X-ORIGIN 'IPA v2' )
+objectclasses: ( ipaReplTopoConf-oid NAME 'ipaReplTopoConf' DESC 'IPA defined objectclass' SUP top STRUCTURAL MUST ipaReplTopoConfRoot MAY ( cn $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal ) X-ORIGIN 'Free IPA' )
+objectclasses: ( ipaReplTopoSegment-oid NAME 'ipaReplTopoSegment' DESC 'IPA defined objectclass' SUP top STRUCTURAL MUST ( ipaReplTopoSegmentDirection $ ipaReplTopoSegmentLeftNode $ ipaReplTopoSegmentRightNode) MAY ( cn $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal $ nsds5BeginReplicaRefresh $ description $ nsds5replicaTimeout $ nsds5ReplicaEnabled  $ nsds5ReplicaStripAttrs $ nsds5replicaSessionPauseTime $ nsds5ReplicaProtocolTimeout ) X-ORIGIN 'Free IPA' )
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index 485be91b7bca2b0f3822a70d0f027793208918c1..be91d1ddafa5b8a6242cfc5195ab0ff4cff6b331 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -13,6 +13,7 @@ sbin_SCRIPTS =			\
 	ipa-replica-install	\
 	ipa-replica-prepare	\
 	ipa-replica-manage	\
+	ipa-topology-manage	\
 	ipa-csreplica-manage	\
 	ipa-server-certinstall  \
 	ipactl			\
diff --git a/install/tools/ipa-topology-manage b/install/tools/ipa-topology-manage
new file mode 100755
index 0000000000000000000000000000000000000000..4e7201fe6f2c8f553d18a31dc101270d7862dd95
--- /dev/null
+++ b/install/tools/ipa-topology-manage
@@ -0,0 +1,23 @@
+#! /usr/bin/python2 -E
+# Authors: Ludwig Krispenz <lkris...@redhat.com>
+#
+# Copyright (C) 2014  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from ipaserver.install.ipa_topology_manage import TopologyManage
+
+TopologyManage.run_cli()
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index c15ef2ceb10b5f8815039e07e95d6fbac5a081c4..00daf5884caa941d27fe1b6dee5d3e778c92ac0f 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -274,6 +274,7 @@ class DsInstance(service.Service):
         self.step("enabling entryUSN plugin", self.__enable_entryusn)
         self.step("configuring lockout plugin", self.__config_lockout_module)
         self.step("configuring OTP last token plugin", self.__config_otp_lasttoken_module)
+        self.step("configuring topology plugin", self.__config_topology_module)
         self.step("creating indices", self.__create_indices)
         self.step("enabling referential integrity plugin", self.__add_referint_module)
         if enable_ssl:
@@ -587,6 +588,9 @@ class DsInstance(service.Service):
     def __config_otp_lasttoken_module(self):
         self._ldap_mod("otp-lasttoken-conf.ldif")
 
+    def __config_topology_module(self):
+        self._ldap_mod("ipa-topology-conf.ldif")
+
     def __repoint_managed_entries(self):
         self._ldap_mod("repoint-managed-entries.ldif", self.sub_dict)
 
diff --git a/ipaserver/install/ipa_topology_manage.py b/ipaserver/install/ipa_topology_manage.py
new file mode 100644
index 0000000000000000000000000000000000000000..e52e9c1c60821db05a396bca1dee2d9ead3c1251
--- /dev/null
+++ b/ipaserver/install/ipa_topology_manage.py
@@ -0,0 +1,387 @@
+# Authors: Ludwig Krispenz <lkris...@redhat.com>
+#
+# Copyright (C) 2008-2012  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import shutil
+import krbV
+import ldap
+import tempfile
+from optparse import OptionGroup
+from ConfigParser import SafeConfigParser
+
+from ipaserver.install import certs, installutils, bindinstance, dsinstance
+from ipaserver.install.replication import enable_replication_version_checking
+from ipaserver.plugins.ldap2 import ldap2
+from ipaserver.install.bindinstance import (
+    add_zone, add_fwd_rr, add_ptr_rr, dns_container_exists)
+from ipapython import ipautil, admintool, dogtag, ipaldap
+from ipapython.dn import DN
+from ipapython import version
+from ipalib import api
+from ipalib import errors
+from ipaplatform.paths import paths
+from ipalib.constants import CACERT
+
+TOPO_CONF_BASE_ATTR = "nsslapd-topo-plugin-shared-config-base"
+TOPO_CONF_REPLICA_ROOT_ATTR =  "ipaReplTopoConfRoot"
+TOPO_CONF_REPLICA_OBJECTCLASS = "ipaReplTopoConf"
+TOPO_CONF_SEGM_LEFT = "ipaReplTopoSegmentLeftNode"
+TOPO_CONF_SEGM_RIGHT = "ipaReplTopoSegmentRightNode"
+
+segment_defaults = {
+    "nsds5replicatimeout": "100",
+    "nsds5replicaEnabled": "on"
+    }
+
+segment_attr_type = {
+    "timeout" : "nsds5replicatimeout",
+    "enabled" : "nsds5replicaEnabled"
+    }
+
+class TopologyManage(admintool.AdminTool):
+    command_name = 'ipa-topology-manage'
+
+    @classmethod
+    def add_options(cls, parser):
+        super(TopologyManage, cls).add_options(parser, debug_option=True)
+
+        parser.add_option("-t", "--test", action="store_true", dest="test",
+            default=False,
+            help="run through the update without changing anything")
+        parser.add_option("-y", dest="pwd_file",
+            help="file containing the Directory Manager password")
+        parser.add_option("-s", '--server',
+            dest="host", default='localhost',
+            help="server to apply topology changes "
+                "(implied when no host is given)")
+        parser.add_option("-W", '--password',
+            dest="password",
+            help="prompt for the Directory Manager password")
+        parser.add_option('--replica',
+            dest="replica", default='dc=example,dc=com',
+            help="replica suffix")
+        parser.add_option("-n", '--name',
+            dest="name",
+            help="naming value of a segment or replica")
+        parser.add_option("-r", '--right',
+            dest="seg_right",
+            help="right node of a connection segment")
+        parser.add_option("-l", '--left',
+            dest="seg_left",
+            help="left node of a connection segment")
+        parser.add_option('--direct',
+            dest="seg_direct", default='both',
+            help="connection direction of a segment")
+        parser.add_option("-p", '--prop', action='append',
+            dest="properties", default=[],
+            help="properties of a segment, define replication agreement attributes")
+
+    @classmethod
+    def get_command_class(cls, options, args):
+        commands = {
+           "view": TopologyManageView,
+           "init": TopologyManageInit,
+           "verify": TopologyManageVerify,
+           "seg-add": TopologyManageSegmentAdd,
+           "seg-del": TopologyManageSegmentDel,
+           "seg-mod": TopologyManageSegmentMod
+        }
+        if len(args) == 1:
+            return commands[args[0]]
+        else:
+            return TopologyManageUsage
+
+    def check_topology_plugin_deployed(self, hostname, realm, dirman_passwd):
+        """
+        Check the topology management plugin exists and is enabled.
+        If it is enabled then check if it manages the given database 
+        """
+        conn = ipaldap.IPAdmin(hostname, realm=realm)
+        if dirman_passwd:
+            conn.do_simple_bind(bindpw=dirman_passwd)
+        else:
+            conn.do_sasl_gssapi_bind()
+        entry = conn.get_entry(DN(('cn', 'IPA Topology Configuration'),
+                                  ('cn', 'plugins'),
+                                  ('cn', 'config')))
+        self.conn = conn
+        return entry
+
+    def gen_name(self):
+        if self.options.name:
+            return self.options.name
+        else:
+            (h1,d1) = self.options.seg_left.split('.',1)
+            (h2,d2) = self.options.seg_right.split('.',1)
+            name = "%s-%s" % (h1,h2)
+            return name
+
+    def find_replica(self, base):
+        options = self.options
+        conn = self.conn
+        filter = "(&(objectclass=%s)(%s=%s))" % (TOPO_CONF_REPLICA_OBJECTCLASS, TOPO_CONF_REPLICA_ROOT_ATTR, options.replica)
+        try:
+            replica = conn.get_entries(DN(base), filter=filter, scope=ldap.SCOPE_SUBTREE)
+        except errors.NotFound:
+            pass  # no entry yet
+        else:
+            if len(replica) > 1:
+                raise admintool.ScriptError('Configuration Error: More than one configuration entry for replica exists', 1)
+            dn = replica[0].dn
+
+        print "replica conf entry is: %s" % dn
+        return replica[0]
+
+    def find_segment(self, replica):
+        options = self.options
+        conn = self.conn
+
+        if replica:
+            if options.name:
+                filter = "(cn=%s)" % options.name
+            else:
+                filter = "(&(%s=%s)(%s=%s))" % (TOPO_CONF_SEGM_LEFT, options.seg_left, TOPO_CONF_SEGM_RIGHT, options.seg_right)
+            try:
+                segment = conn.get_entries(replica.dn, filter=filter, scope=ldap.SCOPE_ONELEVEL)
+            except errors.NotFound:
+                seg = None
+                pass
+            else:
+                if len(segment) > 1:
+                    raise admintool.ScriptError('Configuration Error: More than one segment entry exists', 1)
+                seg = segment[0]
+        return seg
+
+    def run(self):
+        options = self.options
+        self.realm = krbV.default_context().default_realm
+        super(TopologyManage, self).run()
+
+class TopologyManageUsage(TopologyManage):
+
+    def run(self):
+        super(TopologyManageUsage, self).run()
+        options = self.options
+
+        print "ERROR: ipa-topology-manage requires a subcommand"
+        print "USAGE: ipa-topology-manage subcommand [options]"
+        print "valid subcommands are:"
+        print "    init: initialize shared topology configuration"
+        print "    seg-add: add a connection segment to the shared topology"
+        print "    seg-del: delete a connection segment from the shared topology"
+        print "    seg-mod: modify a connection segment in the shared topology"
+        print "    verify: verify connectivity of the shared topology configuration"
+
+class TopologyManageView(TopologyManage):
+
+    def validate_options(self):
+        super(TopologyManageView, self).validate_options()
+
+    def validate_conf(self):
+        dn = "cn=topology,%s" % self.base
+        topo = self.conn.get_entry(DN(dn))
+        if topo:
+            return True
+        else:
+            return False
+
+    def show_servers(self):
+        dn = "cn=masters,%s" % self.base
+        servers = self.conn.get_entries(DN(dn), ldap.SCOPE_ONELEVEL)
+        if servers:
+            for server in servers:
+                print server.dn
+
+    def show_replicas(self):
+        dn = "cn=topology,%s" % self.base
+        replicas = self.conn.get_entries(DN(dn), ldap.SCOPE_ONELEVEL)
+        if replicas:
+            for replica in replicas:
+                cns = replica.get('cn')
+                sfs = replica.get('ipaReplTopoConfRoot')
+                print cns[0]," : ",sfs[0]
+                self.show_segments(replica.dn)
+
+    def show_segments(self, replicadn):
+        try:
+            segments = self.conn.get_entries(replicadn, ldap.SCOPE_ONELEVEL)
+        except errors.NotFound:
+            pass  # no entry yet
+        else:
+            for segment in segments:
+                cns = segment.get('cn')
+                lhs = segment.get('ipaReplTopoSegmentLeftNode')
+                rhs = segment.get('ipaReplTopoSegmentRightNode')
+                print cns[0], ":",lhs[0],"<-->",rhs[0]
+
+    def run(self):
+        print ">>> ipa-topology-manage VIEW subcommand"
+        super(TopologyManageView, self).run()
+        options = self.options
+
+        conf = self.check_topology_plugin_deployed(options.host, self.realm, options.password)
+        if conf:
+            print "topology plugin deployed. Check configuration"
+            conf_base = conf.single_value.get(TOPO_CONF_BASE_ATTR)
+            if  not conf_base:
+                print "ERROR: config base in shared tree not defined"
+            else:
+                self.base = conf_base
+                if self.validate_conf():
+                    print "------- managed hosts ------------"
+                    self.show_servers()
+                    print "------- managed replicas ---------"
+                    self.show_replicas()
+        else:
+            print "topology plugin not deplyed."
+            print "use ipa-manage-replica to manage replication"
+
+        print "<<< ipa-topology-manage VIEW subcommand"
+
+class TopologyManageInit(TopologyManage):
+
+    def validate_options(self):
+        super(TopologyManageInit, self).validate_options()
+
+    def init_conf(self):
+        dn = "cn=topology,%s" % self.base
+
+        try:
+            topo = self.conn.get_entry(DN(dn))
+        except errors.NotFound:
+            # create top entry
+            entry = self.conn.make_entry( DN(dn),
+                objectclass=["nsContainer"],
+                cn=['topology']
+                )
+            self.conn.add_entry(entry)
+
+    def add_repl_conf(self, name, suffix):
+        dn = "cn=%s,cn=topology,%s" % (name,self.base)
+        entry = self.conn.make_entry( DN(dn),
+            objectclass=["ipaReplTopoConf"],
+            ipaReplTopoConfRoot=[suffix],
+            cn=[name]
+            )
+        self.conn.add_entry(entry)
+
+
+    def run(self):
+        super(TopologyManageInit, self).run()
+        options = self.options
+
+        conf = self.check_topology_plugin_deployed(options.host, self.realm, options.password)
+        if conf:
+            conf_base = conf.single_value.get(TOPO_CONF_BASE_ATTR)
+            self.base = conf_base
+            self.init_conf()
+            if options.name and options.replica:
+                self.add_repl_conf(options.name,options.replica)
+
+        print "ipa-topology-manage INIT subcommand"
+
+class TopologyManageVerify(TopologyManage):
+
+    def validate_options(self):
+        super(TopologyManageVerify, self).validate_options()
+
+    def run(self):
+        super(TopologyManageVerify, self).run()
+        options = self.options
+        print "ipa-topology-manage VERIFY subcommand"
+        print "topology verification is not yet supported"
+
+class TopologyManageSegmentAdd(TopologyManage):
+
+    def validate_options(self):
+        super(TopologyManageSegmentAdd, self).validate_options()
+
+    def run(self):
+        super(TopologyManageSegmentAdd, self).run()
+        options = self.options
+
+        if len(options.properties) > 0:
+            segment_params = dict(elem.split('=', 1) for elem in options.properties)
+        else:
+            segment_params = segment_defaults
+
+        conf = self.check_topology_plugin_deployed(options.host, self.realm, options.password)
+        if conf:
+            print "topology plugin deployed. Check configuration"
+            conf_base = conf.single_value.get(TOPO_CONF_BASE_ATTR)
+            if  not conf_base:
+                print "ERROR: config base in shared tree not defined"
+                raise admintool.ScriptError('No configuration base in topology plugin found.',1)
+            else:
+                self.base = conf_base
+                replica = self.find_replica(conf_base)
+                if (replica):
+                    segm = self.find_segment(replica)
+        else:
+            print "topology plugin not deployed."
+            print "use ipa-manage-replica to manage replication"
+
+        if segm:
+            raise admintool.ScriptError('Segment already exists.',1)
+        name = self.gen_name()
+        entry = self.conn.make_entry(
+            DN(('cn',name),replica.dn),
+            objectclass=["ipaReplTopoSegment"],
+            ipaReplTopoSegmentLeftNode=[options.seg_left],
+            ipaReplTopoSegmentRightNode=[options.seg_right],
+            ipaReplTopoSegmentDirection=[options.seg_direct],
+            cn=[name]
+        )
+        for attr in segment_params:
+            entry[segment_attr_type[attr]] = [segment_params[attr]]
+        self.conn.add_entry(entry)
+
+        print "ipa-topology-manage SEGMENT ADD subcommand"
+
+class TopologyManageSegmentDel(TopologyManage):
+
+    def validate_options(self):
+        super(TopologyManageSegmentDel, self).validate_options()
+
+    def run(self):
+        super(TopologyManageSegmentDel, self).run()
+        options = self.options
+
+        conf = self.check_topology_plugin_deployed(options.host, self.realm, options.password)
+        conf_base = conf.single_value.get(TOPO_CONF_BASE_ATTR)
+        self.base = conf_base
+        replica = self.find_replica(conf_base)
+        if (replica):
+            segm = self.find_segment(replica)
+
+        if segm:
+            self.conn.delete_entry(segm.dn);
+
+        print "ipa-topology-manage SEGMENT DEL subcommand"
+
+class TopologyManageSegmentMod(TopologyManage):
+
+    def validate_options(self):
+        super(TopologyManageSegmentMod, self).validate_options()
+
+    def run(self):
+        super(TopologyManageSegmentMod, self).run()
+        options = self.options
+
+        print "ipa-topology-manage SEGMENT MOD subcommand"
-- 
1.9.0

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to