From 09fb073e8210f1c8239c0b253672e613822fc553 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Thu, 24 Jun 2010 10:31:52 -0400 Subject: Replication version checking. Whenever we upgrade IPA such that any data incompatibilities might occur then we need to bump the DATA_VERSION value so that data will not replicate to other servers. The idea is that you can do an in-place upgrade of each IPA server and the different versions own't pollute each other with bad data. --- Makefile | 4 + VERSION | 13 ++ daemons/configure.ac | 1 + daemons/ipa-slapi-plugins/Makefile.am | 1 + daemons/ipa-slapi-plugins/ipa-version/Makefile.am | 44 +++++ .../ipa-version/ipa_repl_version.c | 211 +++++++++++++++++++++ .../ipa-version/version-conf.ldif | 17 ++ ipa.spec.in | 2 + ipaserver/install/dsinstance.py | 4 + 9 files changed, 297 insertions(+) create mode 100644 daemons/ipa-slapi-plugins/ipa-version/Makefile.am create mode 100644 daemons/ipa-slapi-plugins/ipa-version/ipa_repl_version.c create mode 100644 daemons/ipa-slapi-plugins/ipa-version/version-conf.ldif diff --git a/Makefile b/Makefile index cfdcfcfd..4952033d 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,10 @@ version-update: release-update sed -e s/__VERSION__/$(IPA_VERSION)/ ipapython/version.py.in \ > ipapython/version.py perl -pi -e "s:__NUM_VERSION__:$(IPA_VERSION_MAJOR)$(IPA_VERSION_MINOR)$(IPA_VERSION_RELEASE):" ipapython/version.py + sed -e s/__VERSION__/$(IPA_VERSION)/ daemons/ipa-version.h.in \ + > daemons/ipa-version.h + perl -pi -e "s:__NUM_VERSION__:$(IPA_VERSION_MAJOR)$(IPA_VERSION_MINOR)$(IPA_VERSION_RELEASE):" daemons/ipa-version.h + perl -pi -e "s:__DATA_VERSION__:$(IPA_DATA_VERSION):" daemons/ipa-version.h sed -e s/__VERSION__/$(IPA_VERSION)/ -e s/__RELEASE__/$(IPA_RPM_RELEASE)/ \ ipa-client/ipa-client.spec.in > ipa-client/ipa-client.spec diff --git a/VERSION b/VERSION index 06d27957..8eef15b4 100644 --- a/VERSION +++ b/VERSION @@ -53,3 +53,16 @@ IPA_VERSION_RC_RELEASE= # -> "1.0.0GITabcdefg" # ######################################################## IPA_VERSION_IS_GIT_SNAPSHOT="yes" + +######################################################## +# The version of IPA data. This is used to identify # +# incompatibilities in data that could cause issues # +# with replication. If the built-in versions don't # +# match exactly then replication will fail. # +# # +# The format is %Y%m%d%H%M%S # +# # +# e.g. IPA_DATA_VERSION=`date +%Y%m%d%H%M%S` # +# -> "20100614120000" # +######################################################## +IPA_DATA_VERSION=20100614120000 diff --git a/daemons/configure.ac b/daemons/configure.ac index 7f0fd680..5b520897 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -264,6 +264,7 @@ AC_CONFIG_FILES([ ipa-slapi-plugins/ipa-memberof/Makefile ipa-slapi-plugins/ipa-pwd-extop/Makefile ipa-slapi-plugins/ipa-winsync/Makefile + ipa-slapi-plugins/ipa-version/Makefile ]) AC_OUTPUT diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am index f5c23743..ae0a801c 100644 --- a/daemons/ipa-slapi-plugins/Makefile.am +++ b/daemons/ipa-slapi-plugins/Makefile.am @@ -3,6 +3,7 @@ NULL = SUBDIRS = \ ipa-enrollment \ ipa-pwd-extop \ + ipa-version \ ipa-winsync \ $(NULL) diff --git a/daemons/ipa-slapi-plugins/ipa-version/Makefile.am b/daemons/ipa-slapi-plugins/ipa-version/Makefile.am new file mode 100644 index 00000000..331ce7f1 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-version/Makefile.am @@ -0,0 +1,44 @@ +NULL = + +INCLUDES = \ + -I. \ + -I../../ \ + -I$(srcdir) \ + -I/usr/include/dirsrv \ + -DPREFIX=\""$(prefix)"\" \ + -DBINDIR=\""$(bindir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + $(MOZLDAP_CFLAGS) \ + $(KRB5_CFLAGS) \ + $(WARN_CFLAGS) \ + $(NULL) + +plugindir = $(libdir)/dirsrv/plugins +plugin_LTLIBRARIES = \ + libipa_repl_version.la \ + $(NULL) + +libipa_repl_version_la_SOURCES = \ + ipa_repl_version.c \ + $(NULL) + +libipa_repl_version_la_LDFLAGS = -avoid-version + +libipa_repl_version_la_LIBADD = \ + $(MOZLDAP_LIBS) \ + $(NULL) + +appdir = $(IPA_DATA_DIR) +app_DATA = \ + version-conf.ldif \ + $(NULL) + +EXTRA_DIST = \ + $(app_DATA) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in diff --git a/daemons/ipa-slapi-plugins/ipa-version/ipa_repl_version.c b/daemons/ipa-slapi-plugins/ipa-version/ipa_repl_version.c new file mode 100644 index 00000000..a9380013 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-version/ipa_repl_version.c @@ -0,0 +1,211 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2010 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "slapi-plugin.h" +#include "repl-session-plugin.h" +#include "ipa-version.h" +#include + +/* Identify the type of data we're sending, an unsigned int in this case */ +#define REPL_VERSION_DATA_GUID "2D562D8B-2F30-4447-AF76-2B721D1D5F6A" + +static char *repl_version_plugin_name = "ipa_replication_version"; +static char *data_version = NULL; + +/* + * Plugin identifiers + */ +static Slapi_PluginDesc repl_version_pdesc = { + "ipa-repl-version-plugin", + "Red Hat, Inc.", + "1.0", + "IPA Replication version plugin" +}; + +static Slapi_ComponentId *repl_version_plugin_id = NULL; + + +/* + * Replication Version Callbacks + */ + +/* + * This is called on a master when we are about to acquire a + * replica. + * + * Returning non-0 will abort the replication session. This + * results in the master going into incremental backoff mode. + */ +static int +repl_version_plugin_pre_acquire_cb(void *cookie, const Slapi_DN *repl_subtree, + int is_total, char **data_guid, struct berval **data) +{ + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "repl_version_plugin_pre_acquire_cb() called for suffix \"%s\", " + "is_total: \"%s\".\n", slapi_sdn_get_ndn(repl_subtree), + is_total ? "TRUE" : "FALSE"); + + /* allocate some data to be sent to the replica */ + *data_guid = slapi_ch_smprintf("%s", REPL_VERSION_DATA_GUID); + *data = (struct berval *)slapi_ch_malloc(sizeof(struct berval)); + (*data)->bv_val = slapi_ch_smprintf("%s", data_version); + (*data)->bv_len = strlen((*data)->bv_val) + 1; + + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "repl_version_plugin_pre_acquire_cb() sending data: guid: \"%s\" data: \"%s\".\n", + *data_guid, (*data)->bv_val); + + return 0; +} + +/* + * This is called on a replica when it receives a start replication + * extended operation from a master. + * + * The data sent by the master (version) is compared with our own + * hardcoded version to determine if replication can proceed or not. + * + * The replication plug-in will take care of freeing data_guid and data. + * + * Returning non-0 will abort the replication session. This + * results in the master going into incremental backoff mode. + */ +static int +repl_version_plugin_recv_acquire_cb(const char *repl_subtree, int is_total, + const char *data_guid, const struct berval *data) +{ + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "test_repl_session_plugin_recv_acquire_cb() called for suffix \"%s\", is_total: \"%s\".\n", + repl_subtree, is_total ? "TRUE" : "FALSE"); + + /* compare our data version to the master data version */ + if (data_guid && data && (strcmp(data_guid, REPL_VERSION_DATA_GUID) == 0)) { + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "repl_version_plugin_recv_acquire_cb() received data: guid: \"%s\" data: \"%s\".\n", + data_guid, data->bv_val); + if (!(strcmp(data_version, data->bv_val) == 0)) { + slapi_log_error(SLAPI_LOG_FATAL, repl_version_plugin_name, + "Incompatible IPA versions, pausing replication. This server: \"%s\" remote server: \"%s\".\n", data_version, data->bv_val); + return 1; + } + } + + return 0; +} + +/* + * Callback list for registering API + */ +static void *repl_version_api[] = { + NULL, /* reserved for api broker use, must be zero */ + NULL, /* init cb */ + repl_version_plugin_pre_acquire_cb, + NULL, /* reply_acquire_cb */ + NULL, /* post_acquire_cb */ + repl_version_plugin_recv_acquire_cb, + NULL /* destroy cb */ +}; + +/* + * Plug-in framework functions + */ +static int +repl_version_plugin_start(Slapi_PBlock *pb) +{ + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "--> repl_version_plugin_start -- begin\n"); + + data_version = slapi_ch_smprintf("%llu", DATA_VERSION); + + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "<-- repl_version_plugin_start -- end\n"); + return 0; +} + +static int +repl_version_plugin_close(Slapi_PBlock *pb) +{ + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "--> repl_version_plugin_close -- begin\n"); + + slapi_apib_unregister(REPL_SESSION_v1_0_GUID); + + slapi_ch_free_string(&data_version); + + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "<-- repl_version_plugin_close -- end\n"); + return 0; +} + +int repl_version_plugin_init(Slapi_PBlock *pb) +{ + slapi_log_error(SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "--> repl_version_plugin_init -- begin\n"); + + if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01 ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, + (void *) repl_version_plugin_start ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, + (void *) repl_version_plugin_close ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, + (void *)&repl_version_pdesc ) != 0 ) + { + slapi_log_error( SLAPI_LOG_FATAL, repl_version_plugin_name, + "<-- repl_version_plugin_init -- failed to register plugin -- end\n"); + return -1; + } + + if( slapi_apib_register(REPL_SESSION_v1_0_GUID, repl_version_api) ) { + slapi_log_error( SLAPI_LOG_FATAL, repl_version_plugin_name, + "<-- repl_version_plugin_start -- failed to register repl_version api -- end\n"); + return -1; + } + + + /* Retrieve and save the plugin identity to later pass to + internal operations */ + if (slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &repl_version_plugin_id) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, repl_version_plugin_name, + "<-- repl_version_plugin_init -- failed to retrieve plugin identity -- end\n"); + return -1; + } + + slapi_log_error( SLAPI_LOG_PLUGIN, repl_version_plugin_name, + "<-- repl_version_plugin_init -- end\n"); + return 0; +} diff --git a/daemons/ipa-slapi-plugins/ipa-version/version-conf.ldif b/daemons/ipa-slapi-plugins/ipa-version/version-conf.ldif new file mode 100644 index 00000000..99e43597 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-version/version-conf.ldif @@ -0,0 +1,17 @@ +dn: cn=IPA Version Replication,cn=plugins,cn=config +changetype: add +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: IPA Version Replication +nsslapd-pluginpath: libipa_repl_version +nsslapd-plugininitfunc: repl_version_plugin_init +nsslapd-plugintype: preoperation +nsslapd-pluginenabled: on +nsslapd-pluginid: ipa_repl_version +nsslapd-pluginversion: 1.0 +nsslapd-pluginvendor: Red Hat, Inc. +nsslapd-plugindescription: IPA Replication version plugin +nsslapd-plugin-depends-on-type: database +nsslapd-plugin-depends-on-named: Multimaster Replication Plugin + diff --git a/ipa.spec.in b/ipa.spec.in index 2a46ab05..9d6aa69f 100644 --- a/ipa.spec.in +++ b/ipa.spec.in @@ -263,6 +263,7 @@ make client-install DESTDIR=%{buildroot} rm %{buildroot}/%{plugin_dir}/libipa_pwd_extop.la rm %{buildroot}/%{plugin_dir}/libipa_enrollment_extop.la rm %{buildroot}/%{plugin_dir}/libipa_winsync.la +rm %{buildroot}/%{plugin_dir}/libipa_repl_version.la # Some user-modifiable HTML files are provided. Move these to /etc # and link back. @@ -409,6 +410,7 @@ fi %attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so %attr(755,root,root) %{plugin_dir}/libipa_enrollment_extop.so %attr(755,root,root) %{plugin_dir}/libipa_winsync.so +%attr(755,root,root) %{plugin_dir}/libipa_repl_version.so %dir %{_localstatedir}/lib/ipa %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore %dir %{_localstatedir}/cache/ipa diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 1a80a560..05c0c6e3 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -179,6 +179,7 @@ class DsInstance(service.Service): self.step("enabling memberof plugin", self.__add_memberof_module) self.step("enabling referential integrity plugin", self.__add_referint_module) self.step("enabling winsync plugin", self.__add_winsync_module) + self.step("configuring replication version plugin", self.__config_version_module) self.step("enabling IPA enrollment plugin", self.__add_enrollment_module) self.step("enabling ldapi", self.__enable_ldapi) self.step("configuring uniqueness plugin", self.__set_unique_attrs) @@ -338,6 +339,9 @@ class DsInstance(service.Service): def __add_winsync_module(self): self._ldap_mod("ipa-winsync-conf.ldif") + def __config_version_module(self): + self._ldap_mod("ipa-version-conf.ldif") + def __add_enrollment_module(self): self._ldap_mod("enrollment-conf.ldif", self.sub_dict) -- cgit