diff options
author | Ludwig Krispenz <lkrispen@redhat.com> | 2015-05-20 17:28:39 +0200 |
---|---|---|
committer | Petr Vobornik <pvoborni@redhat.com> | 2015-05-26 10:40:29 +0200 |
commit | 25bf0c6e78dca62e7fa11cd654ed0d8675408176 (patch) | |
tree | 732cda7366308c4e7e8920ac0e922c8dfedbc9c6 | |
parent | c5f319d3e8d1b5e500225af207ac247f75baea17 (diff) | |
download | freeipa-25bf0c6e78dca62e7fa11cd654ed0d8675408176.tar.gz freeipa-25bf0c6e78dca62e7fa11cd654ed0d8675408176.tar.xz freeipa-25bf0c6e78dca62e7fa11cd654ed0d8675408176.zip |
ds plugin - manage replication topology in the shared tree
Implementation of ticket: https://fedorahosted.org/freeipa/ticket/4302
Design page: http://www.freeipa.org/page/V4/Manage_replication_topology
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
-rw-r--r-- | daemons/configure.ac | 1 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/Makefile.am | 1 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/Makefile.am | 51 | ||||
-rwxr-xr-x | daemons/ipa-slapi-plugins/topology/ipa-topology-conf.ldif | 20 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology.h | 302 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology_agmt.c | 314 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology_cfg.c | 888 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology_init.c | 313 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology_post.c | 272 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology_pre.c | 458 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/topology/topology_util.c | 1441 | ||||
-rw-r--r-- | freeipa.spec.in | 2 | ||||
-rw-r--r-- | install/share/70topology.ldif | 15 | ||||
-rw-r--r-- | install/share/Makefile.am | 1 |
14 files changed, 4079 insertions, 0 deletions
diff --git a/daemons/configure.ac b/daemons/configure.ac index 7c979fe2d..f2eebee51 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -362,6 +362,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 07733921e..39f9142fd 100644 --- a/daemons/ipa-slapi-plugins/Makefile.am +++ b/daemons/ipa-slapi-plugins/Makefile.am @@ -16,6 +16,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 000000000..8f3fa3b00 --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/Makefile.am @@ -0,0 +1,51 @@ +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_cfg.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 000000000..9b7e5ebf8 --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/ipa-topology-conf.ldif @@ -0,0 +1,20 @@ +dn: cn=IPA Topology Configuration,cn=plugins,cn=config +changetype: add +objectClass: top +objectClass: nsSlapdPlugin +objectClass: extensibleObject +cn: IPA Topology Configuration +nsslapd-pluginPath: libtopology +nsslapd-pluginInitfunc: ipa_topo_init +nsslapd-pluginType: object +nsslapd-pluginEnabled: on +nsslapd-topo-plugin-shared-config-base: cn=ipa,cn=etc,$SUFFIX +nsslapd-topo-plugin-shared-replica-root: $SUFFIX +nsslapd-topo-plugin-shared-binddngroup: cn=replication managers,cn=etc,$SUFFIX +nsslapd-topo-plugin-startup-delay: 20 +nsslapd-pluginId: none +nsslapd-plugin-depends-on-named: ldbm database +nsslapd-plugin-depends-on-named: Multimaster Replication Plugin +nsslapd-pluginVersion: 1.0 +nsslapd-pluginVendor: none +nsslapd-pluginDescription: none diff --git a/daemons/ipa-slapi-plugins/topology/topology.h b/daemons/ipa-slapi-plugins/topology/topology.h new file mode 100644 index 000000000..38c2823f5 --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology.h @@ -0,0 +1,302 @@ + +/** + * 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 "1.0" + +#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 IPA_TOPO_INTERNAL_POSTOP_DESC "ipa-topology-internal-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 SEGMENT_LEFT_RIGHT 0x01 +#define SEGMENT_RIGHT_LEFT 0x02 +#define SEGMENT_BIDIRECTIONAL 0x03 +#define SEGMENT_OBSOLETE 0x04 +#define SEGMENT_AUTOGEN 0x05 +#define SEGMENT_REMOVED 0x06 +#define SEGMENT_OBSOLETE_STR "obsolete" +#define SEGMENT_AUTOGEN_STR "autogen" +#define SEGMENT_REMOVED_STR "removed" +#define TOPO_IGNORE_ENTRY 0 +#define TOPO_CONFIG_ENTRY 1 +#define TOPO_SEGMENT_ENTRY 2 +#define TOPO_HOST_ENTRY 3 +#define TOPO_DOMLEVEL_ENTRY 4 + +typedef struct topo_replica_agmt { + char *rdn; + 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 { + char *name; + int direct; + char *from; + char *to; + int state; + TopoReplicaAgmt *left; + TopoReplicaAgmt *right; +} TopoReplicaSegment; + +typedef struct topo_replica_segment_list { + struct topo_replica_segment_list *next; + TopoReplicaSegment *segm; + int visited; +} TopoReplicaSegmentList; + +typedef struct topo_replica_host { + struct topo_replica_host *next; + char *hostname; +} TopoReplicaHost; + +typedef struct topo_replica { + struct topo_replica *next; + Slapi_Mutex *repl_lock; + char *shared_config_base; + Slapi_DN *shared_config_sdn; + char *repl_root; + TopoReplicaSegmentList *repl_segments; + TopoReplicaHost *hosts; +} TopoReplica; + +typedef struct topo_replica_conf { + Slapi_Mutex *conf_lock; + int startup_inprogress; + TopoReplica *replicas; + TopoReplicaHost *allhosts; /* maybe not needed */ +} TopoReplicaConf; + +typedef struct topo_plugin_config { + Slapi_Mutex *plg_lock; + void *identity; + int version_major; + int version_minor; + int startup_delay; + char *hostname; + char *shared_config_base; + char *shared_topo; + Slapi_DN *shared_topo_sdn; + char *shared_hosts; + Slapi_DN *shared_hosts_sdn; + char *shared_bindgroup; + Slapi_DN *shared_bindgroup_sdn; + char *domain_level; + Slapi_DN *domain_level_sdn; + char **shared_replica_root; + char **managed_attrs; + char **restricted_attrs; + int activated; +} TopoPluginConf; + +typedef struct ipa_domain_level { + int major; + int minor; +} IpaDomainLevel; + +#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_SHARED_BINDDNGROUP "nsslapd-topo-plugin-shared-binddngroup" +#define CONFIG_ATTR_STARTUP_DELAY "nsslapd-topo-plugin-startup-delay" +#define CONFIG_ATTR_PLUGIN_ACTIVE "nsslapd-topo-plugin-activated" +#define CONFIG_ATTR_PLUGIN_VERSION "nsslapd-pluginVersion" + +/* 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); +int ipa_topo_get_plugin_active(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); +Slapi_DN *ipa_topo_get_plugin_shared_bindgroup_dn(void); +char *ipa_topo_get_plugin_shared_topo(void); +char *ipa_topo_get_plugin_shared_hosts(void); +char *ipa_topo_get_plugin_shared_bindgroup(void); +char *ipa_topo_get_plugin_hostname(void); +char **ipa_topo_get_plugin_replica_root(void); +char **ipa_topo_get_plugin_managed_attrs(void); +char **ipa_topo_get_plugin_restricted_attrs(void); +int ipa_topo_get_plugin_version_major(void); +int ipa_topo_get_plugin_version_minor(void); +char *ipa_topo_get_domain_level_entry(void); +Slapi_DN *ipa_topo_get_domain_level_entry_dn(void); +int ipa_topo_get_domain_level_major(void); +int ipa_topo_get_domain_level_minor(void); +int ipa_topo_get_plugin_startup_delay(void); +void ipa_topo_set_plugin_id(void *plg_id); +void ipa_topo_set_plugin_active(int state); +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_set_plugin_restricted_attrs(char **mattrs); +void ipa_topo_set_plugin_startup_delay(char *delay); +void ipa_topo_set_plugin_version(char *version); +int ipa_topo_cfg_plugin_suffix_is_managed(const char *be_suffix); +void ipa_topo_free_plugin_config(void); +void ipa_topo_set_domain_level(char *level); +void ipa_topo_util_check_plugin_active(void); +void ipa_topo_lock_conf(void); +void ipa_topo_unlock_conf(void); +int ipa_topo_acquire_startup_inprogress(void); +void ipa_topo_release_startup_inprogress(void); +void ipa_topo_cfg_host_add(Slapi_Entry *hostentry); +void ipa_topo_cfg_host_del(Slapi_Entry *hostentry); +TopoReplicaHost *ipa_topo_cfg_host_find(TopoReplica *tconf, char *host, int lock); +TopoReplicaHost *ipa_topo_cfg_host_new(char *newhost); +TopoReplicaSegment * +ipa_topo_cfg_segment_find(char *repl_root, char *leftHost, char *rightHost); +TopoReplicaSegment * +ipa_topo_cfg_replica_segment_find(TopoReplica *tconf, char *leftHost, + char *rightHost, int lock); +void ipa_topo_cfg_segment_set_visited(TopoReplica *tconf, TopoReplicaSegment *tsegm); +void ipa_topo_cfg_segment_add(TopoReplica *tconf, TopoReplicaSegment *tsegm); +void ipa_topo_cfg_segment_del(TopoReplica *tconf, TopoReplicaSegment *tsegm); +void ipa_topo_cfg_segment_free(TopoReplicaSegment *tsegm); +TopoReplicaSegment *ipa_topo_cfg_segment_dup(TopoReplicaSegment *orig); +TopoReplicaAgmt *ipa_topo_cfg_agmt_dup(TopoReplicaAgmt *agmt); +TopoReplicaAgmt *ipa_topo_cfg_agmt_dup_reverse(TopoReplicaAgmt *agmt); +TopoReplica *ipa_topo_cfg_replica_new(void); +int ipa_topo_cfg_replica_add(TopoReplica *tconf); +void ipa_topo_cfg_replica_del(TopoReplica *tconf); +void ipa_topo_cfg_replica_free(TopoReplica *tconf); +TopoReplica *ipa_topo_cfg_replica_find(char *repl_root, int lock); + +/* pre and postop plugin functions */ +int ipa_topo_check_entry_type(Slapi_Entry *entry); +/* 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, TopoReplica *repl_conf, + TopoReplicaAgmt *agmt); +int ipa_topo_agmt_del_dn(char *dn); +int ipa_topo_agmt_del(char *hostname, TopoReplica *conf, + TopoReplicaAgmt *agmt); +int ipa_topo_agmt_mod(TopoReplica *conf, TopoReplicaAgmt *agmt, + LDAPMod **mod, char *direction); +int ipa_topo_agmt_setup(char *hostname, TopoReplica *repl_conf, + TopoReplicaAgmt *agmt, int isgssapi); +int ipa_topo_setup_std_agmt(char *hostname, TopoReplica *repl_conf, + TopoReplicaAgmt *agmt); +int ipa_topo_setup_gssapi_agmt(char *hostname, TopoReplica *repl_conf, + TopoReplicaAgmt *agmt); +void ipa_topo_queue_apply_shared_config(time_t event_time, void *arg); +int ipa_topo_apply_shared_config(void); +int ipa_topo_setup_managed_servers(void); +int ipa_topo_util_start(int delay); +int ipa_topo_util_update_agmt_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *repl_segments); +char *ipa_topo_agmt_gen_rdn(char *from, char *to); +char *ipa_topo_agmt_std_rdn(char *to); +char *ipa_topo_agreement_dn(TopoReplica *conf, TopoReplicaAgmt *agmt, char *rdn); +char *ipa_topo_segment_dn(TopoReplica *tconf, char *segname); +void ipa_topo_util_segment_update(TopoReplica *repl_conf, + TopoReplicaSegment *repl_segment, + LDAPMod **mods ,char *fromHost); +void ipa_topo_util_missing_agmts_add(TopoReplica *repl_conf, + TopoReplicaSegment *repl_segment, + char *fromHost); +void ipa_topo_util_existing_agmts_del(TopoReplica *repl_conf, + TopoReplicaSegment *repl_segment, + char *fromHost); +void ipa_topo_util_existing_agmts_update(TopoReplica *repl_conf, + TopoReplicaSegment *repl_segment, + LDAPMod **mods ,char *fromHost); +void ipa_topo_util_missing_agmts_add_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *repl_segments, + char *fromHost); +void ipa_topo_util_existing_agmts_del_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *repl_segments, + char *fromHost); +void ipa_topo_util_existing_agmts_update_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *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(TopoReplicaSegmentList *repl_segments, + char *fromHost, char *toHost); +void ipa_topo_util_segm_update(TopoReplica *tconf, TopoReplicaSegment *tsegm, + int property); +int ipa_topo_util_segment_write(TopoReplica *tconf, TopoReplicaSegment *tsegm); +void ipa_topo_util_segm_remove(TopoReplica *tconf, TopoReplicaSegment *tsegm); +void ipa_topo_util_segment_merge(TopoReplica *tconf, + TopoReplicaSegment *tsegm); +int ipa_topo_util_agmt_mark(TopoReplica *tconf, Slapi_Entry * repl_agmt, + TopoReplicaSegment *tsegm); +int ipa_topo_util_agmt_is_marked(Slapi_Entry * repl_agmt); +char *ipa_topo_agmt_attr_is_managed(char *type, char *direction); +int ipa_topo_cfg_attr_is_restricted(char *type); +int ipa_topo_util_setup_servers(void); +void ipa_topo_util_update_segments_for_host(Slapi_Entry *hostentry); +char *ipa_topo_util_get_ldap_principal(char *repl_root, char *hostname); +void ipa_topo_util_disable_repl_for_principal(char *repl_root, char *principal); +void ipa_topo_util_delete_host(Slapi_Entry *hostentry); +void ipa_topo_util_disable_repl_from_host(char *repl_root, char *delhost); +void ipa_topo_util_delete_segments_for_host(char *repl_root, char *delhost); + +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_segm_from_agmt(Slapi_Entry *repl_agmt); +TopoReplicaSegment *ipa_topo_util_segment_from_entry(TopoReplica *conf, + Slapi_Entry *entry); +TopoReplicaSegment *ipa_topo_util_find_segment(TopoReplica *conf, + Slapi_Entry *entry); +TopoReplica *ipa_topo_util_conf_from_entry(Slapi_Entry *entry); +TopoReplica *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, Slapi_Mods *smods); +char *ipa_topo_util_get_pluginhost(void); +TopoReplica *ipa_topo_util_get_replica_conf(char *repl_root); +TopoReplicaSegmentList *ipa_topo_util_get_replica_segments(TopoReplica *replica); +void ipa_topo_util_set_domain_level(void); 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 000000000..57614351e --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology_agmt.c @@ -0,0 +1,314 @@ +#include "topology.h" + +/* generate the dn for a topology segment by providing replroot and segment name */ +char * +ipa_topo_segment_dn(TopoReplica *tconf, char *segname) +{ + char *dn = NULL; + + dn = slapi_ch_smprintf("cn=%s,%s", segname, tconf->shared_config_base); + return dn; +} + +/* generate the rdn for a replication agreement by providing connected nodes */ +char * +ipa_topo_agmt_gen_rdn(char *from, char *to) +{ + char *agmt_rdn = slapi_ch_smprintf("cn=%s-to-%s", from, to); + + return agmt_rdn; +} + +/* generate the rdn for a replication agreement by providing target node */ +char * +ipa_topo_agmt_std_rdn(char *to) +{ + char *agmt_rdn = slapi_ch_smprintf("cn=meTo%s", to); + + return agmt_rdn; +} + +/* generate the dn for a replication agreement by providing replroot and host */ +char * +ipa_topo_agreement_dn(TopoReplica *conf, TopoReplicaAgmt *agmt, char *rdn) +{ + 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_PLUGIN_SUBSYSTEM, + "ipa_topo_agreement_dn: no replica found\n"); + dn = NULL; + } else if (rdn) { + dn = slapi_ch_smprintf("%s,%s", rdn, + slapi_entry_get_dn_const(entries[0])); + } else { + dn = slapi_ch_smprintf("cn=meTo%s,%s", agmt->target, + 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, TopoReplica *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(TopoReplica *conf, TopoReplicaAgmt *agmt, LDAPMod **mods, + char *direction) +{ + int ret; + Slapi_PBlock *pb; + char *dn = NULL; + Slapi_Entry **entries; + int i; + LDAPMod *tmp; + Slapi_Mods *smods = NULL; + + dn = ipa_topo_agreement_dn(conf, agmt, agmt->rdn); + 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 */ + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_agmt_mod: agreement not found: %s\n", dn); + 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) { + Slapi_DN *sdn = slapi_sdn_new_normdn_byref(dn); + ipa_topo_util_modify(sdn, smods); + slapi_sdn_free(&sdn); + } else { + slapi_ch_free_string(&dn); + } + slapi_mods_free(&smods); +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, TopoReplica *conf, TopoReplicaAgmt *agmt) +{ + char *dn = NULL; + int rc; + + dn = ipa_topo_agreement_dn(conf, agmt, agmt->rdn); + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_agmt_del: %s\n", agmt->rdn?agmt->rdn:"RDN missing"); + if (dn == NULL) + return (-1); + + rc = ipa_topo_agmt_del_dn(dn); + slapi_ch_free_string(&dn); + + return rc; +} + +int +ipa_topo_agmt_del_dn(char *dn) +{ + int ret = 0; + Slapi_PBlock *pb; + 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); + + return ret; +} +int +ipa_topo_agmt_setup(char *hostname, TopoReplica *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 */ + agmt->rdn = ipa_topo_agmt_gen_rdn(agmt->origin, agmt->target); + dn = ipa_topo_agreement_dn(conf, agmt, agmt->rdn); + 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"); + slapi_entry_add_string(e, SLAPI_ATTR_OBJECTCLASS, "ipaReplTopoManagedAgreement"); + cn = slapi_ch_smprintf("%s-to-%s", agmt->origin, agmt->target); + slapi_entry_add_string(e, "cn",cn); + slapi_ch_free_string(&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("%s to %s", ipa_topo_get_plugin_hostname(), hostname); + slapi_entry_add_string(e, "description",description); + slapi_ch_free_string(&description); + slapi_entry_add_string(e, "ipaReplTopoManagedAgreementState", + "managed agreement - generated by topology plugin"); + + 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->repl_attrs) { + slapi_entry_add_string(e, "nsDS5ReplicatedAttributeList",agmt->repl_attrs); + } else { + slapi_entry_add_string(e, "nsDS5ReplicatedAttributeList", REPL_ATTR_LIST); + } + if (agmt->strip_attrs) { + slapi_entry_add_string(e, "nsds5ReplicaStripAttrs", agmt->strip_attrs); + } else { + slapi_entry_add_string(e, "nsds5ReplicaStripAttrs", REPL_ATTR_STRIP); + } + if (agmt->total_attrs) { + slapi_entry_add_string(e, "nsDS5ReplicatedAttributeListTotal", + agmt->total_attrs); + } else { + slapi_entry_add_string(e, "nsDS5ReplicatedAttributeListTotal", + REPL_ATTR_LIST_TOTAL); + } + + 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, + TopoReplica *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) { + dn = ipa_topo_agreement_dn(conf, agmt, agmt->rdn); + Slapi_DN *sdn = slapi_sdn_new_normdn_byref(dn); + ipa_topo_util_modify(sdn, smods); + slapi_sdn_free(&sdn); + } + slapi_mods_free(&smods); + 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 000000000..17493495a --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology_cfg.c @@ -0,0 +1,888 @@ + +#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 = {0}; +static TopoReplicaConf topo_shared_conf = {0}; +static IpaDomainLevel ipa_domain_level = {0,0}; + +char *ipa_topo_plugin_managed_attrs[] = { + "nsds5ReplicaStripAttrs", + "nsds5ReplicatedAttributeList", + "nsDS5ReplicatedAttributeListTotal", + "nsds5BeginReplicaRefresh", + "nsds5replicaTimeout", + "nsds5ReplicaEnabled", + "nsds5replicaSessionPauseTime", + "nsds5replicabinddn", + "nsds5replicacredentials", + "nsds5replicatransportinfo", + "nsds5replicabindmethod", + NULL }; + +/* subset of attrs which can only be modified via + * modification of segments in the shared tree. + * Other attributes still can be directly modified + * eg to reinit a replica or change bind method and + * credentials. + * This is currently needed to make ipa-replica-install work + */ +char *ipa_topo_plugin_restricted_attrs[] = { + "nsds5ReplicaStripAttrs", + "nsds5ReplicatedAttributeList", + "nsDS5ReplicatedAttributeListTotal", + "nsds5replicaTimeout", + "nsds5replicaSessionPauseTime", + 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_restricted_attrs(void) +{ + return topo_plugin_conf.restricted_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_domain_level_entry(void) +{ + return topo_plugin_conf.domain_level; +} + +Slapi_DN * +ipa_topo_get_domain_level_entry_dn(void) +{ + return topo_plugin_conf.domain_level_sdn; +} + +int +ipa_topo_get_domain_level_major(void) +{ + return ipa_domain_level.major; +} + +int +ipa_topo_get_domain_level_minor(void) +{ + return ipa_domain_level.minor; +} + +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_shared_bindgroup(void) +{ + return topo_plugin_conf.shared_bindgroup; +} + +Slapi_DN * +ipa_topo_get_plugin_shared_bindgroup_dn(void) +{ + return topo_plugin_conf.shared_bindgroup_sdn; +} +char ** +ipa_topo_get_plugin_replica_root(void) +{ + return topo_plugin_conf.shared_replica_root; +} + +int +ipa_topo_get_plugin_version_major(void) +{ + return topo_plugin_conf.version_major; +} + +int +ipa_topo_get_plugin_version_minor(void) +{ + return topo_plugin_conf.version_minor; +} + +int +ipa_topo_get_plugin_startup_delay(void) +{ + return topo_plugin_conf.startup_delay; +} + +void +ipa_topo_set_plugin_id(void *plg_id) +{ + topo_plugin_conf.identity = plg_id; +} + +void +ipa_topo_set_plugin_active(int state) +{ + topo_plugin_conf.activated = state; +} +int +ipa_topo_get_plugin_active(void) +{ + return topo_plugin_conf.activated; +} + +void +ipa_topo_set_plugin_shared_config(char *cfg) +{ + char *topo; + char *hosts; + char *domain_level; + 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); + domain_level = slapi_ch_smprintf("%s,%s","cn=domain level",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); + topo_plugin_conf.domain_level = domain_level; + topo_plugin_conf.domain_level_sdn = slapi_sdn_new_normdn_byref(domain_level); + +} + +void +ipa_topo_set_plugin_shared_bindgroup(char *bindgroup) +{ + topo_plugin_conf.shared_bindgroup = bindgroup; + topo_plugin_conf.shared_bindgroup_sdn = slapi_sdn_new_normdn_byref(bindgroup); +} + +void +ipa_topo_set_domain_level(char *level) +{ + char *minor; + + if (level == NULL) { + ipa_domain_level.major = 0; + ipa_domain_level.minor = 0; + return; + } + + minor = strchr(level,'.'); + if (minor) { + *minor = '\0'; + ipa_domain_level.minor = atoi(++minor); + } else { + ipa_domain_level.minor = 0; + } + ipa_domain_level.major = atoi(level); +} + +void +ipa_topo_set_plugin_hostname(char *hostname) +{ + topo_plugin_conf.hostname = hostname; +} + +#define TOPO_PLUGIN_DEFAULT_STARTUP_DELAY 20 +void +ipa_topo_set_plugin_startup_delay(char *delay) +{ + if (delay) { + topo_plugin_conf.startup_delay = atoi(delay); + } else { + topo_plugin_conf.startup_delay = TOPO_PLUGIN_DEFAULT_STARTUP_DELAY; + } +} + +void +ipa_topo_set_plugin_version(char *version) +{ + char *minor; + + if ( version == NULL) { + topo_plugin_conf.version_major = 0; + topo_plugin_conf.version_minor = 0; + return; + } + + minor = strchr(version,'.'); + if (minor) { + *minor = '\0'; + topo_plugin_conf.version_minor = atoi(++minor); + } else { + topo_plugin_conf.version_minor = 0; + } + topo_plugin_conf.version_major = atoi(version); +} + +void +ipa_topo_init_shared_config(void) +{ + topo_shared_conf.allhosts = 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_restricted_attrs(char **attrs) +{ + if (attrs) { + topo_plugin_conf.restricted_attrs = attrs; + } else { + topo_plugin_conf.restricted_attrs = ipa_topo_plugin_restricted_attrs; + } +} + +int +ipa_topo_cfg_plugin_suffix_is_managed(const char *be_suffix) { + + int i = 0; + char **shared_replica_root = ipa_topo_get_plugin_replica_root(); + + while (shared_replica_root[i]) { + if (0 == strcasecmp(shared_replica_root[i], be_suffix)) return 1; + i++; + } + return 0; +} + +int +ipa_topo_cfg_attr_is_restricted(char *type) +{ + int i; + int rc = 0; + char **rattrs = ipa_topo_get_plugin_restricted_attrs(); + for (i=0; rattrs[i]; i++) { + if(0 == strcasecmp(rattrs[i], type)) { + rc = 1; + break; + } + } + return rc; +} + +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 *startup_delay; + char *plugin_version; + char *bindgroup; + 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; + } + + ipa_topo_set_plugin_active(0); + + 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); + } + + bindgroup = slapi_entry_attr_get_charptr(plugin_entry, + CONFIG_ATTR_SHARED_BINDDNGROUP); + if(bindgroup){ + ipa_topo_set_plugin_shared_bindgroup(bindgroup); + } + + startup_delay = slapi_entry_attr_get_charptr(plugin_entry, + CONFIG_ATTR_STARTUP_DELAY); + ipa_topo_set_plugin_startup_delay(startup_delay); + slapi_ch_free_string(&startup_delay); + + plugin_version = slapi_entry_attr_get_charptr(plugin_entry, + CONFIG_ATTR_PLUGIN_VERSION); + ipa_topo_set_plugin_version(plugin_version); + slapi_ch_free_string(&plugin_version); + + ipa_topo_util_set_domain_level(); + + ipa_topo_util_check_plugin_active(); + + + ipa_topo_set_plugin_managed_attrs(NULL); /* use defaults */ + ipa_topo_set_plugin_restricted_attrs(NULL); /* use defaults */ + return 0; + +} + +void +ipa_topo_free_plugin_config(void) +{ + slapi_destroy_mutex(topo_plugin_conf.plg_lock); + slapi_ch_free((void **)topo_plugin_conf.identity); + slapi_ch_free_string(&topo_plugin_conf.hostname); + slapi_ch_free_string(&topo_plugin_conf.shared_config_base); + slapi_ch_free_string(&topo_plugin_conf.shared_topo); + slapi_sdn_free(&topo_plugin_conf.shared_topo_sdn); + slapi_ch_free_string(&topo_plugin_conf.shared_hosts); + slapi_sdn_free(&topo_plugin_conf.shared_hosts_sdn); + slapi_ch_free_string(&topo_plugin_conf.shared_bindgroup); + slapi_sdn_free(&topo_plugin_conf.shared_bindgroup_sdn); + slapi_ch_free_string(&topo_plugin_conf.domain_level); + slapi_sdn_free(&topo_plugin_conf.domain_level_sdn); + slapi_ch_array_free(topo_plugin_conf.shared_replica_root); + if (ipa_topo_plugin_managed_attrs != topo_plugin_conf.managed_attrs) + slapi_ch_array_free(topo_plugin_conf.managed_attrs); + if (ipa_topo_plugin_restricted_attrs != topo_plugin_conf.restricted_attrs) + slapi_ch_array_free(topo_plugin_conf.restricted_attrs); +} + +void +ipa_topo_lock_conf(void) +{ + slapi_lock_mutex(topo_shared_conf.conf_lock); +} + +void +ipa_topo_unlock_conf(void) +{ + slapi_unlock_mutex(topo_shared_conf.conf_lock); +} + +int +ipa_topo_acquire_startup_inprogress(void) +{ + int acquired = 0; + slapi_lock_mutex(topo_shared_conf.conf_lock); + if (topo_shared_conf.startup_inprogress == 0 ) { + topo_shared_conf.startup_inprogress = 1; + acquired = 1; + } + slapi_unlock_mutex(topo_shared_conf.conf_lock); + return acquired; +} + +void +ipa_topo_release_startup_inprogress(void) +{ + slapi_lock_mutex(topo_shared_conf.conf_lock); + topo_shared_conf.startup_inprogress = 0; + slapi_unlock_mutex(topo_shared_conf.conf_lock); +} + +TopoReplicaHost * +ipa_topo_cfg_host_find(TopoReplica *tconf, char *findhost, int lock) +{ + TopoReplicaHost *host = NULL; + + if (tconf->hosts == NULL) return NULL; + + if (lock) slapi_lock_mutex(tconf->repl_lock); + for (host=tconf->hosts;host;host=host->next) { + if (!strcasecmp(host->hostname,findhost)) { + break; + } + } + if (lock) slapi_unlock_mutex(tconf->repl_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; + char **repl_root = NULL; + TopoReplicaHost *hostnode = NULL; + TopoReplica *replica = NULL; + int i; + + newhost = slapi_entry_attr_get_charptr(hostentry,"cn"); + if (newhost == NULL) return; + + repl_root = slapi_entry_attr_get_charray(hostentry,"ipaReplTopoManagedSuffix"); + if (repl_root == NULL || *repl_root == NULL) return; + + for (i=0; repl_root[i];i++) { + replica = ipa_topo_cfg_replica_find(repl_root[i], 1); + if (replica == NULL) continue; + + slapi_lock_mutex(replica->repl_lock); + if (ipa_topo_cfg_host_find(replica, newhost, 0)) { + /* log error */ + slapi_unlock_mutex(replica->repl_lock); + continue; + } + hostnode = ipa_topo_cfg_host_new(slapi_ch_strdup(newhost)); + hostnode->next = replica->hosts; + replica->hosts = hostnode; + slapi_unlock_mutex(replica->repl_lock); + } + + slapi_ch_array_free(repl_root); + slapi_ch_free_string(&newhost); + 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; + char **repl_root = NULL; + TopoReplica *replica = NULL; + int i; + + delhost = slapi_entry_attr_get_charptr(hostentry,"cn"); + if (delhost == NULL) return; + + repl_root = slapi_entry_attr_get_charray(hostentry,"ipaReplTopoManagedSuffix"); + if (repl_root == NULL || *repl_root == NULL) return; + + for (i=0; repl_root[i];i++) { + replica = ipa_topo_cfg_replica_find(repl_root[i], 1); + if (replica == NULL) continue; + + slapi_lock_mutex(replica->repl_lock); + hostnode = replica->hosts; + while (hostnode) { + if (!strcasecmp(hostnode->hostname,delhost)) { + /*remove from list and free*/ + if (prevnode) { + prevnode->next = hostnode->next; + } else { + replica->hosts = hostnode->next; + } + ipa_topo_cfg_host_free(&hostnode); + break; + } else { + prevnode = hostnode; + hostnode = hostnode->next; + } + } + slapi_unlock_mutex(replica->repl_lock); + } + + return; +} + +TopoReplicaSegment * +ipa_topo_cfg_replica_segment_find(TopoReplica *replica, char *leftHost, char *rightHost, int lock) +{ + TopoReplicaSegment *tsegm = NULL; + TopoReplicaSegmentList *segments = NULL; + + if (lock) slapi_lock_mutex(replica->repl_lock); + segments = replica->repl_segments; + while (segments) { + tsegm = segments->segm; + if ( (!strcasecmp(leftHost,tsegm->from) && !strcasecmp(rightHost,tsegm->to) && + (tsegm->direct == SEGMENT_BIDIRECTIONAL || tsegm->direct == SEGMENT_LEFT_RIGHT)) || + (!strcasecmp(leftHost,tsegm->to) && !strcasecmp(rightHost,tsegm->from) && + (tsegm->direct == SEGMENT_BIDIRECTIONAL || tsegm->direct == SEGMENT_RIGHT_LEFT))) { + break; + } + tsegm = NULL; + segments = segments->next; + } + if (lock) slapi_unlock_mutex(replica->repl_lock); + + return tsegm; +} + +TopoReplicaAgmt * +ipa_topo_cfg_agmt_dup(TopoReplicaAgmt *agmt) +{ + TopoReplicaAgmt *dup = NULL; + + if (agmt == NULL) return NULL; + + dup = (TopoReplicaAgmt *) slapi_ch_calloc(1,sizeof(TopoReplicaAgmt)); + dup->rdn = slapi_ch_strdup(agmt->rdn); + dup->origin = slapi_ch_strdup(agmt->origin); + dup->target = slapi_ch_strdup(agmt->target); + dup->enabled = slapi_ch_strdup(agmt->enabled); + dup->repl_root = slapi_ch_strdup(agmt->repl_root); + dup->strip_attrs = slapi_ch_strdup(agmt->strip_attrs); + dup->total_attrs = slapi_ch_strdup(agmt->total_attrs); + dup->repl_attrs = slapi_ch_strdup(agmt->repl_attrs); + dup->repl_pause = slapi_ch_strdup(agmt->repl_pause); + dup->repl_timeout = slapi_ch_strdup(agmt->repl_timeout); + dup->repl_refresh = slapi_ch_strdup(agmt->repl_refresh); + dup->repl_transport = slapi_ch_strdup(agmt->repl_transport); + dup->repl_bind_dn = slapi_ch_strdup(agmt->repl_bind_dn); + dup->repl_bind_cred = slapi_ch_strdup(agmt->repl_bind_cred); + dup->repl_bind_method = slapi_ch_strdup(agmt->repl_bind_method); + + return dup; +} + +TopoReplicaAgmt * +ipa_topo_cfg_agmt_dup_reverse(TopoReplicaAgmt *agmt) +{ + char *tmp; + TopoReplicaAgmt *dup = NULL; + dup = ipa_topo_cfg_agmt_dup(agmt); + + if (dup == NULL) return NULL; + + tmp = dup->origin; + dup->origin = dup->target; + dup->target = tmp; + + /* this is not enough, if a reverse agmt is + * created because segment becomes bidirectional + * we don't really know the rdn of the other direction + * As long as this info is not in the segment, + * assume std agmt naming and do best effort. + */ + + slapi_ch_free_string(&dup->rdn); + dup->rdn = ipa_topo_agmt_std_rdn(dup->target); + return dup; +} +static void +ipa_topo_cfg_agmt_done(TopoReplicaAgmt *agmt) +{ + if (agmt == NULL) return; + + slapi_ch_free_string(&agmt->origin); + slapi_ch_free_string(&agmt->target); + slapi_ch_free_string(&agmt->enabled); + slapi_ch_free_string(&agmt->repl_root); + slapi_ch_free_string(&agmt->strip_attrs); + slapi_ch_free_string(&agmt->total_attrs); + slapi_ch_free_string(&agmt->repl_attrs); + slapi_ch_free_string(&agmt->repl_pause); + slapi_ch_free_string(&agmt->repl_timeout); + slapi_ch_free_string(&agmt->repl_refresh); + slapi_ch_free_string(&agmt->repl_transport); + slapi_ch_free_string(&agmt->repl_bind_dn); + slapi_ch_free_string(&agmt->repl_bind_cred); + slapi_ch_free_string(&agmt->repl_bind_method); +} + +static void +ipa_topo_cfg_segment_done(TopoReplicaSegment *tsegm) +{ + if (tsegm == NULL) return; + + slapi_ch_free_string(&tsegm->name); + slapi_ch_free_string(&tsegm->from); + slapi_ch_free_string(&tsegm->to); + ipa_topo_cfg_agmt_done(tsegm->left); + ipa_topo_cfg_agmt_done(tsegm->right); + slapi_ch_free((void **)&tsegm->left); + slapi_ch_free((void **)&tsegm->right); +} + +void +ipa_topo_cfg_segment_free(TopoReplicaSegment *tsegm) +{ + ipa_topo_cfg_segment_done(tsegm); + slapi_ch_free((void **)&tsegm); +} + +TopoReplicaSegment * +ipa_topo_cfg_segment_dup(TopoReplicaSegment *orig) +{ + TopoReplicaSegment *dup = NULL; + + if (orig == NULL) return NULL; + + dup = (TopoReplicaSegment *) slapi_ch_calloc(1,sizeof(TopoReplicaSegment)); + dup->name = slapi_ch_strdup(orig->name); + dup->from = slapi_ch_strdup(orig->from); + dup->to = slapi_ch_strdup(orig->to); + dup->left = ipa_topo_cfg_agmt_dup(orig->left); + dup->left = ipa_topo_cfg_agmt_dup(orig->left); + dup->direct = orig->direct; + dup->state = orig->state; + return dup; +} + +TopoReplicaSegment * +ipa_topo_cfg_segment_find(char *repl_root, char *leftHost, char *rightHost) +{ + TopoReplicaSegment *tsegm = NULL; + TopoReplica *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, 1); + } + slapi_unlock_mutex(topo_shared_conf.conf_lock); + return tsegm; +} + +void +ipa_topo_cfg_segment_set_visited(TopoReplica *replica, TopoReplicaSegment *vsegm) +{ + TopoReplicaSegmentList *segments = NULL; + TopoReplicaSegment *tsegm = NULL; + char *leftHost = vsegm->from; + char *rightHost = vsegm->to; + + slapi_lock_mutex(replica->repl_lock); + segments = replica->repl_segments; + while (segments) { + tsegm = segments->segm; + if ( (!strcasecmp(leftHost,tsegm->from) && !strcasecmp(rightHost,tsegm->to) && + (tsegm->direct == SEGMENT_BIDIRECTIONAL || tsegm->direct == SEGMENT_LEFT_RIGHT)) || + (!strcasecmp(leftHost,tsegm->to) && !strcasecmp(rightHost,tsegm->from) && + (tsegm->direct == SEGMENT_BIDIRECTIONAL || tsegm->direct == SEGMENT_RIGHT_LEFT))) { + segments->visited = 1; + break; + } + tsegm = NULL; + segments = segments->next; + } + slapi_unlock_mutex(replica->repl_lock); + +} + +void +ipa_topo_cfg_segment_add(TopoReplica *replica, TopoReplicaSegment *tsegm) +{ + TopoReplicaSegmentList *seglist = NULL; + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_cfg_segment_add: %s\n", tsegm->name); + slapi_lock_mutex(replica->repl_lock); + if (ipa_topo_cfg_replica_segment_find(replica, + tsegm->from, + tsegm->to, 0)){ + /* already exists: log error */ + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_cfg_segment_add: error: segment exists: %s\n", + tsegm->name); + goto done; + } + seglist = (TopoReplicaSegmentList *) + slapi_ch_calloc(1,sizeof(TopoReplicaSegmentList)); + seglist->visited = 0; + seglist->segm = tsegm; + if (replica->repl_segments == NULL) { + replica->repl_segments = seglist; + } else { + seglist->next = replica->repl_segments; + replica->repl_segments = seglist; + } + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_cfg_segment_added: %s\n", tsegm->name); +done: + slapi_unlock_mutex(replica->repl_lock); +} + +void +ipa_topo_cfg_segment_del(TopoReplica *tconf, TopoReplicaSegment *tsegm) +{ + TopoReplicaSegmentList *segment = NULL; + TopoReplicaSegmentList *prev = NULL; + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_cfg_segment_del: %s\n", tsegm->name); + slapi_lock_mutex(tconf->repl_lock); + segment = tconf->repl_segments; + while (segment) { + if (segment->segm == tsegm) { + if (prev == NULL) { + tconf->repl_segments = segment->next; + } else { + prev->next = segment->next; + } + /* free segment */ + ipa_topo_cfg_segment_free(tsegm); + slapi_ch_free((void **)&segment); + break; + } + prev = segment; + segment = segment->next; + } + slapi_unlock_mutex(tconf->repl_lock); +} + +TopoReplica * +ipa_topo_cfg_replica_new(void) +{ + TopoReplica *topoRepl; + topoRepl = (TopoReplica *)slapi_ch_malloc(sizeof(TopoReplica)); + if (topoRepl) { + topoRepl->next = NULL; + topoRepl->repl_segments = NULL; + topoRepl->repl_root = NULL; + topoRepl->shared_config_base = NULL; + topoRepl->hosts = NULL; + topoRepl->repl_lock = slapi_new_mutex(); + } + return topoRepl; + +} + +int +ipa_topo_cfg_replica_add(TopoReplica *tconf) +{ + int rc = 0; + 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 */ + rc = -1; + } else { + tconf->next = topo_shared_conf.replicas; + topo_shared_conf.replicas = tconf; + } + slapi_unlock_mutex(topo_shared_conf.conf_lock); + + return rc; +} + +void +ipa_topo_cfg_replica_del(TopoReplica *tconf) +{ +/* TBD */ +} + +void +ipa_topo_cfg_replica_free(TopoReplica *tconf) +{ + TopoReplicaSegmentList *seg, *seg_next; + TopoReplicaHost *host, *host_next; + if (tconf) { + slapi_destroy_mutex(tconf->repl_lock); + slapi_ch_free_string(&tconf->shared_config_base); + slapi_ch_free_string(&tconf->repl_root); + slapi_sdn_free(&tconf->shared_config_sdn); + seg = tconf->repl_segments; + while (seg) { + seg_next = seg->next; + ipa_topo_cfg_segment_free(seg->segm); + slapi_ch_free((void **)&seg); + seg = seg_next; + } + host = tconf->hosts; + while (host) { + host_next = host->next; + slapi_ch_free_string(&host->hostname); + host = host_next; + slapi_ch_free((void **)&host); + } + slapi_ch_free((void **)&tconf); + } + +} + +TopoReplica * +ipa_topo_cfg_replica_find(char *repl_root, int lock) +{ + TopoReplica *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 000000000..f45086760 --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology_init.c @@ -0,0 +1,313 @@ + +#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_internal_postop_init(Slapi_PBlock *pb); +static int ipa_topo_apply_shared_replica_config(char *replica_root); +static int ipa_topo_rootdse_init(Slapi_PBlock *pb); +static int ipa_topo_rootdse_search(Slapi_PBlock *pb, Slapi_Entry* e, + Slapi_Entry* entryAfter, int *returncode, + char *returntext, void *arg); +void ipa_topo_be_state_change(void *handle, char *be_name, + int old_be_state, int new_be_state); + +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; + } + } + if (rc == 0) { + char *plugin_type = "internalpostoperation"; + if (slapi_register_plugin(plugin_type, 1, "ipa_topo_internal_init", + ipa_topo_internal_postop_init, + IPA_TOPO_INTERNAL_POSTOP_DESC, + NULL, ipa_topo_get_plugin_id())) { + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_init: failed to register internal 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_internal_postop_init(Slapi_PBlock *pb) +{ + int rc; + rc = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, + (void *)ipa_topo_post_add); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, + (void *)ipa_topo_post_del); + return(rc); +} + +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; +} +void +ipa_topo_queue_apply_shared_config(time_t event_time, void *arg) +{ + ipa_topo_apply_shared_config(); +} +int +ipa_topo_apply_shared_config(void) +{ + int i = 0; + int rc = 0; + char **shared_replica_root = NULL; + + while (0 == ipa_topo_acquire_startup_inprogress()) { + DS_Sleep(1); + } + + shared_replica_root = ipa_topo_get_plugin_replica_root(); + while (rc == 0 && shared_replica_root[i]) { + rc = ipa_topo_apply_shared_replica_config(shared_replica_root[i]); + i++; + } + /* initialize the list of managed servers */ + rc = ipa_topo_setup_managed_servers(); + + ipa_topo_release_startup_inprogress(); + return (rc); +} + +static int +ipa_topo_apply_shared_replica_config(char *replica_root) +{ + TopoReplica *replica_config = NULL; + TopoReplicaSegmentList *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); + /* step 3. get all replication agreements for replica root */ + rc = ipa_topo_util_update_agmt_list(replica_config, replica_segments); + } + return (rc); +} + +static int +ipa_topo_start(Slapi_PBlock * pb) +{ + int rc = 0; + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "--> ipa_topo_start\n"); + + /* expose info about the plugin via rootdse */ + rc = ipa_topo_rootdse_init(pb); + + /* register callback to handle state changes of backends, + * required to check changes in domain level after online initialization + */ + slapi_register_backend_state_change((void *)ipa_topo_be_state_change, + ipa_topo_be_state_change); + + /* 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); + } + + if (0 == ipa_topo_get_plugin_active()) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "plugin not activated, waiting for increase of domain level\n"); + return rc; + } + + rc = ipa_topo_util_start(1); + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_start\n"); + return (rc); +} + +static int +ipa_topo_close(Slapi_PBlock * pb) +{ + + slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, + "", LDAP_SCOPE_BASE, "(objectclass=*)", ipa_topo_rootdse_search); + slapi_unregister_backend_state_change((void *)ipa_topo_be_state_change); + ipa_topo_free_plugin_config(); + return 0; + +} +static int +ipa_topo_rootdse_init(Slapi_PBlock *pb) +{ + int rc = SLAPI_PLUGIN_FAILURE; + + if (slapi_config_register_callback_plugin(SLAPI_OPERATION_SEARCH, + DSE_FLAG_PREOP | DSE_FLAG_PLUGIN, + "", LDAP_SCOPE_BASE, "(objectclass=*)", + ipa_topo_rootdse_search, NULL, pb)) { + rc = SLAPI_PLUGIN_SUCCESS; + } + + return rc; +} + +static int +ipa_topo_rootdse_search(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, + int *returncode, char *returntext, void *arg) +{ + + char *version = slapi_ch_smprintf("%d.%d", ipa_topo_get_plugin_version_major(), + ipa_topo_get_plugin_version_minor()); + slapi_entry_attr_set_charptr(e, "ipaTopologyPluginVersion", version); + if (ipa_topo_get_plugin_active()) { + slapi_entry_attr_set_charptr(e, "ipaTopologyIsManaged", "on"); + } else { + slapi_entry_attr_set_charptr(e, "ipaTopologyIsManaged", "off"); + } + + /* we expose temporarily the domain level in this function, should + * finally be handled in a plugin managing the domain level + */ + char *level = slapi_ch_smprintf("%d", ipa_topo_get_domain_level_major()); + slapi_entry_attr_set_charptr(e, "ipaDomainLevel", level); + slapi_ch_free_string(&version); + slapi_ch_free_string(&level); + return SLAPI_DSE_CALLBACK_OK; +} +void +ipa_topo_be_state_change(void *handle, char *be_name, + int old_be_state, int new_be_state) +{ + Slapi_Backend *be=NULL; + const char *be_suffix; + + /* check if different backends require different actions */ + be = slapi_be_select_by_instance_name(be_name); + be_suffix = slapi_sdn_get_dn(slapi_be_getsuffix(be, 0)); + if (0 == ipa_topo_cfg_plugin_suffix_is_managed(be_suffix)) { + /* nothing to do */ + return; + } + + if (new_be_state == SLAPI_BE_STATE_ON) { + /* backend came back online - check change in domain level */ + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_be_state_change - " + "backend %s is coming online; " + "checking domain level and init shared topology\n", + be_name); + ipa_topo_util_set_domain_level(); + ipa_topo_util_check_plugin_active(); + if (ipa_topo_get_plugin_active()) { + ipa_topo_util_start(1); + } + } else if (new_be_state == SLAPI_BE_STATE_OFFLINE) { + /* backend is about to be taken down - inactivate plugin */ + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_be_state_change" + "backend %s is going offline; inactivate plugin\n", be_name); + } else if (new_be_state == SLAPI_BE_STATE_DELETE) { + /* backend is about to be removed - disable replication */ + if (old_be_state == SLAPI_BE_STATE_ON) { + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_be_state_change" + "backend %s is about to be deleted; inactivate plugin\n", be_name); + } + } +} 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 000000000..33ded2ad3 --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology_post.c @@ -0,0 +1,272 @@ +#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") == 0) { + ret = TOPO_SEGMENT_ENTRY; + break; + } + } + } else if (slapi_sdn_isparent(ipa_topo_get_plugin_shared_hosts_dn(),add_dn)) { + ret = TOPO_HOST_ENTRY; + } else if (slapi_sdn_issuffix(add_dn,ipa_topo_get_domain_level_entry_dn())) { + ret = TOPO_DOMLEVEL_ENTRY; + } + + return ret; +} +int +ipa_topo_post_add(Slapi_PBlock *pb) +{ + int result = SLAPI_PLUGIN_SUCCESS; + int entry_type; + 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\n"); + return 1; + } + /* 2. check if it is in scope and type + * and if plugin is active + */ + entry_type = ipa_topo_check_entry_type(add_entry); + if (0 == ipa_topo_get_plugin_active() && + entry_type != TOPO_DOMLEVEL_ENTRY) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_post_add - plugin not active\n"); + return 0; + } + switch (entry_type) { + case TOPO_CONFIG_ENTRY: + /* initialize the shared topology data for a replica */ + break; + case TOPO_SEGMENT_ENTRY: { + TopoReplicaSegment *tsegm; + TopoReplica *tconf = ipa_topo_util_get_conf_for_segment(add_entry); + char *status; + /* TBD check 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 + */ + tsegm = ipa_topo_util_segment_from_entry(tconf, add_entry); + status = slapi_entry_attr_get_charptr(add_entry, "ipaReplTopoSegmentStatus"); + if (status == NULL || strcasecmp(status,"autogen")) { + 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); + /* TBD: do we know if the replica already has been initialized ? + * should the agreement be enabled ? + * For now assume everything is ok and enable + */ + /* check if it is unidirectional and if other direction exists */ + ipa_topo_util_segment_merge(tconf, tsegm); + slapi_ch_free_string(&status); + 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 + */ + ipa_topo_util_update_segments_for_host(add_entry); + break; + } + case TOPO_DOMLEVEL_ENTRY: { + /* the domain level entry was just added + * check and set the level, if plugin gets activated + * do initialization. + */ + char *domlevel = slapi_entry_attr_get_charptr(add_entry, "ipaDomainLevel"); + ipa_topo_set_domain_level(domlevel); + ipa_topo_util_check_plugin_active(); + if (ipa_topo_get_plugin_active()) { + ipa_topo_util_start(0); + } + slapi_ch_free_string(&domlevel); + 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; + int entry_type; + 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 */ + entry_type = ipa_topo_check_entry_type(mod_entry); + if (0 == ipa_topo_get_plugin_active() && + entry_type != TOPO_DOMLEVEL_ENTRY) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_post_mod - plugin not active\n"); + return 0; + } + + switch (entry_type) { + case TOPO_CONFIG_ENTRY: + break; + case TOPO_SEGMENT_ENTRY: { + LDAPMod **mods; + TopoReplica *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, + "ipa_topo_post_mod - segment to be modified does not exist\n"); + break; + } + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + ipa_topo_util_segment_update(tconf, tsegm, mods,ipa_topo_get_plugin_hostname()); + ipa_topo_util_existing_agmts_update(tconf, tsegm, mods, + ipa_topo_get_plugin_hostname()); + /* also update local segment in tconf */ + break; + } + case TOPO_DOMLEVEL_ENTRY: { + /* the domain level entry was just modified + * check and set the level, if plugin gets activated + * do initialization. + */ + char *domlevel = slapi_entry_attr_get_charptr(mod_entry, "ipaDomainLevel"); + int already_active = ipa_topo_get_plugin_active(); + ipa_topo_set_domain_level(domlevel); + ipa_topo_util_check_plugin_active(); + if (!already_active && ipa_topo_get_plugin_active()) { + ipa_topo_util_start(0); + } + slapi_ch_free_string(&domlevel); + 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; + int entry_type; + 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\n"); + return 1; + } + /* 2. check if it is in scope */ + entry_type = ipa_topo_check_entry_type(del_entry); + if (0 == ipa_topo_get_plugin_active() && + entry_type != TOPO_DOMLEVEL_ENTRY) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_post_del - plugin not active\n"); + return 0; + } + switch (entry_type) { + case TOPO_CONFIG_ENTRY: + break; + case TOPO_SEGMENT_ENTRY: { + /* check if corresponding agreement exists and delete */ + TopoReplica *tconf = ipa_topo_util_get_conf_for_segment(del_entry); + TopoReplicaSegment *tsegm; + char *status; + 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; + } + status = slapi_entry_attr_get_charptr(del_entry, "ipaReplTopoSegmentStatus"); + if (status == NULL || strcasecmp(status, SEGMENT_OBSOLETE_STR)) { + /* obsoleted segments are a result of merge, do not remove repl agmt */ + 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); + slapi_ch_free_string(&status); + break; + } + case TOPO_DOMLEVEL_ENTRY: { + /* the domain level entry was just deleted + * this should not happen, but it is identical + * to setting domlevel to 0 + * log an error and inactivate plugin + */ + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "postop_del: domainlevel entry deleted - " + "plugin will be inactivated \n"); + break; + } + case TOPO_HOST_ENTRY: + /* deleting an host entry means that the host becomes + * unmanaged, probably because a replica is removed. + * remove all marked replication agreements connecting + * this host. + */ + ipa_topo_util_delete_host(del_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 000000000..528f72b69 --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology_pre.c @@ -0,0 +1,458 @@ +#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_is_modattr_restricted(Slapi_PBlock *pb) +{ + LDAPMod **mods; + int i; + int rc = 0; + + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + for (i = 0; (mods != NULL) && (mods[i] != NULL); i++) { + if (ipa_topo_cfg_attr_is_restricted(mods[i]->mod_type)) { + rc = 1; + break; + } + } + return rc; +} + +/* connectivity check for topology + * checks if the nodes of a segment would still be connected after + * removal of the segments. + * For description of the algorithm see design page + */ +struct node_list { + struct node_list *next; + char *node; +}; + +struct node_fanout { + struct node_fanout *next; + char *node; + struct node_list *targets; + int visited; +}; +struct node_list * +node_list_dup (struct node_list *orig) +{ + struct node_list *dup = NULL; + struct node_list *cursor = orig; + struct node_list *start_dup = NULL; + while (cursor) { + if (dup) { + dup->next = (struct node_list *)slapi_ch_malloc(sizeof(struct node_list)); + dup = dup->next; + } else { + dup = (struct node_list *)slapi_ch_malloc(sizeof(struct node_list)); + start_dup = dup; + } + dup->next = NULL; + dup->node = slapi_ch_strdup(cursor->node); + cursor = cursor->next; + } + return start_dup; +} + +void +node_list_free(struct node_list *orig) +{ + struct node_list *cursor = orig; + struct node_list *cur_next = NULL; + while (cursor) { + cur_next = cursor->next; + slapi_ch_free_string(&cursor->node); + slapi_ch_free((void **)&cursor); + cursor = cur_next; + } +} + +struct node_fanout * +ipa_topo_connection_fanout_new (char *from, char *to) +{ + struct node_fanout *new_fanout = (struct node_fanout *) + slapi_ch_malloc(sizeof(struct node_fanout)); + struct node_list *targets = (struct node_list *) + slapi_ch_malloc(sizeof(struct node_list)); + targets->next = NULL; + targets->node = slapi_ch_strdup(to); + new_fanout->next = NULL; + new_fanout->node = slapi_ch_strdup(from); + new_fanout->targets = targets; + new_fanout->visited = 0; + return new_fanout; +} + +void +ipa_topo_connection_fanout_free (struct node_fanout *fanout) +{ + struct node_fanout *cursor = fanout; + struct node_fanout *cur_next = NULL; + while (cursor) { + cur_next = cursor->next; + slapi_ch_free_string(&cursor->node); + node_list_free(cursor->targets); + slapi_ch_free((void **)&cursor); + cursor = cur_next; + } +} + +struct node_fanout * +ipa_topo_connection_fanout_extend (struct node_fanout *fanout_in, char *from, char *to) +{ + struct node_fanout *cursor; + if (fanout_in == NULL) { + /* init fanout */ + return ipa_topo_connection_fanout_new(from,to); + } + /* extend existing fanout struct */ + cursor = fanout_in; + while (cursor) { + if (strcasecmp(cursor->node, from) == 0) break; + cursor = cursor->next; + } + if (cursor) { + struct node_list *target = (struct node_list *) + slapi_ch_malloc(sizeof(struct node_list)); + target->next = cursor->targets; + target->node = slapi_ch_strdup(to); + cursor->targets = target; + return fanout_in; + } else { + cursor = ipa_topo_connection_fanout_new(from,to); + cursor->next = fanout_in; + return cursor; + } +} +struct node_fanout * +ipa_topo_connection_fanout(TopoReplica *tconf, TopoReplicaSegment *tseg) +{ + struct node_fanout *fout = NULL; + TopoReplicaSegment *segm; + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_connection_fanout for segment: %s\n",tseg->name); + /* lock it */ + TopoReplicaSegmentList *seglist = tconf->repl_segments; + while (seglist) { + segm = seglist->segm; + if (strcasecmp(segm->name, tseg->name)) { + if (segm->direct == SEGMENT_LEFT_RIGHT || + segm->direct == SEGMENT_BIDIRECTIONAL ) { + fout = ipa_topo_connection_fanout_extend(fout, segm->from, segm->to); + } + if (segm->direct == SEGMENT_RIGHT_LEFT || + segm->direct == SEGMENT_BIDIRECTIONAL) { + fout = ipa_topo_connection_fanout_extend(fout, segm->to, segm->from); + } + } + seglist = seglist->next; + } + return fout; +} + +void +ipa_topo_connection_append(struct node_fanout *fanout, struct node_list *reachable) +{ + struct node_fanout *cursor = fanout; + + while (cursor) { + if (strcasecmp(reachable->node, cursor->node) == 0 && + cursor->visited == 0) { + struct node_list *tail; + struct node_list *extend; + cursor->visited = 1; + extend = node_list_dup(cursor->targets); + tail = reachable; + while (tail->next) { + tail = tail->next; + } + tail->next = extend; + break; + } + cursor = cursor->next; + } +} + +int +ipa_topo_connection_exists(struct node_fanout *fanout, char* from, char *to) +{ + struct node_list *reachable = NULL; + struct node_fanout *cursor = fanout; + int connected = 0; + /* init reachable nodes */ + while (cursor) { + if (strcasecmp(cursor->node, from) == 0) { + cursor->visited = 1; + reachable = node_list_dup(cursor->targets); + } else { + cursor->visited = 0; + } + cursor = cursor->next; + } + /* check if target is in reachable nodes, if + * not, expand reachables + */ + if (reachable == NULL) return 0; + while (reachable) { + if (strcasecmp(reachable->node, to) == 0) { + connected = 1; + break; + } + ipa_topo_connection_append(fanout, reachable); + reachable = reachable->next; + } + node_list_free(reachable); + return connected; +} + +int +ipa_topo_check_connect_reject(Slapi_PBlock *pb) +{ + int rc = 0; + Slapi_Entry *add_entry; + char *pi; + + /* 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; + } + slapi_pblock_get(pb,SLAPI_DELETE_EXISTING_ENTRY,&add_entry); + if (TOPO_SEGMENT_ENTRY != ipa_topo_check_entry_type(add_entry)) { + return 0; + } else { + /* a new segment is added + * verify that the segment does not yet exist + */ + TopoReplicaSegment *tsegm; + TopoReplica *tconf = ipa_topo_util_get_conf_for_segment(add_entry); + tsegm = ipa_topo_util_find_segment(tconf, add_entry); + if (tsegm) { + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "segment to be added does already exist\n"); + rc = 1; + } + } + return rc; +} + +int +ipa_topo_check_disconnect_reject(Slapi_PBlock *pb) +{ + int rc = 1; + Slapi_Entry *del_entry; + struct node_fanout *fanout = NULL; + char *pi; + + /* 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; + } + slapi_pblock_get(pb,SLAPI_DELETE_EXISTING_ENTRY,&del_entry); + if (TOPO_SEGMENT_ENTRY != ipa_topo_check_entry_type(del_entry)) { + return 0; + } else { + TopoReplica *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"); + goto done; + } + /* check if removal of segment would break connectivity */ + fanout = ipa_topo_connection_fanout(tconf, tsegm); + if (fanout == NULL) goto done; + + if (ipa_topo_connection_exists(fanout, tsegm->from, tsegm->to) && + ipa_topo_connection_exists(fanout, tsegm->to, tsegm->from)) { + rc = 0; + } + ipa_topo_connection_fanout_free(fanout); + } + +done: + return rc; +} + +static int +ipa_topo_pre_ignore_op(Slapi_PBlock *pb) +{ + int repl_op = 0; + /* changes to cn=config aren't replicated, for changes to + * shared topology area checks have been done on master + * accepting the operation + */ + slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); + return repl_op; +} + +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 (0 == ipa_topo_get_plugin_active()) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_pre_add - plugin not active\n"); + return 0; + } + + if (ipa_topo_pre_ignore_op(pb)) return result; + + 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; + } else if (ipa_topo_check_connect_reject(pb)) { + int rc = LDAP_UNWILLING_TO_PERFORM; + char *errtxt; + errtxt = slapi_ch_smprintf("Segment already exists in topology." + "Add rejected.\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 (0 == ipa_topo_get_plugin_active()) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_pre_mod - plugin not active\n"); + return 0; + } + + if (ipa_topo_pre_ignore_op(pb)) return result; + + if (ipa_topo_is_entry_managed(pb) && ipa_topo_is_modattr_restricted(pb)) { + int rc = LDAP_UNWILLING_TO_PERFORM; + char *errtxt; + errtxt = slapi_ch_smprintf("Entry and attributes are 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 (0 == ipa_topo_get_plugin_active()) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_pre_del - plugin not active\n"); + return 0; + } + + if (ipa_topo_pre_ignore_op(pb)) return result; + + 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; + } else if (ipa_topo_check_disconnect_reject(pb)) { + int rc = LDAP_UNWILLING_TO_PERFORM; + char *errtxt; + errtxt = slapi_ch_smprintf("Removal of Segment disconnects topology." + "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 000000000..94d8b33fd --- /dev/null +++ b/daemons/ipa-slapi-plugins/topology/topology_util.c @@ -0,0 +1,1441 @@ +#include "topology.h" + + +int +ipa_topo_util_modify(Slapi_DN *entrySDN, Slapi_Mods *smods) +{ + int rc = 0; + Slapi_PBlock *mod_pb; + LDAPMod **mods; + + mod_pb = slapi_pblock_new(); + slapi_pblock_init(mod_pb); + + mods = (slapi_mods_get_ldapmods_passout(smods)); + 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); + if (rc != 0) + { + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_modify: " + "failed to modify entry (%s): error %d\n", slapi_sdn_get_dn(entrySDN), rc); + } + 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 = slapi_entry_dup(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; +} + +void +ipa_topo_util_check_plugin_active(void) +{ + if (ipa_topo_get_plugin_version_major() < ipa_topo_get_domain_level_major() || + (ipa_topo_get_plugin_version_major() == ipa_topo_get_domain_level_major() && + ipa_topo_get_plugin_version_minor() <= ipa_topo_get_domain_level_minor())) { + ipa_topo_set_plugin_active(1); + } else { + ipa_topo_set_plugin_active(0); + } +} + +void +ipa_topo_util_set_domain_level(void) +{ + int rc = 0; + Slapi_Entry **entries; + Slapi_PBlock *pb = NULL; + + pb = slapi_pblock_new(); + slapi_search_internal_set_pb(pb, ipa_topo_get_domain_level_entry(), + 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_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_set_domain_level: " + "failed to lookup domain level entry: 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_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_set_domain_level: domain level" + " entry does not exist, use default domain level of 0\n"); + ipa_topo_set_domain_level(NULL); + } else { + char *domlevel = slapi_entry_attr_get_charptr(entries[0], "ipaDomainLevel"); + ipa_topo_set_domain_level(domlevel); + slapi_ch_free_string(&domlevel); + } + } + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); +} + +TopoReplica * +ipa_topo_util_get_replica_conf(char *repl_root) +{ + int rc = 0; + Slapi_Entry **entries; + Slapi_PBlock *pb = NULL; + char *filter; + TopoReplica *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); + } + } + slapi_ch_free_string(&filter); + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + + if (0 != ipa_topo_cfg_replica_add(topoRepl)) { + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_get_replica_conf: " + "replica already exists\n"); + ipa_topo_cfg_replica_free(topoRepl); + topoRepl = NULL; + } + + return topoRepl; +} + +TopoReplicaSegmentList * +ipa_topo_util_get_replica_segments(TopoReplica *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_PLUGIN, 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_PLUGIN, 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++) { + 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 == LDAP_NO_SUCH_OBJECT) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_setup_servers: " + "search for servers failed (continuing): error %d\n", rc); + /* masters not yet configured, continue plugin startup */ + rc = 0; + } else if (rc != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, 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_PLUGIN, 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); + + mattr = slapi_ch_smprintf("ipaReplTopoSegmentGenerated;%s",direction); + mval = slapi_entry_attr_get_charptr(entry,mattr); + if (mval == 0) { + mval = slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentGenerated"); + } + if (mval) { + agmt->rdn = ipa_topo_agmt_gen_rdn(fromHost,toHost); + } else { + agmt->rdn = ipa_topo_agmt_std_rdn(toHost); + } + slapi_ch_free_string(&mattr); + slapi_ch_free_string(&mval); + + 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); + slapi_ch_free_string(&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(TopoReplica *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_segment_find(conf->repl_root, leftHost, rightHost); + + slapi_ch_free((void **)&leftHost); + slapi_ch_free((void **)&rightHost); + return segment; +} + +TopoReplicaSegment * +ipa_topo_util_segment_from_entry(TopoReplica *conf, Slapi_Entry *entry) +{ + char *leftHost; + char *rightHost; + char *direction; + char *name; + char *state; + + 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"); + name = slapi_entry_attr_get_charptr(entry,"cn"); + if (strcasecmp(direction,SEGMENT_DIR_BOTH) == 0){ + segment->direct = SEGMENT_BIDIRECTIONAL; + 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->direct = SEGMENT_LEFT_RIGHT; + 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->direct = SEGMENT_RIGHT_LEFT; + segment->right = ipa_topo_util_agmt_from_entry(entry,conf->repl_root, + rightHost,leftHost, "right"); + } + state = slapi_entry_attr_get_charptr(entry,"ipaReplTopoSegmentStatus"); + if (state && 0 == strcasecmp(state, SEGMENT_OBSOLETE_STR)) { + /* state obsolete was set during merge */ + segment->state = SEGMENT_OBSOLETE; + } else if (state && 0 == strcasecmp(state, SEGMENT_REMOVED_STR)) { + /* state removed was set during host delete */ + segment->state = SEGMENT_REMOVED; + } else { + segment->state = 0; + } + segment->from = leftHost; + segment->to = rightHost; + segment->name = name; + slapi_ch_free((void **)&direction); + slapi_ch_free((void **)&state); + + return segment; +} + +TopoReplicaSegment * +ipa_topo_util_segm_from_agmt(Slapi_Entry *repl_agmt) +{ + TopoReplicaSegment *segment = NULL; + TopoReplicaAgmt *agmt = NULL; + segment = (TopoReplicaSegment *) slapi_ch_calloc(1,sizeof(TopoReplicaSegment)); + agmt = (TopoReplicaAgmt *) slapi_ch_calloc(1,sizeof(TopoReplicaAgmt)); + segment->from = slapi_ch_strdup(ipa_topo_get_plugin_hostname()); + segment->to = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicahost"); + segment->direct = SEGMENT_LEFT_RIGHT; + segment->state = SEGMENT_AUTOGEN; + segment->name = slapi_ch_smprintf("%s-to-%s", segment->from, segment->to); + segment->left = agmt; + segment->right = NULL; + + agmt->origin = slapi_ch_strdup(segment->from); + agmt->target = slapi_ch_strdup(segment->to); + agmt->repl_timeout = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicatimeout"); + agmt->repl_root = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicaroot"); + + agmt->repl_attrs = slapi_entry_attr_get_charptr(repl_agmt, "nsDS5ReplicatedAttributeList"); + agmt->strip_attrs = slapi_entry_attr_get_charptr(repl_agmt, "nsds5ReplicaStripAttrs"); + agmt->total_attrs = slapi_entry_attr_get_charptr(repl_agmt, "nsDS5ReplicatedAttributeListTotal"); + agmt->repl_bind_dn = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicabinddn"); + agmt->repl_bind_cred = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicacredentials"); + agmt->repl_transport = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicatransportinfo"); + agmt->repl_bind_method = slapi_entry_attr_get_charptr(repl_agmt, "nsds5replicabindmethod"); + + return segment; + +} + +TopoReplica * +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 */ + TopoReplica *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); + slapi_entry_free(conf); + + return tconf; +} + +TopoReplica * +ipa_topo_util_conf_from_entry(Slapi_Entry *entry) +{ + TopoReplica *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 = (TopoReplica *) slapi_ch_calloc(1,sizeof(TopoReplica)); + conf->repl_root = repl_root; + /* TBD read defined managed attrs as defaults */ + return conf; + } +} + +int +ipa_topo_util_update_agmt_list(TopoReplica *conf, TopoReplicaSegmentList *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_list: " + "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_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_agmts_list: " + "no agrements found\n"); + goto update_only; + } + } + + /* for each agreement find segment */ + nentries = 0; + repl_agmt = entries[0]; + while (repl_agmt) { + char *targetHost; + TopoReplicaAgmt *topo_agmt; + TopoReplicaSegment *topo_segm; + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_agmts_list: 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_PLUGIN, 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(conf->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) { + ipa_topo_util_modify((Slapi_DN *)slapi_entry_get_sdn_const(repl_agmt), + smods); + } + slapi_mods_free(&smods); + } else { + if (ipa_topo_util_agmt_is_marked(repl_agmt)) { + /* agreement is marked and no segment exists, delete agreement */ + ipa_topo_agmt_del_dn((char *)slapi_sdn_get_dn(slapi_entry_get_sdn_const(repl_agmt))); + } else { + /* generate segment from agreement */ + topo_segm = ipa_topo_util_segm_from_agmt(repl_agmt); + rc = ipa_topo_util_segment_write(conf, topo_segm); + if ( rc != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_agmts_list: " + "failed to write segment: error %d\n", rc); + } + rc = ipa_topo_util_agmt_mark(conf, repl_agmt, topo_segm); + if (rc != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_agmt_list: " + "failed to mark agreement for host %s: error %d\n", targetHost, rc); + } + /* segment has been added in postop of segment write, + * prevent adding an agreement again + */ + ipa_topo_cfg_segment_set_visited(conf, topo_segm); + } + } + + repl_agmt = entries[++nentries]; + } + slapi_free_search_results_internal(pb); + +update_only: + /* check if segments not covered by agreement exist + * add agreeement + */ + ipa_topo_util_missing_agmts_add_list(conf, conf->repl_segments, + ipa_topo_get_plugin_hostname()); + +error_return: + slapi_ch_free_string(&filter); + slapi_pblock_destroy(pb); + return rc; +} + +TopoReplicaAgmt * +ipa_topo_util_find_segment_agmt(TopoReplicaSegmentList *repl_segments, + char *fromHost, char *toHost) +{ + TopoReplicaAgmt *agmt = NULL; + TopoReplicaAgmt *agmtfound = NULL; + TopoReplicaSegmentList *segment = repl_segments; + + while (segment) { + if (segment->visited) { + segment = segment->next; + continue; + } + agmt = segment->segm->left; + if (agmt && (0 == strcasecmp(agmt->origin, fromHost)) && + (0 == strcasecmp(agmt->target, toHost))) { + agmtfound = agmt; + break; + } + agmt = segment->segm->right; + if (agmt && (0 == strcasecmp(agmt->origin, fromHost)) && + (0 == strcasecmp(agmt->target, toHost))) { + agmtfound = agmt; + break; + } + segment = segment->next; + } + if (segment) { + segment->visited = 1; + } + return agmtfound; +} + +void +ipa_topo_util_missing_agmts_add_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *repl_segments, + char *fromHost) +{ + TopoReplicaSegmentList *segment = repl_segments; + + while (segment) { + if (segment->visited) { + segment->visited = 0; + segment = segment->next; + continue; + } + ipa_topo_util_missing_agmts_add(repl_conf, segment->segm, fromHost); + segment = segment->next; + } +} + +void +ipa_topo_util_missing_agmts_add(TopoReplica *repl_conf, + TopoReplicaSegment *segment, + char *fromHost) +{ + if (0 == strcasecmp(segment->from, fromHost)) { + if (segment->left) { + ipa_topo_agmt_new(segment->to,repl_conf, segment->left); + } + } else if (0 == strcasecmp(segment->to, fromHost)) { + if (segment->right) { + ipa_topo_agmt_new(segment->from,repl_conf, segment->right); + } + } +} + +void +ipa_topo_util_existing_agmts_del_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *repl_segments, + char *fromHost) +{ + TopoReplicaSegmentList *segment = repl_segments; + + while (segment) { + if (segment->visited) { + segment->visited = 0; + segment = segment->next; + continue; + } + ipa_topo_util_existing_agmts_del(repl_conf, segment->segm,fromHost); + segment = segment->next; + } +} + +void +ipa_topo_util_existing_agmts_del(TopoReplica *repl_conf, + TopoReplicaSegment *segment, + char *fromHost) +{ + if (0 == strcasecmp(segment->from, fromHost)) { + if (segment->left) { + ipa_topo_agmt_del(segment->to,repl_conf, segment->left); + } + } else if (0 == strcasecmp(segment->to, fromHost)) { + if (segment->right) { + ipa_topo_agmt_del(segment->from,repl_conf, segment->right); + } + } +} + +void +ipa_topo_util_existing_agmts_update_list(TopoReplica *repl_conf, + TopoReplicaSegmentList *repl_segments, + LDAPMod **mods ,char *fromHost) +{ + TopoReplicaSegmentList *segment = repl_segments; + + while (segment) { + if (segment->visited) { + segment->visited = 0; + segment = segment->next; + continue; + } + ipa_topo_util_existing_agmts_update(repl_conf, segment->segm, mods, fromHost); + segment = segment->next; + } +} + +void +ipa_topo_util_existing_agmts_update(TopoReplica *repl_conf, + TopoReplicaSegment *segment, + LDAPMod **mods ,char *fromHost) +{ + TopoReplicaAgmt *l_agmt = NULL; + TopoReplicaAgmt *r_agmt = NULL; + l_agmt = segment->left; + r_agmt = segment->right; + if (l_agmt && r_agmt) { + if (0 == strcasecmp(l_agmt->origin, fromHost)) { + ipa_topo_agmt_mod(repl_conf, l_agmt, mods, "left"); + } else if (0 == strcasecmp(r_agmt->origin, fromHost)) { + ipa_topo_agmt_mod(repl_conf, r_agmt, mods, "right"); + } + } +} + +void +ipa_topo_util_segment_update(TopoReplica *repl_conf, + TopoReplicaSegment *segment, + LDAPMod **mods ,char *fromHost) +{ + int i; + for (i = 0; (mods != NULL) && (mods[i] != NULL); i++) { + switch (mods[i]->mod_op & ~LDAP_MOD_BVALUES) { + case LDAP_MOD_DELETE: + break; + case LDAP_MOD_ADD: + case LDAP_MOD_REPLACE: + if (0 == strcasecmp(mods[i]->mod_type,"ipaReplTopoSegmentDirection")) { + if (0 == strcasecmp(mods[i]->mod_bvalues[0]->bv_val,"both")) { + if (segment->direct == SEGMENT_LEFT_RIGHT) { + segment->right = ipa_topo_cfg_agmt_dup_reverse(segment->left); + } else if (segment->direct == SEGMENT_RIGHT_LEFT) { + segment->left = ipa_topo_cfg_agmt_dup_reverse(segment->right); + } + segment->direct = SEGMENT_BIDIRECTIONAL; + } else { + /* only onedirectionl --> bidirectional handled so far */ + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_segment_update: no downgrade of direction\n"); + } + } else if (0 == strcasecmp(mods[i]->mod_type,"ipaReplTopoSegmentStatus")) { + if (0 == strcasecmp(mods[i]->mod_bvalues[0]->bv_val,SEGMENT_OBSOLETE_STR)) { + segment->state = SEGMENT_OBSOLETE; + } else if (0 == strcasecmp(mods[i]->mod_bvalues[0]->bv_val,SEGMENT_AUTOGEN_STR)) { + segment->state = SEGMENT_AUTOGEN; + } + } + break; + } + } +} + +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; + char *repl_root; + TopoReplica *replica; + int ret = 0; + + /* first check: is the entry managed alreday by the topology plugin */ + /* at the moment only replication agreements are managed */ + if (ipa_topo_util_agmt_is_marked(e)) return 1; + + /* next check: is the replcation agreement targeting a meanged server + * deny agreements to an managed server + * allow other agreements + */ + targethost = slapi_entry_attr_get_charptr(e,"nsDS5ReplicaHost"); + repl_root = slapi_entry_attr_get_charptr(e,"nsDS5ReplicaRoot"); + replica = ipa_topo_cfg_replica_find(repl_root,1); + if (targethost && replica && + ipa_topo_cfg_host_find(replica, targethost, 1)) { + ret = 1; + } + slapi_ch_free_string(&targethost); + slapi_ch_free_string(&repl_root); + + return ret; + +} + +void +ipa_topo_util_segm_update (TopoReplica *tconf, + TopoReplicaSegment *tsegm, + int property) +{ + char *dn = NULL; + Slapi_Mods *smods; + + dn = ipa_topo_segment_dn(tconf, tsegm->name); + + if (dn == NULL) return; + + /* apply mods to entry */ + smods = slapi_mods_new(); + switch (property) { + case SEGMENT_BIDIRECTIONAL: + tsegm->direct = SEGMENT_BIDIRECTIONAL; + slapi_mods_add_string(smods, LDAP_MOD_REPLACE, + "ipaReplTopoSegmentDirection", "both"); + break; + case SEGMENT_LEFT_RIGHT: + tsegm->direct = SEGMENT_LEFT_RIGHT; + slapi_mods_add_string(smods, LDAP_MOD_REPLACE, + "ipaReplTopoSegmentDirection", "left-right"); + break; + case SEGMENT_RIGHT_LEFT: + tsegm->direct = SEGMENT_RIGHT_LEFT; + slapi_mods_add_string(smods, LDAP_MOD_REPLACE, + "ipaReplTopoSegmentDirection", "right-left"); + break; + case SEGMENT_OBSOLETE: + tsegm->state = SEGMENT_OBSOLETE; + slapi_mods_add_string(smods, LDAP_MOD_REPLACE, + "ipaReplTopoSegmentStatus", SEGMENT_OBSOLETE_STR); + break; + case SEGMENT_REMOVED: + tsegm->state = SEGMENT_OBSOLETE; + slapi_mods_add_string(smods, LDAP_MOD_REPLACE, + "ipaReplTopoSegmentStatus", SEGMENT_REMOVED_STR); + break; + } + if (slapi_mods_get_num_mods(smods) > 0) { + Slapi_DN *sdn = slapi_sdn_new_normdn_byref(dn); + ipa_topo_util_modify(sdn, smods); + slapi_sdn_free(&sdn); + } + + slapi_mods_free(&smods); + slapi_ch_free_string(&dn); + +} + +void +ipa_topo_util_segm_remove(TopoReplica *tconf, + TopoReplicaSegment *tsegm) +{ + Slapi_PBlock *pb; + char *dn = NULL; + int ret; + + /* remove it from the database */ + dn = ipa_topo_segment_dn(tconf, tsegm->name); + if (dn == NULL) return; + + 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); + + /* removed from the internal data struct in the delete postop */ + /* ipa_topo_cfg_segment_del(tconf, tsegm); */ +} + +/* for merging segments the result should be the same on any server + * an deliberate decision is to compare hostnames to get a preference + */ +static int +ipa_topo_util_segm_order(TopoReplicaSegment *l, TopoReplicaSegment *r) +{ + return strcasecmp(l->from, r->from); +} + +void +ipa_topo_util_segment_merge(TopoReplica *tconf, + TopoReplicaSegment *tsegm) +{ + TopoReplicaSegment *ex_segm; + + if (tsegm->direct == SEGMENT_BIDIRECTIONAL) return; + + if (strcasecmp(tsegm->from,ipa_topo_get_plugin_hostname()) && + strcasecmp(tsegm->to,ipa_topo_get_plugin_hostname())) { + /* merging is only done on one of the endpoints of the segm */ + return; + } + + ex_segm = ipa_topo_cfg_replica_segment_find(tconf, tsegm->to, tsegm->from, 1 /*lock*/); + if (ex_segm == NULL) return; + + /* to avoid conflicts merging has to be done only once and + * so there is a preference which segment survives and on + * which server it will be done + */ + if (ipa_topo_util_segm_order(ex_segm, tsegm) > 0) { + if (0 == strcasecmp(tsegm->from,ipa_topo_get_plugin_hostname())) { + tsegm->right = ipa_topo_cfg_agmt_dup(ex_segm->left); + ipa_topo_util_segm_update(tconf,ex_segm, SEGMENT_OBSOLETE); + ipa_topo_util_segm_remove(tconf, ex_segm); + ipa_topo_util_segm_update(tconf,tsegm, SEGMENT_BIDIRECTIONAL); + } + } else { + if (0 == strcasecmp(ex_segm->from,ipa_topo_get_plugin_hostname())) { + ex_segm->right = ipa_topo_cfg_agmt_dup(tsegm->left); + ipa_topo_util_segm_update(tconf,tsegm, SEGMENT_OBSOLETE); + ipa_topo_util_segm_remove(tconf, tsegm); + ipa_topo_util_segm_update(tconf,ex_segm, SEGMENT_BIDIRECTIONAL); + } + } + +} +int +ipa_topo_util_segment_write(TopoReplica *tconf, TopoReplicaSegment *tsegm) +{ + Slapi_Entry *e = NULL; + Slapi_PBlock *pb; + char *dn = NULL; + Slapi_DN *sdn = NULL; + int ret = 0; + /* Set up the new segment entry */ + dn = ipa_topo_segment_dn(tconf, tsegm->name); + 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, "iparepltoposegment"); + slapi_entry_add_string(e, "cn",tsegm->name); + slapi_entry_add_string(e, "iparepltoposegmentleftnode",tsegm->from); + slapi_entry_add_string(e, "iparepltoposegmentrightnode",tsegm->to); + slapi_entry_add_string(e, "iparepltoposegmentdirection", "left-right"); + /* write other attributes of the segment if present */ + if (tsegm->state == SEGMENT_AUTOGEN) { + slapi_entry_add_string(e, "iparepltoposegmentstatus", "autogen"); + } + + 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; + +} +/* + * to distinguish replication agreements created by the topology plugin + * and agreemens created by admins or legacy tools a "marker" objectclass is used. + * Witheout extending the schema the description attribute is used and a + * specific value is added: + * - when a segment is created from an agreement an so put under control of + * the plugin: + * "ipaReplTopoManagedAgreementState: managed agreement - controlled by topology plugin" + * - when an agreement is created from a segment: + * "ipaReplTopoManagedAgreementState: managed agreement - generated by topology plugin" + */ +int +ipa_topo_util_agmt_mark(TopoReplica *tconf, Slapi_Entry *repl_agmt, + TopoReplicaSegment *tsegm) +{ + int ret = 0; + + Slapi_Mods *smods = slapi_mods_new(); + slapi_mods_add_string(smods, LDAP_MOD_ADD, "objectclass", + "ipaReplTopoManagedAgreement"); + slapi_mods_add_string(smods, LDAP_MOD_ADD, "ipaReplTopoManagedAgreementState", + "managed agreement - controlled by topology plugin"); + if (slapi_mods_get_num_mods(smods) > 0) { + ret = ipa_topo_util_modify( + (Slapi_DN *)slapi_entry_get_sdn_const(repl_agmt), smods); + } + slapi_mods_free(&smods); + + return ret; +} + +int +ipa_topo_util_agmt_is_marked(Slapi_Entry *repl_agmt) +{ + int ret = 0; + int i; + char **descs; + + descs = slapi_entry_attr_get_charray(repl_agmt, "objectclass"); + for (i=0; descs &&descs[i]; i++) { + if (strcasecmp(descs[i],"ipaReplTopoManagedAgreement") == 0) { + ret = 1; + break; + } + } + slapi_ch_array_free(descs); + return ret; +} + +void +ipa_topo_util_update_segments_for_host(Slapi_Entry *hostentry) +{ + int rc = 0; + int nentries; + char* newhost = NULL; + Slapi_Entry **entries; + Slapi_Entry *repl_agmt; + Slapi_PBlock *pb = NULL; + char *filter; + + /* find all replication agreements to the new host entry + * Since the host was not yet managed new segments ghave to be + * created + */ + newhost = slapi_entry_attr_get_charptr(hostentry,"cn"); + + pb = slapi_pblock_new(); + filter = slapi_ch_smprintf("(&(objectclass=nsds5replicationagreement)(nsds5replicahost=%s))", + newhost); + 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_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_segments_for_host: " + "no replication agreeements for host %s: error %d\n", + newhost, 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_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_segments_for_host: " + "no agrements found\n"); + goto error_return; + } + } + + /* for each agreement find segment */ + nentries = 0; + repl_agmt = entries[0]; + while (repl_agmt) { + TopoReplica *conf = NULL; + TopoReplicaSegment *topo_segm; + char *repl_root = NULL; + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_segments_for_host: " + "processing agreement: %s\n", + slapi_entry_get_dn_const(repl_agmt)); + + /* generate segment from agreement */ + repl_root = slapi_entry_attr_get_charptr(repl_agmt,"nsds5replicaroot"); + conf = ipa_topo_cfg_replica_find(repl_root,1); + if (conf == NULL) goto next_agmt; + topo_segm = ipa_topo_util_segm_from_agmt(repl_agmt); + rc = ipa_topo_util_segment_write(conf, topo_segm); + if (rc != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_segments_for_host: " + "failed to write segment for host %s: error %d\n", + newhost, rc); + } + rc = ipa_topo_util_agmt_mark(conf, repl_agmt, topo_segm); + if (rc != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_update_segments_for_host: " + "failed to mark agreement for host %s: error %d\n", + newhost, rc); + } + ipa_topo_cfg_segment_add(conf, topo_segm); +next_agmt: + slapi_ch_free_string(&repl_root); + repl_agmt = entries[++nentries]; + + } + + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + +error_return: + slapi_ch_free_string(&newhost); + +} + +void +ipa_topo_util_disable_repl_from_host(char *repl_root, char *delhost) +{ + char *principal = ipa_topo_util_get_ldap_principal(repl_root, delhost); + ipa_topo_util_disable_repl_for_principal(repl_root, principal); +} + +void +ipa_topo_util_delete_segments_for_host(char *repl_root, char *delhost) +{ + TopoReplicaSegment *segm = NULL; + TopoReplica *tconf = ipa_topo_cfg_replica_find(repl_root, 1); + int check_reverse = 1; + + /* first check if a segment originating at localhost exists */ + segm = ipa_topo_cfg_segment_find(repl_root, + ipa_topo_get_plugin_hostname(), delhost); + if (segm) { + /* mark segment as removable, bypass connectivity check when replicated */ + if (segm->direct == SEGMENT_BIDIRECTIONAL) check_reverse = 0; + ipa_topo_util_segm_update(tconf,segm, SEGMENT_REMOVED); + /* delete segment */ + /* the replication agreement will be deleted in the postop_del*/ + ipa_topo_util_segm_remove(tconf, segm); + } + /* check if one directional segment in reverse direction exists */ + if (check_reverse) { + segm = ipa_topo_cfg_segment_find(repl_root, + delhost, ipa_topo_get_plugin_hostname()); + if (segm) { + ipa_topo_util_segm_update(tconf,segm, SEGMENT_REMOVED); + /* mark and delete, no repl agmt on this server */ + ipa_topo_util_segm_remove(tconf, segm); + } + } + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_delete_segments_for_host <-- done\n"); +} + +void +ipa_topo_util_delete_host(Slapi_Entry *hostentry) +{ + char* delhost = NULL; + + delhost = slapi_entry_attr_get_charptr(hostentry,"cn"); + /* if the deleted host is the current host, do not + * delete the segments, deleting segments will trigger + * removal of replication agreements and it cannot be + * ensured that the deletion of the host will reach + * other servers in the replica before. + * So wait until the delete is received on the other + * servers and the deletion of segments is received. + */ + if (0 == strcasecmp(delhost,ipa_topo_get_plugin_hostname())) { + return; + } else { + /* find all segments connecting the local master to the + * deleted master. + * - mark the segments as no longer managed + * - delete the segments + * - if the segment originates at the local host + * remove the corresponding replication agreement + */ + int i = 0; + char **shared_root = ipa_topo_get_plugin_replica_root(); + + while (shared_root[i]) { + ipa_topo_util_disable_repl_from_host(shared_root[i], delhost); + ipa_topo_util_delete_segments_for_host(shared_root[i], delhost); + i++; + } + } + +} + +int +ipa_topo_util_start(int delay) +{ + /* main routine to synchronize data in the shared tree and + * config data. It will be called: + * - at startup of the server + * - if the domain level increases above the plugin version + * and the plugin becomes active + * - after a backup state change, eg after online initialization + * when there is no guarantee that data in the shared tree + * and plugin data still match. + * + * the parameter delay controls if the operation is performed + * immediately or after some delay. This delay is necessary + * during startup because the plugins only become active after + * all plugins have been started and modifications would not be + * logged in the changelog and replicated + */ + + time_t now; + int rc = 0; + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "--> ipa_topo_util_start - deleay: %d\n",delay); + + ipa_topo_init_shared_config(); + + /* initialize the config data from the shared tree and apply to + * the managed data under cn=config + */ + if (delay) { + time(&now); + if (!slapi_eq_once(ipa_topo_queue_apply_shared_config,NULL,now + + ipa_topo_get_plugin_startup_delay())) { + slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM, + "unable to queue configuration update\n"); + return -1; + } + } else { + rc = ipa_topo_apply_shared_config(); + } + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_util_start\n"); + return rc; +} + +char * +ipa_topo_util_get_ldap_principal(char *repl_root, char *hostname) +{ + int rc = 0; + Slapi_Entry **entries; + Slapi_PBlock *pb = NULL; + char *filter; + char *dn; + + filter = slapi_ch_smprintf("krbprincipalname=ldap/%s*",hostname); + pb = slapi_pblock_new(); + + slapi_search_internal_set_pb(pb, repl_root, LDAP_SCOPE_SUBTREE, + 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_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_get_ldap_principal: " + "unable to search for entry (%s): error %d\n", filter, rc); + } else { + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (NULL == entries || NULL == entries[0]) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_util_get_ldap_principal: entry not found: (%s)\n", filter); + } else { + dn = slapi_ch_strdup(slapi_entry_get_dn(entries[0])); + } + } + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + slapi_ch_free_string(&filter); + return dn; +} + +void +ipa_topo_util_disable_repl_for_principal(char *repl_root, char *principal) +{ + Slapi_DN *sdn; + char *filter; + Slapi_PBlock *pb; + Slapi_Entry **entries; + Slapi_Mods *smods; + int ret; + + /* to disable replication for a user/principal it ahs to be removed from the + * allowed bind dns in the replica object and from the bind dn group + */ + + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "--> ipa_topo_util_disable_repl_for_principal\n"); + /* find replica object */ + pb = slapi_pblock_new(); + filter = slapi_ch_smprintf("(&(objectclass=nsds5replica)(nsds5replicaroot=%s))", 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) { + sdn = NULL; + } else { + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (NULL == entries || NULL == entries[0]) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_agreement_dn: no replica found\n"); + sdn = NULL; + } else { + sdn = slapi_sdn_dup(slapi_entry_get_sdn(entries[0])); + } + } + slapi_free_search_results_internal(pb); + + /* remove principal from binddns */ + smods = slapi_mods_new(); + slapi_mods_add_string(smods, LDAP_MOD_DELETE, + "nsds5replicabinddn", principal); + ret = ipa_topo_util_modify(sdn, smods); + slapi_sdn_free(&sdn); + + /* find binddn group */ + slapi_pblock_init(pb); + + slapi_search_internal_set_pb(pb, ipa_topo_get_plugin_shared_bindgroup(), LDAP_SCOPE_BASE, + "(objectclass=groupofnames)", + 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) { + sdn = NULL; + } else { + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (NULL == entries || NULL == entries[0]) { + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "ipa_topo_agreement_dn: no replica found\n"); + sdn = NULL; + } else { + sdn = slapi_sdn_dup(slapi_entry_get_sdn(entries[0])); + } + } + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + + /* delete principal as binddn group member */ + smods = slapi_mods_new(); + slapi_mods_add_string(smods, LDAP_MOD_DELETE, + "member", principal); + ret = ipa_topo_util_modify(sdn, smods); + slapi_mods_free(&smods); + slapi_sdn_free(&sdn); + slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM, + "<-- ipa_topo_util_disable_repl_for_principal\n"); + +} diff --git a/freeipa.spec.in b/freeipa.spec.in index b14acee63..7dc576256 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -405,6 +405,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_extdom_extop.la rm %{buildroot}/%{plugin_dir}/libipa_range_check.la rm %{buildroot}/%{plugin_dir}/libipa_otp_counter.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 @@ -798,6 +799,7 @@ fi %attr(755,root,root) %{plugin_dir}/libipa_range_check.so %attr(755,root,root) %{plugin_dir}/libipa_otp_counter.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/70topology.ldif b/install/share/70topology.ldif new file mode 100644 index 000000000..5a13255ac --- /dev/null +++ b/install/share/70topology.ldif @@ -0,0 +1,15 @@ +# IPA Topology Plugin schema +# BaseOID: 2.16.840.1.113730.3.8.20 +dn: cn=schema +attributetypes: ( 2.16.840.1.113730.3.8.20.2.1 NAME 'ipaReplTopoConfRoot' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.2 NAME 'ipaReplTopoSegmentDirection' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.3 NAME 'ipaReplTopoSegmentLeftNode' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.4 NAME 'ipaReplTopoSegmentRightNode' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.5 NAME 'ipaReplTopoSegmentStatus' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.6 NAME 'ipaReplTopoSegmentGenerated' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.7 NAME 'ipaReplTopoManagedAgreementState' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +attributetypes: ( 2.16.840.1.113730.3.8.20.2.8 NAME 'ipaReplTopoManagedSuffix' DESC 'IPA defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'FreeIPA' ) +objectclasses: ( 2.16.840.1.113730.3.8.20.1.1 NAME 'ipaReplTopoConf' DESC 'IPA defined objectclass' SUP top STRUCTURAL MUST ipaReplTopoConfRoot MAY ( cn $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal ) X-ORIGIN 'Free IPA' ) +objectclasses: ( 2.16.840.1.113730.3.8.20.1.2 NAME 'ipaReplTopoSegment' DESC 'IPA defined objectclass' SUP top STRUCTURAL MUST ( ipaReplTopoSegmentDirection $ ipaReplTopoSegmentLeftNode $ ipaReplTopoSegmentRightNode) MAY ( cn $ ipaReplTopoSegmentStatus $ ipaReplTopoSegmentGenerated $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal $ nsds5BeginReplicaRefresh $ description $ nsds5replicaTimeout $ nsds5ReplicaEnabled $ nsds5ReplicaStripAttrs $ nsds5replicaSessionPauseTime $ nsds5ReplicaProtocolTimeout ) X-ORIGIN 'Free IPA' ) +objectclasses: ( 2.16.840.1.113730.3.8.20.1.3 NAME 'ipaReplTopoManagedAgreement' DESC 'marker objectclass for managed replication agreements' SUP top AUXILIARY MAY ( ipaReplTopoManagedAgreementState ) X-ORIGIN 'Free IPA' ) +objectclasses: ( 2.16.840.1.113730.3.8.20.1.4 NAME 'ipaReplTopoManagedServer' DESC 'part of managed replication topology' SUP top AUXILIARY MAY ( ipaReplTopoManagedSuffix ) X-ORIGIN 'Free IPA' ) diff --git a/install/share/Makefile.am b/install/share/Makefile.am index ca6128e29..da3ed3176 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -20,6 +20,7 @@ app_DATA = \ 65ipacertstore.ldif \ 65ipasudo.ldif \ 70ipaotp.ldif \ + 70topology.ldif \ 71idviews.ldif \ anonymous-vlv.ldif \ bootstrap-template.ldif \ |