mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Create tool to manage dogtag replication agreements
For the most part the existing replication code worked with the following exceptions: - Added more port options - It assumed that initial connections were done to an SSL port. Added ability to use startTLS - It assumed that the name of the agreement was the same on both sides. In dogtag one is marked as master and one as clone. A new option is added, master, the determines which side we're working on or None if it isn't a dogtag agreement. - Don't set the attribute exclude list on dogtag agreements - dogtag doesn't set a schedule by default (which is actually recommended by 389-ds). This causes problems when doing a force-sync though so if one is done we set a schedule to run all the time. Otherwise the temporary schedule can't be removed (LDAP operations error). https://fedorahosted.org/freeipa/ticket/1250
This commit is contained in:
parent
2f650b60a4
commit
3fdca99c48
@ -365,6 +365,7 @@ fi
|
|||||||
%{_sbindir}/ipa-replica-install
|
%{_sbindir}/ipa-replica-install
|
||||||
%{_sbindir}/ipa-replica-prepare
|
%{_sbindir}/ipa-replica-prepare
|
||||||
%{_sbindir}/ipa-replica-manage
|
%{_sbindir}/ipa-replica-manage
|
||||||
|
%{_sbindir}/ipa-csreplica-manage
|
||||||
%{_sbindir}/ipa-server-certinstall
|
%{_sbindir}/ipa-server-certinstall
|
||||||
%{_sbindir}/ipa-ldap-updater
|
%{_sbindir}/ipa-ldap-updater
|
||||||
%{_sbindir}/ipa-compat-manage
|
%{_sbindir}/ipa-compat-manage
|
||||||
@ -437,6 +438,7 @@ fi
|
|||||||
%{_mandir}/man1/ipa-replica-conncheck.1.gz
|
%{_mandir}/man1/ipa-replica-conncheck.1.gz
|
||||||
%{_mandir}/man1/ipa-replica-install.1.gz
|
%{_mandir}/man1/ipa-replica-install.1.gz
|
||||||
%{_mandir}/man1/ipa-replica-manage.1.gz
|
%{_mandir}/man1/ipa-replica-manage.1.gz
|
||||||
|
%{_mandir}/man1/ipa-csreplica-manage.1.gz
|
||||||
%{_mandir}/man1/ipa-replica-prepare.1.gz
|
%{_mandir}/man1/ipa-replica-prepare.1.gz
|
||||||
%{_mandir}/man1/ipa-server-certinstall.1.gz
|
%{_mandir}/man1/ipa-server-certinstall.1.gz
|
||||||
%{_mandir}/man1/ipa-server-install.1.gz
|
%{_mandir}/man1/ipa-server-install.1.gz
|
||||||
@ -504,6 +506,9 @@ fi
|
|||||||
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
|
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Jul 14 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-6
|
||||||
|
- Add ipa-csreplica-manage tool.
|
||||||
|
|
||||||
* Wed Jul 6 2011 Adam Young <ayoung@redhat.com> - 2.0.90-5
|
* Wed Jul 6 2011 Adam Young <ayoung@redhat.com> - 2.0.90-5
|
||||||
- Add HTML file describing issues with HBAC deny rules
|
- Add HTML file describing issues with HBAC deny rules
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ sbin_SCRIPTS = \
|
|||||||
ipa-replica-install \
|
ipa-replica-install \
|
||||||
ipa-replica-prepare \
|
ipa-replica-prepare \
|
||||||
ipa-replica-manage \
|
ipa-replica-manage \
|
||||||
|
ipa-csreplica-manage \
|
||||||
ipa-server-certinstall \
|
ipa-server-certinstall \
|
||||||
ipactl \
|
ipactl \
|
||||||
ipa-compat-manage \
|
ipa-compat-manage \
|
||||||
|
452
install/tools/ipa-csreplica-manage
Executable file
452
install/tools/ipa-csreplica-manage
Executable file
@ -0,0 +1,452 @@
|
|||||||
|
#! /usr/bin/python -E
|
||||||
|
# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
#
|
||||||
|
# Based on ipa-replica-manage by Karl MacMillan <kmacmillan@mentalrootkit.com>
|
||||||
|
#
|
||||||
|
# Copyright (C) 2011 Red Hat
|
||||||
|
# see file 'COPYING' for use and warranty information
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
import getpass, ldap, krbV
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ipapython import ipautil
|
||||||
|
from ipaserver.install import replication, installutils
|
||||||
|
from ipaserver import ipaldap
|
||||||
|
from ipapython import version
|
||||||
|
from ipalib import api, errors, util
|
||||||
|
from ipalib.dn import DN
|
||||||
|
|
||||||
|
CACERT = "/etc/ipa/ca.crt"
|
||||||
|
PORT = 7389
|
||||||
|
|
||||||
|
# dict of command name and tuples of min/max num of args needed
|
||||||
|
commands = {
|
||||||
|
"list":(0, 1, "[master fqdn]", ""),
|
||||||
|
"connect":(1, 2, "<master fqdn> [other master fqdn]",
|
||||||
|
"must provide the name of the servers to connect"),
|
||||||
|
"disconnect":(1, 2, "<master fqdn> [other master fqdn]",
|
||||||
|
"must provide the name of the server to disconnect"),
|
||||||
|
"del":(1, 1, "<master fqdn>",
|
||||||
|
"must provide hostname of master to delete"),
|
||||||
|
"re-initialize":(0, 0, "", ""),
|
||||||
|
"force-sync":(0, 0, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
def convert_error(exc):
|
||||||
|
"""
|
||||||
|
LDAP exceptions are a dictionary, make them prettier.
|
||||||
|
"""
|
||||||
|
if isinstance(exc, ldap.LDAPError):
|
||||||
|
desc = exc.args[0]['desc'].strip()
|
||||||
|
info = exc.args[0].get('info', '').strip()
|
||||||
|
return '%s %s' % (desc, info)
|
||||||
|
else:
|
||||||
|
return str(exc)
|
||||||
|
|
||||||
|
class CSReplicationManager(replication.ReplicationManager):
|
||||||
|
|
||||||
|
def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=True):
|
||||||
|
super(CSReplicationManager, self).__init__(realm, hostname, dirman_passwd, port, starttls)
|
||||||
|
self.suffix = 'o=ipaca'
|
||||||
|
self.hostnames = [] # set before calling or agreement_dn() will fail
|
||||||
|
|
||||||
|
def agreement_dn(self, hostname, master=None):
|
||||||
|
"""
|
||||||
|
Construct a dogtag replication agreement name. This needs to be much
|
||||||
|
more agressive than the IPA replication agreements because the name
|
||||||
|
is different on each side.
|
||||||
|
|
||||||
|
hostname is the local hostname, not the remote one, for both sides
|
||||||
|
|
||||||
|
NOTE: The agreement number is hardcoded in dogtag as well
|
||||||
|
|
||||||
|
TODO: configurable instance name
|
||||||
|
"""
|
||||||
|
dn = None
|
||||||
|
cn = None
|
||||||
|
instance_name = 'pki-ca'
|
||||||
|
|
||||||
|
# if master is not None we know what dn to return:
|
||||||
|
if master is not None:
|
||||||
|
if master is True:
|
||||||
|
name = "master"
|
||||||
|
else:
|
||||||
|
name = "clone"
|
||||||
|
cn="%sAgreement1-%s-%s" % (name, hostname, instance_name)
|
||||||
|
dn = str(DN("cn=%s, %s" % (cn, self.replica_dn())))
|
||||||
|
return (cn, dn)
|
||||||
|
|
||||||
|
for host in self.hostnames:
|
||||||
|
for master in ["master", "clone"]:
|
||||||
|
try:
|
||||||
|
cn="%sAgreement1-%s-%s" % (master, host, instance_name)
|
||||||
|
dn = "cn=%s, %s" % (cn, self.replica_dn())
|
||||||
|
self.conn.getEntry(dn, ldap.SCOPE_BASE)
|
||||||
|
return (cn, dn)
|
||||||
|
except errors.NotFound:
|
||||||
|
dn = None
|
||||||
|
cn = None
|
||||||
|
|
||||||
|
raise errors.NotFound(reason='No agreement found for %s' % hostname)
|
||||||
|
|
||||||
|
def delete_referral(self, hostname):
|
||||||
|
esc1_suffix = self.suffix.replace('=', '\\3D').replace(',', '\\2C')
|
||||||
|
esc2_suffix = self.suffix.replace('=', '%3D').replace(',', '%2C')
|
||||||
|
dn = 'cn=%s,cn=mapping tree,cn=config' % esc1_suffix
|
||||||
|
# TODO: should we detect proto/port somehow ?
|
||||||
|
mod = [(ldap.MOD_DELETE, 'nsslapd-referral',
|
||||||
|
'ldap://%s:%s/%s' % (hostname, PORT, esc2_suffix))]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.conn.modify_s(dn, mod)
|
||||||
|
except Exception, e:
|
||||||
|
logging.debug("Failed to remove referral value: %s" % convert_error(e))
|
||||||
|
|
||||||
|
def parse_options():
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
parser = OptionParser(version=version.VERSION)
|
||||||
|
parser.add_option("-H", "--host", dest="host", help="starting host")
|
||||||
|
parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
|
||||||
|
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
|
||||||
|
help="provide additional information")
|
||||||
|
parser.add_option("-f", "--force", dest="force", action="store_true", default=False,
|
||||||
|
help="ignore some types of errors")
|
||||||
|
parser.add_option("--from", dest="fromhost", help="Host to get data from")
|
||||||
|
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
valid_syntax = False
|
||||||
|
|
||||||
|
if len(args):
|
||||||
|
n = len(args) - 1
|
||||||
|
k = commands.keys()
|
||||||
|
for cmd in k:
|
||||||
|
if cmd == args[0]:
|
||||||
|
v = commands[cmd]
|
||||||
|
err = None
|
||||||
|
if n < v[0]:
|
||||||
|
err = v[3]
|
||||||
|
elif n > v[1]:
|
||||||
|
err = "too many arguments"
|
||||||
|
else:
|
||||||
|
valid_syntax = True
|
||||||
|
if err:
|
||||||
|
parser.error("Invalid syntax: %s\nUsage: %s [options] %s" % (err, cmd, v[2]))
|
||||||
|
|
||||||
|
if not valid_syntax:
|
||||||
|
cmdstr = " | ".join(commands.keys())
|
||||||
|
parser.error("must provide a command [%s]" % cmdstr)
|
||||||
|
|
||||||
|
# set log level
|
||||||
|
if options.verbose:
|
||||||
|
# if verbose, output events at INFO level if not already
|
||||||
|
mylogger = logging.getLogger()
|
||||||
|
if mylogger.getEffectiveLevel() > logging.INFO:
|
||||||
|
mylogger.setLevel(logging.INFO)
|
||||||
|
# else user has already configured logging externally lower
|
||||||
|
return options, args
|
||||||
|
|
||||||
|
def list_replicas(realm, host, replica, dirman_passwd, verbose):
|
||||||
|
|
||||||
|
peers = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# connect to main IPA LDAP server
|
||||||
|
conn = ipaldap.IPAdmin(host, 636, cacert=CACERT)
|
||||||
|
conn.do_simple_bind(bindpw=dirman_passwd)
|
||||||
|
|
||||||
|
dn = str(DN('cn=masters,cn=ipa,cn=etc,%s' % util.realm_to_suffix(realm)))
|
||||||
|
entries = conn.search_s(dn, ldap.SCOPE_ONELEVEL)
|
||||||
|
|
||||||
|
for ent in entries:
|
||||||
|
try:
|
||||||
|
cadn = DN(('cn', 'CA'), DN(ent.dn))
|
||||||
|
entry = conn.getEntry(str(cadn), ldap.SCOPE_BASE)
|
||||||
|
peers[ent.cn] = ['master', '']
|
||||||
|
except errors.NotFound:
|
||||||
|
peers[ent.cn] = ['CA not configured', '']
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("Failed to get data from '%s': %s" % (host, convert_error(e)))
|
||||||
|
finally:
|
||||||
|
conn.unbind_s()
|
||||||
|
|
||||||
|
if not replica:
|
||||||
|
for k, p in peers.iteritems():
|
||||||
|
print '%s: %s' % (k, p[0])
|
||||||
|
return
|
||||||
|
|
||||||
|
repl = CSReplicationManager(realm, replica, dirman_passwd, PORT, True)
|
||||||
|
entries = repl.find_replication_agreements()
|
||||||
|
|
||||||
|
for entry in entries:
|
||||||
|
print '%s' % entry.nsds5replicahost
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print " last init status: %s" % entry.nsds5replicalastinitstatus
|
||||||
|
print " last init ended: %s" % str(ipautil.parse_generalized_time(entry.nsds5replicalastinitend))
|
||||||
|
print " last update status: %s" % entry.nsds5replicalastupdatestatus
|
||||||
|
print " last update ended: %s" % str(ipautil.parse_generalized_time(entry.nsds5replicalastupdateend))
|
||||||
|
|
||||||
|
def del_link(realm, replica1, replica2, dirman_passwd, force=False):
|
||||||
|
|
||||||
|
repl2 = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
repl1 = CSReplicationManager(realm, replica1, dirman_passwd, PORT, True)
|
||||||
|
|
||||||
|
repl1.hostnames = [replica1, replica2]
|
||||||
|
type1 = repl1.get_agreement_type(replica2)
|
||||||
|
|
||||||
|
repl_list = repl1.find_ipa_replication_agreements()
|
||||||
|
if not force and len(repl_list) <= 1:
|
||||||
|
print "Cannot remove the last replication link of '%s'" % replica1
|
||||||
|
print "Please use the 'del' command to remove it from the domain"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
sys.exit("'%s' has no replication agreement for '%s'" % (replica1, replica2))
|
||||||
|
except errors.NotFound:
|
||||||
|
sys.exit("'%s' has no replication agreement for '%s'" % (replica1, replica2))
|
||||||
|
except ldap.SERVER_DOWN, e:
|
||||||
|
sys.exit("Unable to connect to %s:%d: %s" % (replica1, PORT, convert_error(e)))
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("Failed to get data from '%s': %s" % (replica1, convert_error(e)))
|
||||||
|
|
||||||
|
try:
|
||||||
|
repl2 = CSReplicationManager(realm, replica2, dirman_passwd, PORT, True)
|
||||||
|
repl2.hostnames = [replica1, replica2]
|
||||||
|
|
||||||
|
repl_list = repl1.find_ipa_replication_agreements()
|
||||||
|
if not force and len(repl_list) <= 1:
|
||||||
|
print "Cannot remove the last replication link of '%s'" % replica2
|
||||||
|
print "Please use the 'del' command to remove it from the domain"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
print "'%s' has no replication agreement for '%s'" % (replica2, replica1)
|
||||||
|
if not force:
|
||||||
|
sys.exit(1)
|
||||||
|
except errors.NotFound:
|
||||||
|
print "'%s' has no replication agreement for '%s'" % (replica2, replica1)
|
||||||
|
if not force:
|
||||||
|
return
|
||||||
|
except Exception, e:
|
||||||
|
print "Failed to get data from '%s': %s" % (replica2, convert_error(e))
|
||||||
|
if not force:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if repl2:
|
||||||
|
failed = False
|
||||||
|
try:
|
||||||
|
repl2.delete_agreement(replica1)
|
||||||
|
repl2.delete_referral(replica1)
|
||||||
|
except Exception, e:
|
||||||
|
print "Unable to remove agreement on %s: %s" % (replica2, convert_error(e))
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
if force:
|
||||||
|
print "Forcing removal on '%s'" % replica1
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not repl2 and force:
|
||||||
|
print "Forcing removal on '%s'" % replica1
|
||||||
|
|
||||||
|
repl1.delete_agreement(replica2)
|
||||||
|
repl1.delete_referral(replica2)
|
||||||
|
|
||||||
|
def del_master(realm, hostname, options):
|
||||||
|
|
||||||
|
force_del = False
|
||||||
|
|
||||||
|
delrepl = None
|
||||||
|
# 1. Connect to the dogtag DS to be removed.
|
||||||
|
try:
|
||||||
|
delrepl = CSReplicationManager(realm, hostname, options.dirman_passwd)
|
||||||
|
except Exception, e:
|
||||||
|
if not options.force:
|
||||||
|
print "Unable to delete replica %s: %s" % (hostname, convert_error(e))
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print "Unable to connect to replica %s, forcing removal" % hostname
|
||||||
|
force_del = True
|
||||||
|
|
||||||
|
# 2. Connect to the local dogtag DS server
|
||||||
|
try:
|
||||||
|
thisrepl = CSReplicationManager(realm, options.host,
|
||||||
|
options.dirman_passwd)
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("Failed to connect to server %s: %s" % (options.host, convert_error(e)))
|
||||||
|
|
||||||
|
# 2. Get list of agreements.
|
||||||
|
if delrepl is None:
|
||||||
|
# server not up, just remove it from this server
|
||||||
|
replica_names = [options.host]
|
||||||
|
else:
|
||||||
|
replica_names = delrepl.find_ipa_replication_agreements()
|
||||||
|
|
||||||
|
# 3. Remove each agreement
|
||||||
|
for r in replica_names:
|
||||||
|
try:
|
||||||
|
del_link(realm, r, hostname, options.dirman_passwd, force=True)
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("There were issues removing a connection: %s" % convert_error(e))
|
||||||
|
|
||||||
|
def add_link(realm, replica1, replica2, dirman_passwd, options):
|
||||||
|
try:
|
||||||
|
conn = ipaldap.IPAdmin(replica2, 636, cacert=CACERT)
|
||||||
|
conn.do_simple_bind(bindpw=dirman_passwd)
|
||||||
|
|
||||||
|
dn = str(DN('cn=CA,cn=%s,cn=masters,cn=ipa,cn=etc,%s' % (replica2, util.realm_to_suffix(realm))))
|
||||||
|
conn.search_s(dn, ldap.SCOPE_ONELEVEL)
|
||||||
|
conn.unbind_s()
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
sys.exit('%s does not have a CA configured.' % replica2)
|
||||||
|
except ldap.SERVER_DOWN, e:
|
||||||
|
sys.exit("Unable to connect to %s:636: %s" % (replica2, convert_error(e)))
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("Failed to get data from '%s': %s" % (replica1, convert_error(e)))
|
||||||
|
|
||||||
|
try:
|
||||||
|
repl1 = CSReplicationManager(realm, replica1, dirman_passwd, PORT, True)
|
||||||
|
entries = repl1.find_replication_agreements()
|
||||||
|
for e in entries:
|
||||||
|
if replica1 in e.dn or replica2 in e.dn:
|
||||||
|
sys.exit('This replication agreement already exists.')
|
||||||
|
repl1.hostnames = [replica1, replica2]
|
||||||
|
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
sys.exit("Cannot find replica '%s'" % replica1)
|
||||||
|
except ldap.SERVER_DOWN, e:
|
||||||
|
sys.exit("Unable to connect to %s:%d %s" % (replica1, PORT, convert_error(e)))
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("Failed to get data from '%s': %s" % (replica1, convert_error(e)))
|
||||||
|
|
||||||
|
repl1.setup_replication(replica2, PORT, 0, "cn=Directory Manager", dirman_passwd, True)
|
||||||
|
print "Connected '%s' to '%s'" % (replica1, replica2)
|
||||||
|
|
||||||
|
def re_initialize(realm, options):
|
||||||
|
|
||||||
|
if not options.fromhost:
|
||||||
|
sys.exit("re-initialize requires the option --from <host name>")
|
||||||
|
|
||||||
|
repl = CSReplicationManager(realm, options.fromhost, options.dirman_passwd,
|
||||||
|
PORT, True)
|
||||||
|
|
||||||
|
thishost = installutils.get_fqdn()
|
||||||
|
|
||||||
|
filter = "(&(nsDS5ReplicaHost=%s)(|(objectclass=nsDSWindowsReplicationAgreement)(objectclass=nsds5ReplicationAgreement)))" % thishost
|
||||||
|
entry = repl.conn.search_s("cn=config", ldap.SCOPE_SUBTREE, filter)
|
||||||
|
if len(entry) == 0:
|
||||||
|
logging.error("Unable to find %s -> %s replication agreement" % (options.fromhost, thishost))
|
||||||
|
sys.exit(1)
|
||||||
|
if len(entry) > 1:
|
||||||
|
logging.error("Found multiple agreements for %s. Only initializing the first one returned: %s" % (thishost, entry[0].dn))
|
||||||
|
|
||||||
|
repl.initialize_replication(entry[0].dn, repl.conn)
|
||||||
|
repl.wait_for_repl_init(repl.conn, entry[0].dn)
|
||||||
|
|
||||||
|
def force_sync(realm, thishost, fromhost, dirman_passwd):
|
||||||
|
|
||||||
|
repl = CSReplicationManager(realm, fromhost, dirman_passwd, PORT, True)
|
||||||
|
try:
|
||||||
|
repl.force_sync(repl.conn, thishost)
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit(convert_error(e))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
options, args = parse_options()
|
||||||
|
|
||||||
|
# Just initialize the environment. This is so the installer can have
|
||||||
|
# access to the plugin environment
|
||||||
|
api_env = {}
|
||||||
|
api_env['in_server'] = True
|
||||||
|
|
||||||
|
if os.getegid() != 0:
|
||||||
|
api_env['log'] = None # turn off logging for non-root
|
||||||
|
|
||||||
|
api.bootstrap(**api_env)
|
||||||
|
api.finalize()
|
||||||
|
|
||||||
|
dirman_passwd = None
|
||||||
|
realm = krbV.default_context().default_realm
|
||||||
|
|
||||||
|
if options.host:
|
||||||
|
host = options.host
|
||||||
|
else:
|
||||||
|
host = installutils.get_fqdn()
|
||||||
|
|
||||||
|
options.host = host
|
||||||
|
|
||||||
|
if options.dirman_passwd:
|
||||||
|
dirman_passwd = options.dirman_passwd
|
||||||
|
else:
|
||||||
|
dirman_passwd = getpass.getpass("Directory Manager password: ")
|
||||||
|
|
||||||
|
options.dirman_passwd = dirman_passwd
|
||||||
|
|
||||||
|
if args[0] == "list":
|
||||||
|
replica = None
|
||||||
|
if len(args) == 2:
|
||||||
|
replica = args[1]
|
||||||
|
list_replicas(realm, host, replica, dirman_passwd, options.verbose)
|
||||||
|
elif args[0] == "del":
|
||||||
|
del_master(realm, args[1], options)
|
||||||
|
elif args[0] == "re-initialize":
|
||||||
|
re_initialize(realm, options)
|
||||||
|
elif args[0] == "force-sync":
|
||||||
|
if not options.fromhost:
|
||||||
|
sys.exit("force-sync requires the option --from <host name>")
|
||||||
|
force_sync(realm, host, options.fromhost, options.dirman_passwd)
|
||||||
|
elif args[0] == "connect":
|
||||||
|
if len(args) == 3:
|
||||||
|
replica1 = args[1]
|
||||||
|
replica2 = args[2]
|
||||||
|
elif len(args) == 2:
|
||||||
|
replica1 = host
|
||||||
|
replica2 = args[1]
|
||||||
|
add_link(realm, replica1, replica2, dirman_passwd, options)
|
||||||
|
elif args[0] == "disconnect":
|
||||||
|
if len(args) == 3:
|
||||||
|
replica1 = args[1]
|
||||||
|
replica2 = args[2]
|
||||||
|
elif len(args) == 2:
|
||||||
|
replica1 = host
|
||||||
|
replica2 = args[1]
|
||||||
|
del_link(realm, replica1, replica2, dirman_passwd)
|
||||||
|
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
||||||
|
except SystemExit, e:
|
||||||
|
sys.exit(e)
|
||||||
|
except ldap.INVALID_CREDENTIALS:
|
||||||
|
sys.exit("Invalid password")
|
||||||
|
except ldap.INSUFFICIENT_ACCESS:
|
||||||
|
sys.exit("Insufficient access")
|
||||||
|
except ldap.LOCAL_ERROR, e:
|
||||||
|
sys.exit(convert_error(e))
|
||||||
|
except ldap.SERVER_DOWN, e:
|
||||||
|
sys.exit("%s" % convert_error(e))
|
||||||
|
except Exception, e:
|
||||||
|
sys.exit("unexpected error: %s" % convert_error(e))
|
@ -8,6 +8,7 @@ man1_MANS = \
|
|||||||
ipa-replica-conncheck.1 \
|
ipa-replica-conncheck.1 \
|
||||||
ipa-replica-install.1 \
|
ipa-replica-install.1 \
|
||||||
ipa-replica-manage.1 \
|
ipa-replica-manage.1 \
|
||||||
|
ipa-csreplica-manage.1 \
|
||||||
ipa-replica-prepare.1 \
|
ipa-replica-prepare.1 \
|
||||||
ipa-server-certinstall.1 \
|
ipa-server-certinstall.1 \
|
||||||
ipa-server-install.1 \
|
ipa-server-install.1 \
|
||||||
|
93
install/tools/man/ipa-csreplica-manage.1
Normal file
93
install/tools/man/ipa-csreplica-manage.1
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
.\" A man page for ipa-csreplica-manage
|
||||||
|
.\" Copyright (C) 2011 Red Hat, Inc.
|
||||||
|
.\"
|
||||||
|
.\" This program is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the GNU General Public License as published by
|
||||||
|
.\" the Free Software Foundation, either version 3 of the License, or
|
||||||
|
.\" (at your option) any later version.
|
||||||
|
.\"
|
||||||
|
.\" This program is distributed in the hope that it will be useful, but
|
||||||
|
.\" WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
.\" General Public License for more details.
|
||||||
|
.\"
|
||||||
|
.\" You should have received a copy of the GNU General Public License
|
||||||
|
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
.\"
|
||||||
|
.\" Author: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
.\"
|
||||||
|
.TH "ipa-replica-manage" "1" "Jul 14 2011" "freeipa" ""
|
||||||
|
.SH "NAME"
|
||||||
|
ipa\-replica\-manage \- Manage an IPA CS replica
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
ipa\-replica\-manage [\fIOPTION\fR]... [connect|disconnect|del|list|re\-initialize|force\-sync]
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
Manages the CA replication agreements of an IPA server.
|
||||||
|
.TP
|
||||||
|
\fBconnect\fR [SERVER_A] <SERVER_B>
|
||||||
|
\- Adds a new replication agreement between SERVER_A/localhost and SERVER_B
|
||||||
|
.TP
|
||||||
|
\fBdisconnect\fR [SERVER_A] <SERVER_B>
|
||||||
|
\- Removes a replication agreement between SERVER_A/localhost and SERVER_B
|
||||||
|
.TP
|
||||||
|
\fBdel\fR <SERVER>
|
||||||
|
\- Removes all replication agreements and data about SERVER
|
||||||
|
.TP
|
||||||
|
\fBlist\fR [SERVER]
|
||||||
|
\- Lists all the servers or the list of agreements of SERVER
|
||||||
|
.TP
|
||||||
|
\fBre\-initialize\fR
|
||||||
|
\- Forces a full re\-initialization of the IPA CA server retrieving data from the server specified with the \-\-from option
|
||||||
|
.TP
|
||||||
|
\fBforce\-sync\fR
|
||||||
|
\- Immediately flush any data to be replicated from a server specified with the \-\-from option
|
||||||
|
.TP
|
||||||
|
The connect and disconnect options are used to manage the replication topology. When a replica is created it is only connected with the master that created it. The connect option may be used to connect it to other existing replicas.
|
||||||
|
.TP
|
||||||
|
The disconnect option cannot be used to remove the last link of a replica. To remove a replica from the topology use the del option.
|
||||||
|
.TP
|
||||||
|
If a replica is deleted and then re\-added within a short time-frame then the 389\-ds instance on the master that created it should be restarted before re\-installing the replica. The master will have the old service principals cached which will cause replication to fail.
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.TP
|
||||||
|
\fB\-H\fR \fIHOST\fR, \fB\-\-host\fR=\fIHOST\fR
|
||||||
|
The IPA server to manage.
|
||||||
|
The default is the machine on which the command is run
|
||||||
|
Not honoured by the re\-initialize command.
|
||||||
|
.TP
|
||||||
|
\fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
|
||||||
|
The Directory Manager password to use for authentication
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR, \fB\-\-verbose\fR
|
||||||
|
Provide additional information
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR, \fB\-\-force\fR
|
||||||
|
Ignore some types of errors
|
||||||
|
.TP
|
||||||
|
\fB\-\-from\fR=\fISERVER\fR
|
||||||
|
The server to pull the data from, used by the re\-initialize and force\-sync commands.
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.TP
|
||||||
|
List a server's replication agreements.
|
||||||
|
# ipa\-csreplica\-manage list srv1.example.com
|
||||||
|
srv2.example.com
|
||||||
|
srv3.example.com
|
||||||
|
.TP
|
||||||
|
Re\-initialize a replica:
|
||||||
|
# ipa\-csreplica\-manage re\-initialize \-\-from srv2.example.com
|
||||||
|
|
||||||
|
This will re\-initialize the data on the server where you execute the command, retrieving the data from the srv2.example.com replica
|
||||||
|
.TP
|
||||||
|
Add a new replication agreement:
|
||||||
|
# ipa\-csreplica\-manage connect srv2.example.com srv4.example.com
|
||||||
|
.TP
|
||||||
|
Remove an existing replication agreement:
|
||||||
|
# ipa\-csreplica\-manage disconnect srv1.example.com srv3.example.com
|
||||||
|
.TP
|
||||||
|
Completely remove a replica:
|
||||||
|
# ipa\-csreplica\-manage del srv4.example.com
|
||||||
|
.TP
|
||||||
|
Using connect/disconnect you can manage the replication topology.
|
||||||
|
.SH "EXIT STATUS"
|
||||||
|
0 if the command was successful
|
||||||
|
.TP
|
||||||
|
1 if an error occurred
|
@ -305,8 +305,8 @@ class DsInstance(service.Service):
|
|||||||
self.fqdn,
|
self.fqdn,
|
||||||
self.dm_password)
|
self.dm_password)
|
||||||
repl.setup_replication(self.master_fqdn,
|
repl.setup_replication(self.master_fqdn,
|
||||||
"cn=Directory Manager",
|
r_binddn="cn=Directory Manager",
|
||||||
self.dm_password)
|
r_bindpw=self.dm_password)
|
||||||
|
|
||||||
def __enable(self):
|
def __enable(self):
|
||||||
self.backup_state("enabled", self.is_enabled())
|
self.backup_state("enabled", self.is_enabled())
|
||||||
|
@ -29,6 +29,7 @@ from ldap import modlist
|
|||||||
from ipalib import util
|
from ipalib import util
|
||||||
from ipalib import errors
|
from ipalib import errors
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
|
from ipalib.dn import DN
|
||||||
|
|
||||||
DIRMAN_CN = "cn=directory manager"
|
DIRMAN_CN = "cn=directory manager"
|
||||||
CACERT = "/etc/ipa/ca.crt"
|
CACERT = "/etc/ipa/ca.crt"
|
||||||
@ -38,6 +39,7 @@ WIN_USER_CONTAINER = "cn=Users"
|
|||||||
IPA_USER_CONTAINER = "cn=users,cn=accounts"
|
IPA_USER_CONTAINER = "cn=users,cn=accounts"
|
||||||
PORT = 636
|
PORT = 636
|
||||||
TIMEOUT = 120
|
TIMEOUT = 120
|
||||||
|
REPL_MAN_DN = "cn=replication manager,cn=config"
|
||||||
|
|
||||||
IPA_REPLICA = 1
|
IPA_REPLICA = 1
|
||||||
WINSYNC = 2
|
WINSYNC = 2
|
||||||
@ -108,19 +110,26 @@ def enable_replication_version_checking(hostname, realm, dirman_passwd):
|
|||||||
else:
|
else:
|
||||||
conn.unbind()
|
conn.unbind()
|
||||||
|
|
||||||
class ReplicationManager:
|
class ReplicationManager(object):
|
||||||
"""Manage replication agreements between DS servers, and sync
|
"""Manage replication agreements between DS servers, and sync
|
||||||
agreements with Windows servers"""
|
agreements with Windows servers"""
|
||||||
def __init__(self, realm, hostname, dirman_passwd):
|
def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False):
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
|
self.port = port
|
||||||
self.dirman_passwd = dirman_passwd
|
self.dirman_passwd = dirman_passwd
|
||||||
self.realm = realm
|
self.realm = realm
|
||||||
|
self.starttls = starttls
|
||||||
tmp = util.realm_to_suffix(realm)
|
tmp = util.realm_to_suffix(realm)
|
||||||
self.suffix = ipaldap.IPAdmin.normalizeDN(tmp)
|
self.suffix = ipaldap.IPAdmin.normalizeDN(tmp)
|
||||||
|
|
||||||
# If we are passed a password we'll use it as the DM password
|
# If we are passed a password we'll use it as the DM password
|
||||||
# otherwise we'll do a GSSAPI bind.
|
# otherwise we'll do a GSSAPI bind.
|
||||||
self.conn = ipaldap.IPAdmin(hostname, port=PORT, cacert=CACERT)
|
if starttls:
|
||||||
|
self.conn = ipaldap.IPAdmin(hostname, port=port)
|
||||||
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
|
||||||
|
self.conn.start_tls_s()
|
||||||
|
else:
|
||||||
|
self.conn = ipaldap.IPAdmin(hostname, port=port, cacert=CACERT)
|
||||||
if dirman_passwd:
|
if dirman_passwd:
|
||||||
self.conn.do_simple_bind(bindpw=dirman_passwd)
|
self.conn.do_simple_bind(bindpw=dirman_passwd)
|
||||||
else:
|
else:
|
||||||
@ -130,7 +139,7 @@ class ReplicationManager:
|
|||||||
|
|
||||||
# these are likely constant, but you could change them
|
# these are likely constant, but you could change them
|
||||||
# at runtime if you really want
|
# at runtime if you really want
|
||||||
self.repl_man_dn = "cn=replication manager,cn=config"
|
self.repl_man_dn = REPL_MAN_DN
|
||||||
self.repl_man_cn = "replication manager"
|
self.repl_man_cn = "replication manager"
|
||||||
|
|
||||||
def _get_replica_id(self, conn, master_conn):
|
def _get_replica_id(self, conn, master_conn):
|
||||||
@ -152,7 +161,7 @@ class ReplicationManager:
|
|||||||
# Ok, either the entry doesn't exist or the attribute isn't set
|
# Ok, either the entry doesn't exist or the attribute isn't set
|
||||||
# so get it from the other master
|
# so get it from the other master
|
||||||
retval = -1
|
retval = -1
|
||||||
dn = "cn=replication, cn=etc, %s" % self.suffix
|
dn = str(DN("cn=replication, cn=etc, %s" % self.suffix))
|
||||||
try:
|
try:
|
||||||
replica = master_conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0]
|
replica = master_conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0]
|
||||||
if not replica.getValue('nsDS5ReplicaId'):
|
if not replica.getValue('nsDS5ReplicaId'):
|
||||||
@ -235,7 +244,7 @@ class ReplicationManager:
|
|||||||
conn.modify_s(dn, [(ldap.MOD_REPLACE, "userpassword", pw)])
|
conn.modify_s(dn, [(ldap.MOD_REPLACE, "userpassword", pw)])
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def delete_replication_manager(self, conn, dn="cn=replication manager,cn=config"):
|
def delete_replication_manager(self, conn, dn=REPL_MAN_DN):
|
||||||
try:
|
try:
|
||||||
conn.delete_s(dn)
|
conn.delete_s(dn)
|
||||||
except ldap.NO_SUCH_OBJECT:
|
except ldap.NO_SUCH_OBJECT:
|
||||||
@ -248,13 +257,21 @@ class ReplicationManager:
|
|||||||
return "2"
|
return "2"
|
||||||
|
|
||||||
def replica_dn(self):
|
def replica_dn(self):
|
||||||
return 'cn=replica, cn="%s", cn=mapping tree, cn=config' % self.suffix
|
return str(DN('cn=replica, cn="%s", cn=mapping tree, cn=config' % self.suffix))
|
||||||
|
|
||||||
def replica_config(self, conn, replica_id, replica_binddn):
|
def replica_config(self, conn, replica_id, replica_binddn):
|
||||||
dn = self.replica_dn()
|
dn = self.replica_dn()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn.getEntry(dn, ldap.SCOPE_BASE)
|
entry = conn.getEntry(dn, ldap.SCOPE_BASE)
|
||||||
|
managers = entry.getValues('nsDS5ReplicaBindDN')
|
||||||
|
for m in managers:
|
||||||
|
if DN(replica_binddn) == DN(m):
|
||||||
|
return
|
||||||
|
# Add the new replication manager
|
||||||
|
mod = [(ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn)]
|
||||||
|
conn.modify_s(dn, mod)
|
||||||
|
|
||||||
# replication is already configured
|
# replication is already configured
|
||||||
return
|
return
|
||||||
except errors.NotFound:
|
except errors.NotFound:
|
||||||
@ -409,24 +426,34 @@ class ReplicationManager:
|
|||||||
entry.setValues("nsds7NewWinGroupSyncEnabled", 'false')
|
entry.setValues("nsds7NewWinGroupSyncEnabled", 'false')
|
||||||
entry.setValues("nsds7WindowsDomain", windomain)
|
entry.setValues("nsds7WindowsDomain", windomain)
|
||||||
|
|
||||||
def agreement_dn(self, hostname):
|
def agreement_dn(self, hostname, master=None):
|
||||||
|
"""
|
||||||
|
IPA agreement use the same dn on both sides, dogtag does not.
|
||||||
|
master is not used for IPA agreements but for dogtag it will
|
||||||
|
tell which side we want.
|
||||||
|
"""
|
||||||
cn = "meTo%s" % (hostname)
|
cn = "meTo%s" % (hostname)
|
||||||
dn = "cn=%s, %s" % (cn, self.replica_dn())
|
dn = "cn=%s, %s" % (cn, self.replica_dn())
|
||||||
|
|
||||||
return (cn, dn)
|
return (cn, dn)
|
||||||
|
|
||||||
def setup_agreement(self, a_conn, b_hostname,
|
def setup_agreement(self, a_conn, b_hostname, port=389,
|
||||||
repl_man_dn=None, repl_man_passwd=None,
|
repl_man_dn=None, repl_man_passwd=None,
|
||||||
iswinsync=False, win_subtree=None, isgssapi=False):
|
iswinsync=False, win_subtree=None, isgssapi=False,
|
||||||
cn, dn = self.agreement_dn(b_hostname)
|
master=None):
|
||||||
|
"""
|
||||||
|
master is used to determine which side of the agreement we are
|
||||||
|
creating. This is only needed for dogtag replication agreements
|
||||||
|
which use a different name on each side. If master is None then
|
||||||
|
isn't a dogtag replication agreement.
|
||||||
|
"""
|
||||||
|
cn, dn = self.agreement_dn(b_hostname, master=master)
|
||||||
try:
|
try:
|
||||||
a_conn.getEntry(dn, ldap.SCOPE_BASE)
|
a_conn.getEntry(dn, ldap.SCOPE_BASE)
|
||||||
return
|
return
|
||||||
except errors.NotFound:
|
except errors.NotFound:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
port = 389
|
|
||||||
|
|
||||||
# List of attributes that need to be excluded from replication.
|
# List of attributes that need to be excluded from replication.
|
||||||
excludes = ('memberof', 'entryusn',
|
excludes = ('memberof', 'entryusn',
|
||||||
'krblastsuccessfulauth',
|
'krblastsuccessfulauth',
|
||||||
@ -440,6 +467,7 @@ class ReplicationManager:
|
|||||||
entry.setValues('nsds5replicaport', str(port))
|
entry.setValues('nsds5replicaport', str(port))
|
||||||
entry.setValues('nsds5replicatimeout', str(TIMEOUT))
|
entry.setValues('nsds5replicatimeout', str(TIMEOUT))
|
||||||
entry.setValues('nsds5replicaroot', self.suffix)
|
entry.setValues('nsds5replicaroot', self.suffix)
|
||||||
|
if master is None:
|
||||||
entry.setValues('nsds5replicaupdateschedule', '0000-2359 0123456')
|
entry.setValues('nsds5replicaupdateschedule', '0000-2359 0123456')
|
||||||
entry.setValues('nsDS5ReplicatedAttributeList',
|
entry.setValues('nsDS5ReplicatedAttributeList',
|
||||||
'(objectclass=*) $ EXCLUDE %s' % " ".join(excludes))
|
'(objectclass=*) $ EXCLUDE %s' % " ".join(excludes))
|
||||||
@ -623,11 +651,11 @@ class ReplicationManager:
|
|||||||
haserror = 1
|
haserror = 1
|
||||||
return haserror
|
return haserror
|
||||||
|
|
||||||
def start_replication(self, conn, hostname=None):
|
def start_replication(self, conn, hostname=None, master=None):
|
||||||
print "Starting replication, please wait until this has completed."
|
print "Starting replication, please wait until this has completed."
|
||||||
if hostname == None:
|
if hostname == None:
|
||||||
hostname = self.conn.host
|
hostname = self.conn.host
|
||||||
cn, dn = self.agreement_dn(hostname)
|
cn, dn = self.agreement_dn(hostname, master)
|
||||||
|
|
||||||
mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]
|
mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]
|
||||||
conn.modify_s(dn, mod)
|
conn.modify_s(dn, mod)
|
||||||
@ -640,10 +668,16 @@ class ReplicationManager:
|
|||||||
self.replica_config(conn, replica_id, repldn)
|
self.replica_config(conn, replica_id, repldn)
|
||||||
self.setup_changelog(conn)
|
self.setup_changelog(conn)
|
||||||
|
|
||||||
def setup_replication(self, r_hostname, r_binddn=None, r_bindpw=None):
|
def setup_replication(self, r_hostname, r_port=389, r_sslport=636, r_binddn=None, r_bindpw=None, starttls=False):
|
||||||
# note - there appears to be a bug in python-ldap - it does not
|
# note - there appears to be a bug in python-ldap - it does not
|
||||||
# allow connections using two different CA certs
|
# allow connections using two different CA certs
|
||||||
r_conn = ipaldap.IPAdmin(r_hostname, port=PORT, cacert=CACERT)
|
if starttls:
|
||||||
|
r_conn = ipaldap.IPAdmin(r_hostname, port=r_port)
|
||||||
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
|
||||||
|
r_conn.start_tls_s()
|
||||||
|
else:
|
||||||
|
r_conn = ipaldap.IPAdmin(r_hostname, port=r_sslport, cacert=CACERT)
|
||||||
|
|
||||||
if r_bindpw:
|
if r_bindpw:
|
||||||
r_conn.do_simple_bind(binddn=r_binddn, bindpw=r_bindpw)
|
r_conn.do_simple_bind(binddn=r_binddn, bindpw=r_bindpw)
|
||||||
else:
|
else:
|
||||||
@ -659,15 +693,17 @@ class ReplicationManager:
|
|||||||
self.basic_replication_setup(r_conn, r_id,
|
self.basic_replication_setup(r_conn, r_id,
|
||||||
self.repl_man_dn, self.repl_man_passwd)
|
self.repl_man_dn, self.repl_man_passwd)
|
||||||
|
|
||||||
self.setup_agreement(r_conn, self.conn.host,
|
self.setup_agreement(r_conn, self.conn.host, port=r_port,
|
||||||
repl_man_dn=self.repl_man_dn,
|
repl_man_dn=self.repl_man_dn,
|
||||||
repl_man_passwd=self.repl_man_passwd)
|
repl_man_passwd=self.repl_man_passwd,
|
||||||
self.setup_agreement(self.conn, r_hostname,
|
master=True)
|
||||||
|
self.setup_agreement(self.conn, r_hostname, port=r_port,
|
||||||
repl_man_dn=self.repl_man_dn,
|
repl_man_dn=self.repl_man_dn,
|
||||||
repl_man_passwd=self.repl_man_passwd)
|
repl_man_passwd=self.repl_man_passwd,
|
||||||
|
master=False)
|
||||||
|
|
||||||
#Finally start replication
|
#Finally start replication
|
||||||
ret = self.start_replication(r_conn)
|
ret = self.start_replication(r_conn, master=True)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
raise RuntimeError("Failed to start replication")
|
raise RuntimeError("Failed to start replication")
|
||||||
|
|
||||||
@ -717,7 +753,7 @@ class ReplicationManager:
|
|||||||
logging.info("Agreement is ready, starting replication . . .")
|
logging.info("Agreement is ready, starting replication . . .")
|
||||||
|
|
||||||
# Add winsync replica to the public DIT
|
# Add winsync replica to the public DIT
|
||||||
dn = 'cn=%s,cn=replicas,cn=ipa,cn=etc,%s' % (ad_dc_name, self.suffix)
|
dn = str(DN('cn=%s,cn=replicas,cn=ipa,cn=etc,%s' % (ad_dc_name, self.suffix)))
|
||||||
entry = ipaldap.Entry(dn)
|
entry = ipaldap.Entry(dn)
|
||||||
entry.setValues("objectclass", ["nsContainer", "ipaConfigObject"])
|
entry.setValues("objectclass", ["nsContainer", "ipaConfigObject"])
|
||||||
entry.setValues("cn", ad_dc_name)
|
entry.setValues("cn", ad_dc_name)
|
||||||
@ -802,6 +838,8 @@ class ReplicationManager:
|
|||||||
|
|
||||||
dn = entry[0].dn
|
dn = entry[0].dn
|
||||||
schedule = entry[0].nsds5replicaupdateschedule
|
schedule = entry[0].nsds5replicaupdateschedule
|
||||||
|
if schedule is None:
|
||||||
|
schedule = '0000-2359 0123456'
|
||||||
|
|
||||||
# On the remote chance of a match. We force a synch to happen right
|
# On the remote chance of a match. We force a synch to happen right
|
||||||
# now by changing the schedule to something else and quickly changing
|
# now by changing the schedule to something else and quickly changing
|
||||||
|
Loading…
Reference in New Issue
Block a user