>From cb545509b0c9cb57c65b5f2692b1e84ddd088fac Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 14 Aug 2015 11:02:01 +1200 Subject: [PATCH 01/32] KCC: ldif tests load tdb://, not ldap:// urls Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/tests/kcc/ldif_import_export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/kcc/ldif_import_export.py b/python/samba/tests/kcc/ldif_import_export.py index 67bcd39..56d2722 100644 --- a/python/samba/tests/kcc/ldif_import_export.py +++ b/python/samba/tests/kcc/ldif_import_export.py @@ -168,7 +168,7 @@ class KCCMultisiteLdifTests(samba.tests.TestCaseInTempDir): tmpdb = os.path.join(self.tempdir, 'verify-tmpdb') my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) - my_kcc.run("ldap://%s" % tmpdb, + my_kcc.run("tdb://%s" % tmpdb, self.lp, self.creds, attempt_live_connections=False) self.remove_files(tmpdb) @@ -180,7 +180,7 @@ class KCCMultisiteLdifTests(samba.tests.TestCaseInTempDir): tmpdb = os.path.join(self.tempdir, 'dotfile-tmpdb') files = [tmpdb] my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) - my_kcc.run("ldap://%s" % tmpdb, + my_kcc.run("tdb://%s" % tmpdb, self.lp, self.creds, attempt_live_connections=False) -- 2.1.4 >From e4c5c28783fedd3407352432ed61168d4957082d Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 17 Jun 2015 15:11:20 +1200 Subject: [PATCH 02/32] KCC: shift common is_generated() check out of branches Signed-off-by: Douglas Bagnall Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 46a25ce..341cf14 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -484,9 +484,10 @@ class KCC(object): # cn!objectGUID < cn2!objectGUID # # Bit NTDSCONN_OPT_RODC_TOPOLOGY is clear in cn!options + if not cn_conn.is_generated(): + continue + if same_site: - if not cn_conn.is_generated(): - continue if self.my_site.is_cleanup_ntdsconn_disabled(): continue @@ -544,9 +545,6 @@ class KCC(object): if not mydsa.is_istg(): continue - if not cn_conn.is_generated(): - continue - # TODO # We are directly using this connection in intersite or # we are using a connection which can supersede this one. -- 2.1.4 >From fcb8c3af2a44de3d6f8b9ed5b67d521e4f661b7f Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 25 Jun 2015 16:38:29 +1200 Subject: [PATCH 03/32] TODO if it works all fine on the fully connected topology we had generated from the old KCC, then mark me as reviewed KCC: pull apart remove_unneeded_ntdsconn(), fixing intersite The confusing big mess was hiding bugs. Firstly, intersite links on non-intersite-topology-generators were not getting looked at. Secondly, the logic around superseding intersite links was missing. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 223 ++++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 110 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 341cf14..3571685 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -432,19 +432,17 @@ class KCC(object): # that became active during this run. pass - def remove_unneeded_ntdsconn(self, all_connected): - """Remove unneeded NTDS Connections once topology is calculated + def _ensure_connections_are_loaded(self, connections): + """Load or fake-load NTDSConnections lacking GUIDs - Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections + New connections don't have GUIDs and created times which are + needed for sorting. If we're in read-only mode, we make fake + GUIDs, otherwise we ask SamDB to do it for us. - :param all_connected: indicates whether all sites are connected + :param connections: an iterable of NTDSConnection objects. :return: None """ - mydsa = self.my_dsa - - # New connections won't have GUIDs which are needed for - # sorting. Add them. - for cn_conn in mydsa.connect_table.values(): + for cn_conn in connections: if cn_conn.guid is None: if self.readonly: cn_conn.guid = misc.GUID(str(uuid.uuid4())) @@ -452,122 +450,108 @@ class KCC(object): else: cn_conn.load_connection(self.samdb) - for cn_conn in mydsa.connect_table.values(): + def _mark_broken_ntdsconn(self): + """Find NTDS Connections that lack a remote + I'm not sure how they appear. Let's be rid of them by marking + them with the to_be_deleted attribute. + + :return: None + """ + for cn_conn in self.my_dsa.connect_table.values(): s_dnstr = cn_conn.get_from_dnstr() if s_dnstr is None: + DEBUG_FN("%s has phantom connection %s" % (self.my_dsa, + cn_conn)) cn_conn.to_be_deleted = True - continue - - #XXX should an RODC be regarded as same site - same_site = s_dnstr in self.my_site.dsa_table - - # Given an nTDSConnection object cn, if the DC with the - # nTDSDSA object dc that is the parent object of cn and - # the DC with the nTDSDA object referenced by cn!fromServer - # are in the same site, the KCC on dc deletes cn if all of - # the following are true: - # - # Bit NTDSCONN_OPT_IS_GENERATED is clear in cn!options. - # - # No site settings object s exists for the local DC's site, or - # bit NTDSSETTINGS_OPT_IS_TOPL_CLEANUP_DISABLED is clear in - # s!options. - # - # Another nTDSConnection object cn2 exists such that cn and - # cn2 have the same parent object, cn!fromServer = cn2!fromServer, - # and either - # - # cn!whenCreated < cn2!whenCreated - # - # cn!whenCreated = cn2!whenCreated and - # cn!objectGUID < cn2!objectGUID - # - # Bit NTDSCONN_OPT_RODC_TOPOLOGY is clear in cn!options - if not cn_conn.is_generated(): - continue - - if same_site: - if self.my_site.is_cleanup_ntdsconn_disabled(): - continue + def _mark_unneeded_local_ntdsconn(self): + """Find unneeded intrasite NTDS Connections for removal - # Loop thru connections looking for a duplicate that - # fulfills the previous criteria - lesser = False - packed_guid = ndr_pack(cn_conn.guid) - for cn2_conn in mydsa.connect_table.values(): - if cn2_conn is cn_conn: - continue + Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections. + Every DC removes its own unnecessary intrasite connections. + This function tags them with the to_be_deleted attribute. - s2_dnstr = cn2_conn.get_from_dnstr() + :return: None + """ + # XXX should an RODC be regarded as same site? It isn't part + # of the intrasite ring. - # If the NTDS Connections has a different - # fromServer field then no match - if s2_dnstr != s_dnstr: - continue + if self.my_site.is_cleanup_ntdsconn_disabled(): + DEBUG_FN("not doing ntdsconn cleanup for site %s, " + "because it is disabled" % self.my_site) + return - lesser = (cn_conn.whenCreated < cn2_conn.whenCreated or - (cn_conn.whenCreated == cn2_conn.whenCreated and - packed_guid < ndr_pack(cn2_conn.guid))) + mydsa = self.my_dsa - if lesser: - break + self._ensure_connections_are_loaded(mydsa.connect_table.values()) - if lesser and not cn_conn.is_rodc_topology(): - cn_conn.to_be_deleted = True + local_connections = [] - # Given an nTDSConnection object cn, if the DC with the nTDSDSA - # object dc that is the parent object of cn and the DC with - # the nTDSDSA object referenced by cn!fromServer are in - # different sites, a KCC acting as an ISTG in dc's site - # deletes cn if all of the following are true: - # - # Bit NTDSCONN_OPT_IS_GENERATED is clear in cn!options. - # - # cn!fromServer references an nTDSDSA object for a DC - # in a site other than the local DC's site. - # - # The keepConnections sequence returned by - # CreateIntersiteConnections() does not contain - # cn!objectGUID, or cn is "superseded by" (see below) - # another nTDSConnection cn2 and keepConnections - # contains cn2!objectGUID. - # - # The return value of CreateIntersiteConnections() - # was true. - # - # Bit NTDSCONN_OPT_RODC_TOPOLOGY is clear in - # cn!options - # - else: # different site + for cn_conn in mydsa.connect_table.values(): + s_dnstr = cn_conn.get_from_dnstr() + if s_dnstr in self.my_site.dsa_table: + removable = not (cn_conn.is_generated() or + cn_conn.is_rodc_topology()) + packed_guid = ndr_pack(cn_conn.guid) + local_connections.append((cn_conn, s_dnstr, + packed_guid, removable)) + + for a, b in itertools.permutations(local_connections, 2): + cn_conn, s_dnstr, packed_guid, removable = a + cn_conn2, s_dnstr2, packed_guid2, removable2 = b + if (removable and + s_dnstr == s_dnstr2 and + cn_conn.whenCreated < cn_conn2.whenCreated or + (cn_conn.whenCreated == cn_conn2.whenCreated and + packed_guid < packed_guid2)): + cn_conn.to_be_deleted = True - if not mydsa.is_istg(): - continue + def _mark_unneeded_intersite_ntdsconn(self): + """find unneeded intersite NTDS Connections for removal - # TODO - # We are directly using this connection in intersite or - # we are using a connection which can supersede this one. - # - # MS-ADTS 6.2.2.4 - Removing Unnecessary Connections does not - # appear to be correct. - # - # 1. cn!fromServer and cn!parent appear inconsistent with - # no cn2 - # 2. The repsFrom do not imply each other - # - if cn_conn in self.kept_connections: # and not_superceded: - continue + Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections. The + intersite topology generator removes links for all DCs in its + site. Here we just tag them with the to_be_deleted attribute. - # This is the result of create_intersite_connections - if not all_connected: - continue + :return: None + """ + # Find the intersite connections + local_dsas = self.my_site.dsa_table + connections_and_dsas = [] + for dsa in local_dsas.values(): + for cn in dsa.connect_table.values(): + s_dnstr = cn.get_from_dnstr() + if s_dnstr not in local_dsas: + from_dsa = self.get_dsa(s_dnstr) + connections_and_dsas.append((cn, dsa, from_dsa)) + + self._ensure_connections_are_loaded(x[0] for x in connections_and_dsas) + for cn, to_dsa, from_dsa in connections_and_dsas: + if not cn.is_generated() or cn.is_rodc_topology(): + continue - if not cn_conn.is_rodc_topology(): - cn_conn.to_be_deleted = True + # If the connection is in the kept_connections list, we + # only remove it if an endpoint seems down. + if (cn in self.kept_connections and + not (self.is_bridgehead_failed(to_dsa, True) or + self.is_bridgehead_failed(from_dsa, True))): + continue - if mydsa.is_ro() or self.readonly: - for connect in mydsa.connect_table.values(): + # this one is broken and might be superseded by another. + # But which other? Let's just say another link to the same + # site can supersede. + from_dnstr = from_dsa.dsa_dnstr + for site in self.site_table.values(): + if from_dnstr in site.rw_dsa_table: + for cn2, to_dsa2, from_dsa2 in connections_and_dsas: + if (cn is not cn2 and + from_dsa2 in site.rw_dsa_table): + cn.to_be_deleted = True + + def _commit_changes(self, dsa): + if dsa.is_ro() or self.readonly: + for connect in dsa.connect_table.values(): if connect.to_be_deleted: DEBUG_FN("TO BE DELETED:\n%s" % connect) if connect.to_be_added: @@ -575,10 +559,29 @@ class KCC(object): # Peform deletion from our tables but perform # no database modification - mydsa.commit_connections(self.samdb, ro=True) + dsa.commit_connections(self.samdb, ro=True) else: # Commit any modified connections - mydsa.commit_connections(self.samdb) + dsa.commit_connections(self.samdb) + + def remove_unneeded_ntdsconn(self, all_connected): + """Remove unneeded NTDS Connections once topology is calculated + + Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections + + :param all_connected: indicates whether all sites are connected + :return: None + """ + self._mark_broken_ntdsconn() + self._mark_unneeded_local_ntdsconn() + # if we are not the istg, we're done! + # if we are the istg, but all_connected is False, we also do nothing. + if self.my_dsa.is_istg() and all_connected: + self._mark_unneeded_intersite_ntdsconn() + + for dsa in self.my_site.dsa_table.values(): + self._commit_changes(dsa) + def modify_repsFrom(self, n_rep, t_repsFrom, s_rep, s_dsa, cn_conn): """Update an repsFrom object if required. -- 2.1.4 >From 9d07c0af42349dd5daae887d03d1df68e21a5c83 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 25 Jun 2015 16:48:21 +1200 Subject: [PATCH 04/32] KCC: Share commit wrapper between forget_ntdsconn and intrasite The wrapper is only to create DEBUG output in read-only mode -- normally it amounts to `dsa.commit_connections(self.samdb)`. Signed-off-by: Douglas Bagnall Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 3571685..a3c31fb 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -553,9 +553,11 @@ class KCC(object): if dsa.is_ro() or self.readonly: for connect in dsa.connect_table.values(): if connect.to_be_deleted: - DEBUG_FN("TO BE DELETED:\n%s" % connect) + logger.info("TO BE DELETED:\n%s" % connect) if connect.to_be_added: - DEBUG_FN("TO BE ADDED:\n%s" % connect) + logger.info("TO BE ADDED:\n%s" % connect) + if connect.to_be_modified: + logger.info("TO BE MODIFIED:\n%s" % connect) # Peform deletion from our tables but perform # no database modification @@ -2347,20 +2349,7 @@ class KCC(object): self.construct_intrasite_graph(mysite, mydsa, part, True, False) # don't detect stale - if self.readonly: - # Display any to be added or modified repsFrom - for connect in mydsa.connect_table.values(): - if connect.to_be_deleted: - logger.info("TO BE DELETED:\n%s" % connect) - if connect.to_be_modified: - logger.info("TO BE MODIFIED:\n%s" % connect) - if connect.to_be_added: - debug.DEBUG_GREEN("TO BE ADDED:\n%s" % connect) - - mydsa.commit_connections(self.samdb, ro=True) - else: - # Commit any newly created connections to the samdb - mydsa.commit_connections(self.samdb) + self._commit_changes(mydsa) def list_dsas(self): """Compile a comprehensive list of DSA DNs -- 2.1.4 >From bbac7a65385df40fa6c517b98bc3651f28a79bde Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 25 Jun 2015 17:01:02 +1200 Subject: [PATCH 05/32] KCC: simplify get_dsa_for_implied_replica(), using IP invariant We only do IP transports. Therfore the long list of alternatives... (not n_rep.is_domain() or n_rep.is_partial() or cn_conn.transport_dnstr is None or cn_conn.transport_dnstr.find("CN=IP") == 0) that ends with the equivalant of "is this IP?" always evaluates to True. If we leave it there it will confuse people for ever. Signed-off-by: Douglas Bagnall Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index a3c31fb..039d54a 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -845,13 +845,7 @@ class KCC(object): # replica, cn!transportType has no value, or # cn!transportType has an RDN of CN=IP. # - implied = (not s_rep.is_ro() or n_rep.is_partial()) and \ - (not n_rep.is_domain() or - n_rep.is_partial() or - cn_conn.transport_dnstr is None or - cn_conn.transport_dnstr.find("CN=IP") == 0) - - if implied: + if not s_rep.is_ro() or n_rep.is_partial(): return s_dsa return None -- 2.1.4 >From b3ae012bfd30b9054e8dbf4f2491818781c967a3 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 25 Jun 2015 17:11:07 +1200 Subject: [PATCH 06/32] KCC: stop --forget-intersite-links forgetting local links It will still forget intrasite links on other sites, but that in theory should not matter. It will still break your network, and is only useful for debugging. Signed-off-by: Douglas Bagnall Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 039d54a..9e69b39 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -2507,7 +2507,9 @@ class KCC(object): for dsa in self.my_site.dsa_table.values(): dsa.connect_table = dict((k, v) for k, v in dsa.connect_table.items() - if v.is_rodc_topology()) + if v.is_rodc_topology() or + (v.from_dnstr not in + self.my_site.dsa_table)) self.plot_all_connections('dsa_forgotten_local') if forget_intersite_links: -- 2.1.4 >From 9dc2a80f6d8aa83c324166933e9044155c628215 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 25 Jun 2015 17:32:21 +1200 Subject: [PATCH 07/32] KCC: remove useless comments and simplify get_dsa_for_implied_replica() These comments are a close reflection (or possibly copy/paste) of the spec, but our code here no longer resembles the spec. We end up just glazing over when we see comments and losing track of the flow of code. If you want the spec just look at the spec. Signed-off-by: Douglas Bagnall Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 41 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 9e69b39..015278f 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -800,14 +800,12 @@ class KCC(object): :param cn_conn: NTDS Connection :return: source DSA or None """ - #XXX different conditions for "implies" than MS-ADTS 6.2.2 + # XXX different conditions for "implies" than MS-ADTS 6.2.2 + # preamble. - # NTDS Connection must satisfy all the following criteria - # to imply a repsFrom tuple is needed: - # - # cn!enabledConnection = true. - # cn!options does not contain NTDSCONN_OPT_RODC_TOPOLOGY. - # cn!fromServer references an nTDSDSA object. + # It boils down to: we want an enabled, non-FRS connections to + # a valid remote DSA with a non-RO replica corresponding to + # n_rep. if not cn_conn.is_enabled() or cn_conn.is_rodc_topology(): return None @@ -819,36 +817,15 @@ class KCC(object): if s_dsa is None: return None - # To imply a repsFrom tuple is needed, each of these - # must be True: - # - # An NC replica of the NC "is present" on the DC to - # which the nTDSDSA object referenced by cn!fromServer - # corresponds. - # - # An NC replica of the NC "should be present" on - # the local DC s_rep = s_dsa.get_current_replica(n_rep.nc_dnstr) - if s_rep is None or not s_rep.is_present(): - return None - - # To imply a repsFrom tuple is needed, each of these - # must be True: - # - # The NC replica on the DC referenced by cn!fromServer is - # a writable replica or the NC replica that "should be - # present" on the local DC is a partial replica. - # - # The NC is not a domain NC, the NC replica that - # "should be present" on the local DC is a partial - # replica, cn!transportType has no value, or - # cn!transportType has an RDN of CN=IP. - # - if not s_rep.is_ro() or n_rep.is_partial(): + if (s_rep is not None and + s_rep.is_present() and + (not s_rep.is_ro() or n_rep.is_partial())): return s_dsa return None + def translate_ntdsconn(self, current_dsa=None): """Adjust repsFrom to match NTDSConnections -- 2.1.4 >From ef0dc72a4df600a164bd9166cdcf79c17d40ef28 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 26 Jun 2015 17:39:03 +1200 Subject: [PATCH 08/32] KCC: Use detect_failed in create_connections Without this, dead DCs were treated as live, and could be used in the tree. If they're in the tree they can split the network. Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett --- python/samba/kcc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 015278f..2e44f12 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -1570,7 +1570,7 @@ class KCC(object): for v in graph.vertices: v.color_vertex() - if self.add_transports(v, my_vertex, graph, False): + if self.add_transports(v, my_vertex, graph, detect_failed): found_failed = True # No NC replicas for this NC in the site of the local DC, -- 2.1.4 >From fe09e955482b4d4dd0b879503cbcc13f44e31388 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 10 Jul 2015 17:02:02 +1200 Subject: [PATCH 09/32] KCC: correctly use dsa.new_connection() system_flags argument The dsa.system_flags attribute is important and gets saved in the database, but was never getting altered because we were setting dsa.flags instead. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/kcc_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/samba/kcc/kcc_utils.py b/python/samba/kcc/kcc_utils.py index 3b5b0cc..1dfd6e9 100644 --- a/python/samba/kcc/kcc_utils.py +++ b/python/samba/kcc/kcc_utils.py @@ -769,7 +769,8 @@ class DirectoryServiceAgent(object): '''Debug dump string output of connect table''' return '\n'.join(str(x) for x in self.connect_table) - def new_connection(self, options, flags, transport, from_dnstr, sched): + def new_connection(self, options, system_flags, transport, from_dnstr, + sched): """Set up a new connection for the DSA based on input parameters. Connection will be added to the DSA connect_table and will be marked as "to be added" pending @@ -782,7 +783,7 @@ class DirectoryServiceAgent(object): connect.enabled = True connect.from_dnstr = from_dnstr connect.options = options - connect.flags = flags + connect.system_flags = system_flags if transport is not None: connect.transport_dnstr = transport.dnstr -- 2.1.4 >From 18bd21729ac4b2c164d8a7802033fd3009647206 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 10 Jul 2015 17:53:58 +1200 Subject: [PATCH 10/32] KCC: set system flags for new intrasite connections These flags are mandatory for intrasite connections. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 2e44f12..7bf3dc5 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -1464,7 +1464,10 @@ class KCC(object): # cn!options = opt, cn!transportType is a reference to t, # cn!fromServer is a reference to rbh, and cn!schedule = sch DEBUG_FN("new connection, KCC dsa: %s" % self.my_dsa.dsa_dnstr) - cn = lbh.new_connection(opt, 0, transport, + system_flags = (dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME | + dsdb.SYSTEM_FLAG_CONFIG_ALLOW_MOVE) + + cn = lbh.new_connection(opt, system_flags, transport, rbh.dsa_dnstr, link_sched) # Display any added connection -- 2.1.4 >From af0aa16cae4aa71af502b605aee1ea507abc2c28 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 10 Jul 2015 17:44:48 +1200 Subject: [PATCH 11/32] KCC: keep track of IP transport for dsa.new_connection() Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 2 +- python/samba/kcc/kcc_utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 7bf3dc5..28d7de6 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -2207,7 +2207,7 @@ class KCC(object): # points to us that satisfies the KCC criteria if tnode.dsa_dnstr == dc_local.dsa_dnstr: - tnode.add_connections_from_edges(dc_local) + tnode.add_connections_from_edges(dc_local, self.ip_transport) if self.verify or do_dot_files: dot_edges = [] diff --git a/python/samba/kcc/kcc_utils.py b/python/samba/kcc/kcc_utils.py index 1dfd6e9..c99efa1 100644 --- a/python/samba/kcc/kcc_utils.py +++ b/python/samba/kcc/kcc_utils.py @@ -1723,7 +1723,7 @@ class GraphNode(object): for connect in dsa.connect_table.values(): self.add_edge_from(connect.from_dnstr) - def add_connections_from_edges(self, dsa): + def add_connections_from_edges(self, dsa, transport): """For each edge directed to this graph node, ensure there is a corresponding nTDSConnection object in the dsa. """ @@ -1760,7 +1760,7 @@ class GraphNode(object): flags = (dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME | dsdb.SYSTEM_FLAG_CONFIG_ALLOW_MOVE) - dsa.new_connection(opt, flags, None, edge_dnstr, None) + dsa.new_connection(opt, flags, transport, edge_dnstr, None) def has_sufficient_edges(self): '''Return True if we have met the maximum "from edges" criteria''' -- 2.1.4 >From 5d80a96d17466801653428aa239382e576dadf75 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 10 Jul 2015 17:46:59 +1200 Subject: [PATCH 12/32] KCC: clarify debugging messages in bridgehead finding code Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 28d7de6..9de9bc3 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -1080,14 +1080,14 @@ class KCC(object): bhs = self.get_all_bridgeheads(site, part, transport, partial_ok, detect_failed) - if len(bhs) == 0: - debug.DEBUG_MAGENTA("get_bridgehead:\n\tsitedn=%s\n\tbhdn=None" % + if not bhs: + debug.DEBUG_MAGENTA("get_bridgehead FAILED:\nsitedn = %s" % site.site_dnstr) return None - else: - debug.DEBUG_GREEN("get_bridgehead:\n\tsitedn=%s\n\tbhdn=%s" % - (site.site_dnstr, bhs[0].dsa_dnstr)) - return bhs[0] + + debug.DEBUG_GREEN("get_bridgehead:\n\tsitedn = %s\n\tbhdn = %s" % + (site.site_dnstr, bhs[0].dsa_dnstr)) + return bhs[0] def get_all_bridgeheads(self, site, part, transport, partial_ok, detect_failed): @@ -1115,7 +1115,6 @@ class KCC(object): "non-IP transport! %r" % (transport.name,)) - DEBUG_FN("get_all_bridgeheads") DEBUG_FN(site.rw_dsa_table) for dsa in site.rw_dsa_table.values(): @@ -1163,7 +1162,7 @@ class KCC(object): DEBUG("bridgehead is failed") continue - DEBUG_FN("get_all_bridgeheads: dsadn=%s" % dsa.dsa_dnstr) + DEBUG_FN("found a bridgehead: %s" % dsa.dsa_dnstr) bhs.append(dsa) # IF bit NTDSSETTINGS_OPT_IS_RAND_BH_SELECTION_DISABLED is set in -- 2.1.4 >From af0171d74897a7e8b9719dfd0110758d33011cf2 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 10 Jul 2015 17:49:05 +1200 Subject: [PATCH 13/32] KCC: better explain our confusion in colour_vertices comment Reviewed-by: Garming Sam Signed-off-by: Douglas Bagnall --- python/samba/kcc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 9de9bc3..c2d3619 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -1501,7 +1501,7 @@ class KCC(object): # here, but using vertex seems to make more sense. That is, # the docs want this: # - #bh = self.get_bridgehead(vertex.site, vertex.part, transport, + #bh = self.get_bridgehead(local_vertex.site, vertex.part, transport, # local_vertex.is_black(), detect_failed) # # TODO WHY????? -- 2.1.4 >From a1bcf527e3c1e57a9e294333ae0f32f6d167450b Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 17 Jul 2015 15:51:11 +1200 Subject: [PATCH 14/32] KCC: fix typo in error path Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index c2d3619..120f001 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -305,7 +305,7 @@ class KCC(object): " it must be RODC.\n" "Let's add it, because my_dsa is special!" "\n(likewise for self.dsa_by_guid)" % - self.my_dsas_dnstr) + self.my_dsa_dnstr) self.dsa_by_dnstr[self.my_dsa_dnstr] = self.my_dsa self.dsa_by_guid[str(self.my_dsa.dsa_guid)] = self.my_dsa -- 2.1.4 >From 721a2488e5ca29187117768fb183be45bd3b5f27 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 17 Jul 2015 15:53:26 +1200 Subject: [PATCH 15/32] KCC: default to not loading new samdb when we already have one This should make things simpler in the --import-ldif case. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 25 +++++++++++++++---------- source4/scripting/bin/samba_kcc | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 120f001..0e37611 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -2348,13 +2348,21 @@ class KCC(object): for dsa in site.dsa_table.values()]) return dsas - def load_samdb(self, dburl, lp, creds): + def load_samdb(self, dburl, lp, creds, force=False): """Load the database using an url, loadparm, and credentials + If force is False, the samdb won't be reloaded if it already + exists. + :param dburl: a database url. :param lp: a loadparm object. :param creds: a Credentials object. + :param force: a boolean indicating whether to overwrite. + """ + if self.samdb is not None and not force: + return + self.samdb = SamDB(url=dburl, session_info=system_session(), credentials=creds, lp=lp) @@ -2411,15 +2419,12 @@ class KCC(object): determine link availability (boolean, default False) :return: 1 on error, 0 otherwise """ - # We may already have a samdb setup if we are - # currently importing an ldif for a test run - if self.samdb is None: - try: - self.load_samdb(dburl, lp, creds) - except ldb.LdbError, (num, msg): - logger.error("Unable to open sam database %s : %s" % - (dburl, msg)) - return 1 + try: + self.load_samdb(dburl, lp, creds, force=False) + except ldb.LdbError, (num, msg): + logger.error("Unable to open sam database %s : %s" % + (dburl, msg)) + return 1 if forced_local_dsa: self.samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index b5476dc..889e47b 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -301,7 +301,7 @@ if opts.importldif: sys.exit(rc) if opts.list_valid_dsas: - kcc.load_samdb(opts.dburl, lp, creds) + kcc.load_samdb(opts.dburl, lp, creds, force=False) print '\n'.join(kcc.list_dsas()) sys.exit() -- 2.1.4 >From f5f728063eaac4d2e641e9408719b42556470644 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 17 Jul 2015 15:56:24 +1200 Subject: [PATCH 16/32] KCC: more debug info when --import-ldif goes badly Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 0e37611..6f97e67 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -261,14 +261,15 @@ class KCC(object): :return: None :raise: KCCError if DSA can't be found """ - dn = ldb.Dn(self.samdb, "" % self.samdb.get_ntds_GUID()) + dn_query = "" % self.samdb.get_ntds_GUID() + dn = ldb.Dn(self.samdb, dn_query) try: res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) except ldb.LdbError, (enum, estr): - logger.warning("Search for %s failed: %s. This typically happens" - " in --importldif mode due to lack of module" - " support.", dn, estr) + logger.warning("Search for dn '%s' [from %s] failed: %s. " + "This typically happens in --importldif mode due " + "to lack of module support.", dn, dn_query, estr) try: # We work around the failure above by looking at the # dsServiceName that was put in the fake rootdse by -- 2.1.4 >From b6e3590fbc3044dd66afdf1ee1850979bb634d4d Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 17 Jul 2015 16:10:39 +1200 Subject: [PATCH 17/32] KCC: Simplify RNG seeding logic, dropping the default value There is no particular justification for the previous default, other than being deterministic makes testing more reliable. The algorithms using randomness do not assume determinism. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- source4/scripting/bin/samba_kcc | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index 889e47b..0caba79 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -51,6 +51,8 @@ import logging from samba.kcc.debug import logger, DEBUG, DEBUG_FN from samba.kcc import KCC +# If DEFAULT_RNG_SEED is None, /dev/urandom or system time is used. +DEFAULT_RNG_SEED = None def test_all_reps_from(lp, creds, unix_now, rng_seed=None): """Run the KCC from all DSAs in read-only mode @@ -86,7 +88,7 @@ def test_all_reps_from(lp, creds, unix_now, rng_seed=None): vertex_colours = [] for dsa_dn in dsas: - if rng_seed: + if rng_seed is not None: random.seed(rng_seed) kcc = KCC(unix_now, readonly=True, verify=opts.verify, debug=opts.debug, @@ -185,7 +187,7 @@ parser.add_option("--dot-file-dir", default=None, parser.add_option("--seed", help="random number seed", - type=int) + type=int, default=DEFAULT_RNG_SEED) parser.add_option("--importldif", help="import topology ldif file", @@ -248,11 +250,7 @@ elif opts.readonly: else: logger.setLevel(logging.WARNING) -# initialize seed from optional input parameter -if opts.seed: - random.seed(opts.seed) -else: - random.seed(0xACE5CA11) +random.seed(opts.seed) if opts.now: for timeformat in ("%Y%m%d%H%M%S%Z", "%Y%m%d%H%M%S"): @@ -277,8 +275,7 @@ if opts.dburl is None: if opts.test_all_reps_from: opts.readonly = True - rng_seed = opts.seed or 0xACE5CA11 - test_all_reps_from(lp, creds, unix_now, rng_seed=rng_seed) + test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=opts.seed) sys.exit() # Instantiate Knowledge Consistency Checker and perform run -- 2.1.4 >From a6fa98d26fb029c5721bd683dc366bf9ddc19e76 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 17 Jul 2015 16:27:19 +1200 Subject: [PATCH 18/32] KCC: shift --test-all-reps-from call to after kcc loading This is in an effort to allow --test-all-reps-from to work with --import-ldif (though so far it doesn't for other reasons). Rather than replicate all the ldif loading logic within test_all_reps_from, we just wait delay the test_all_reps_from() call. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- source4/scripting/bin/samba_kcc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index 0caba79..449edda 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -54,7 +54,7 @@ from samba.kcc import KCC # If DEFAULT_RNG_SEED is None, /dev/urandom or system time is used. DEFAULT_RNG_SEED = None -def test_all_reps_from(lp, creds, unix_now, rng_seed=None): +def test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=None): """Run the KCC from all DSAs in read-only mode The behaviour depends on the global opts variable which contains @@ -69,11 +69,8 @@ def test_all_reps_from(lp, creds, unix_now, rng_seed=None): :return None: """ # This implies readonly and attempt_live_connections - kcc = KCC(unix_now, readonly=True, - verify=opts.verify, debug=opts.debug, - dot_file_dir=opts.dot_file_dir) - kcc.load_samdb(opts.dburl, lp, creds) dsas = kcc.list_dsas() + samdb = kcc.samdb needed_parts = {} current_parts = {} @@ -93,6 +90,7 @@ def test_all_reps_from(lp, creds, unix_now, rng_seed=None): kcc = KCC(unix_now, readonly=True, verify=opts.verify, debug=opts.debug, dot_file_dir=opts.dot_file_dir) + kcc.samdb = samdb kcc.run(opts.dburl, lp, creds, forced_local_dsa=dsa_dn, forget_local_links=opts.forget_local_links, forget_intersite_links=opts.forget_intersite_links, @@ -243,6 +241,9 @@ if opts.list_verify_tests: list_verify_tests() sys.exit(0) +if opts.test_all_reps_from: + opts.readonly = True + if opts.debug: logger.setLevel(logging.DEBUG) elif opts.readonly: @@ -273,16 +274,10 @@ creds = credopts.get_credentials(lp, fallback_machine=True) if opts.dburl is None: opts.dburl = lp.samdb_url() -if opts.test_all_reps_from: - opts.readonly = True - test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=opts.seed) - sys.exit() - # Instantiate Knowledge Consistency Checker and perform run kcc = KCC(unix_now, readonly=opts.readonly, verify=opts.verify, debug=opts.debug, dot_file_dir=opts.dot_file_dir) - if opts.exportldif: rc = kcc.export_ldif(opts.dburl, lp, creds, opts.exportldif) sys.exit(rc) @@ -297,6 +292,11 @@ if opts.importldif: if rc != 0: sys.exit(rc) +if opts.test_all_reps_from: + kcc.load_samdb(opts.dburl, lp, creds, force=False) + test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=opts.seed) + sys.exit() + if opts.list_valid_dsas: kcc.load_samdb(opts.dburl, lp, creds, force=False) print '\n'.join(kcc.list_dsas()) -- 2.1.4 >From a40c56bd466740d4289a889d678e7dd3e4eae879 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 15:55:55 +1200 Subject: [PATCH 19/32] KCC: avoid logging alarming things about exected events Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 6f97e67..458af39 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -165,7 +165,7 @@ class KCC(object): if transport.name == 'IP': self.ip_transport = transport elif transport.name == 'SMTP': - logger.info("Samba KCC is ignoring the obsolete SMTP transport.") + logger.debug("Samba KCC is ignoring the obsolete SMTP transport.") else: logger.warning("Samba KCC does not support the transport called %r." @@ -267,9 +267,9 @@ class KCC(object): res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) except ldb.LdbError, (enum, estr): - logger.warning("Search for dn '%s' [from %s] failed: %s. " - "This typically happens in --importldif mode due " - "to lack of module support.", dn, dn_query, estr) + DEBUG_FN("Search for dn '%s' [from %s] failed: %s. " + "This typically happens in --importldif mode due " + "to lack of module support." % (dn, dn_query, estr)) try: # We work around the failure above by looking at the # dsServiceName that was put in the fake rootdse by -- 2.1.4 >From f1717ebbf3556263be75f03169e7033801e61403 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 15:57:50 +1200 Subject: [PATCH 20/32] KCC: load the object GUID with --import-ldif Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/ldif_import_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/kcc/ldif_import_export.py b/python/samba/kcc/ldif_import_export.py index ab7c7a0..9a05198 100644 --- a/python/samba/kcc/ldif_import_export.py +++ b/python/samba/kcc/ldif_import_export.py @@ -84,7 +84,7 @@ dsServiceName: CN=NTDS Settings,%s # modules only during this re-open samdb = SamDB(url=dburl, session_info=system_session(), lp=lp, options=["modules:rootdse,extended_dn_in," - "extended_dn_out_ldb"]) + "extended_dn_out_ldb,objectguid"]) return samdb -- 2.1.4 >From b9d452a211e538f84789d08b9b76c5d3687ea26a Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 16:49:24 +1200 Subject: [PATCH 21/32] KCC: load samdb before calling kcc.run() kcc.run() is a mega-function that does nearly everything, including loading the database. The --list-valid-dsas and --test-all-reps-from tasks also want to load the database, but not do all that other run() stuff, so it makes sense to pull it out. When the samdb has not been loaded, run() will still load it -- this avoids having to change all the tests. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 21 ++++++++++----------- source4/scripting/bin/samba_kcc | 5 +++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 458af39..d9c47c6 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -2361,12 +2361,14 @@ class KCC(object): :param force: a boolean indicating whether to overwrite. """ - if self.samdb is not None and not force: - return - - self.samdb = SamDB(url=dburl, - session_info=system_session(), - credentials=creds, lp=lp) + if force or self.samdb is None: + try: + self.samdb = SamDB(url=dburl, + session_info=system_session(), + credentials=creds, lp=lp) + except ldb.LdbError, (num, msg): + raise KCCError("Unable to open sam database %s : %s" % + (dburl, msg)) def plot_all_connections(self, basename, verify_properties=()): """Helper function to plot and verify NTDSConnections @@ -2420,12 +2422,9 @@ class KCC(object): determine link availability (boolean, default False) :return: 1 on error, 0 otherwise """ - try: + if self.samdb is None: + DEBUG_FN("samdb is None; let's load it from %s" % (dburl,)) self.load_samdb(dburl, lp, creds, force=False) - except ldb.LdbError, (num, msg): - logger.error("Unable to open sam database %s : %s" % - (dburl, msg)) - return 1 if forced_local_dsa: self.samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index 449edda..b0bb616 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -292,13 +292,14 @@ if opts.importldif: if rc != 0: sys.exit(rc) + +kcc.load_samdb(opts.dburl, lp, creds, force=False) + if opts.test_all_reps_from: - kcc.load_samdb(opts.dburl, lp, creds, force=False) test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=opts.seed) sys.exit() if opts.list_valid_dsas: - kcc.load_samdb(opts.dburl, lp, creds, force=False) print '\n'.join(kcc.list_dsas()) sys.exit() -- 2.1.4 >From 40e8b8a0020be18c99ef4ad7c0e8d42bb27e5e67 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 16:51:19 +1200 Subject: [PATCH 22/32] KCC: remove debug print statements from intrasite and intersite Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index d9c47c6..615a996 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -2136,7 +2136,6 @@ class KCC(object): if not self.get_dsa(x).is_ro()) rw_dot_edges = [(a, b) for a, b in dot_edges if a in rw_dot_vertices and b in rw_dot_vertices] - print rw_dot_edges, rw_dot_vertices rw_verify_properties = ('connected', 'directed_double_ring_or_small') verify_and_dot('intrasite_rw_pre_ntdscon', rw_dot_edges, @@ -2232,7 +2231,6 @@ class KCC(object): if not self.get_dsa(x).is_ro()) rw_dot_edges = [(a, b) for a, b in dot_edges if a in rw_dot_vertices and b in rw_dot_vertices] - print rw_dot_edges, rw_dot_vertices rw_verify_properties = ('connected', 'directed_double_ring_or_small') verify_and_dot('intrasite_rw_post_ntdscon', rw_dot_edges, -- 2.1.4 >From 70120ed39f85301ee5bb6a9a4033f9716e78a6b1 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 16:54:36 +1200 Subject: [PATCH 23/32] KCC: NTDSConnection.load_connection() requires objectGUID If there is no GUID, that is an error, so we raise an exception instead of stepping around it. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/kcc_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/samba/kcc/kcc_utils.py b/python/samba/kcc/kcc_utils.py index c99efa1..e0b7221 100644 --- a/python/samba/kcc/kcc_utils.py +++ b/python/samba/kcc/kcc_utils.py @@ -890,10 +890,13 @@ class NTDSConnection(object): if "systemFlags" in msg: self.system_flags = int(msg["systemFlags"][0]) - if "objectGUID" in msg: + try: self.guid = \ misc.GUID(samdb.schema_format_value("objectGUID", msg["objectGUID"][0])) + except KeyError: + raise KCCError("Unable to find objectGUID in nTDSConnection " + "for (%s)" % (self.dnstr)) if "transportType" in msg: dsdn = dsdb_Dn(samdb, msg["transportType"][0]) -- 2.1.4 >From 57f4150ba35c9948517dcfd27f89b3712529e082 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 16:57:25 +1200 Subject: [PATCH 24/32] KCC: raise KCCError, not Exception, in multiple places "except Exception" lines will still catch them, but more fine-grained control is possible. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/kcc_utils.py | 83 +++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/python/samba/kcc/kcc_utils.py b/python/samba/kcc/kcc_utils.py index e0b7221..a430366 100644 --- a/python/samba/kcc/kcc_utils.py +++ b/python/samba/kcc/kcc_utils.py @@ -84,8 +84,8 @@ class NamingContext(object): scope=ldb.SCOPE_BASE, attrs=attrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find naming context (%s) - (%s)" % - (self.nc_dnstr, estr)) + raise KCCError("Unable to find naming context (%s) - (%s)" % + (self.nc_dnstr, estr)) msg = res[0] if "objectGUID" in msg: self.nc_guid = misc.GUID(samdb.schema_format_value("objectGUID", @@ -314,8 +314,8 @@ class NCReplica(NamingContext): attrs=["repsFrom"]) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find NC for (%s) - (%s)" % - (self.nc_dnstr, estr)) + raise KCCError("Unable to find NC for (%s) - (%s)" % + (self.nc_dnstr, estr)) msg = res[0] @@ -386,8 +386,8 @@ class NCReplica(NamingContext): samdb.modify(m) except ldb.LdbError, estr: - raise Exception("Could not set repsFrom for (%s) - (%s)" % - (self.nc_dnstr, estr)) + raise KCCError("Could not set repsFrom for (%s) - (%s)" % + (self.nc_dnstr, estr)) def load_replUpToDateVector(self, samdb): """Given an NC replica which has been discovered thru the nTDSDSA @@ -402,8 +402,8 @@ class NCReplica(NamingContext): attrs=["replUpToDateVector"]) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find NC for (%s) - (%s)" % - (self.nc_dnstr, estr)) + raise KCCError("Unable to find NC for (%s) - (%s)" % + (self.nc_dnstr, estr)) msg = res[0] @@ -436,8 +436,8 @@ class NCReplica(NamingContext): attrs=["fSMORoleOwner"]) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find NC for (%s) - (%s)" % - (self.nc_dnstr, estr)) + raise KCCError("Unable to find NC for (%s) - (%s)" % + (self.nc_dnstr, estr)) msg = res[0] @@ -568,8 +568,8 @@ class DirectoryServiceAgent(object): attrs=attrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find nTDSDSA for (%s) - (%s)" % - (self.dsa_dnstr, estr)) + raise KCCError("Unable to find nTDSDSA for (%s) - (%s)" % + (self.dsa_dnstr, estr)) msg = res[0] self.dsa_guid = misc.GUID(samdb.schema_format_value("objectGUID", @@ -629,8 +629,8 @@ class DirectoryServiceAgent(object): attrs=ncattrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find nTDSDSA NCs for (%s) - (%s)" % - (self.dsa_dnstr, estr)) + raise KCCError("Unable to find nTDSDSA NCs for (%s) - (%s)" % + (self.dsa_dnstr, estr)) # The table of NCs for the dsa we are searching tmp_table = {} @@ -675,7 +675,7 @@ class DirectoryServiceAgent(object): if rep.is_default(): self.default_dnstr = dnstr else: - raise Exception("No nTDSDSA NCs for (%s)" % self.dsa_dnstr) + raise KCCError("No nTDSDSA NCs for (%s)" % self.dsa_dnstr) # Assign our newly built NC replica table to this dsa self.current_rep_table = tmp_table @@ -697,8 +697,8 @@ class DirectoryServiceAgent(object): expression="(objectClass=nTDSConnection)") except ldb.LdbError, (enum, estr): - raise Exception("Unable to find nTDSConnection for (%s) - (%s)" % - (self.dsa_dnstr, estr)) + raise KCCError("Unable to find nTDSConnection for (%s) - (%s)" % + (self.dsa_dnstr, estr)) for msg in res: dnstr = str(msg.dn) @@ -875,8 +875,8 @@ class NTDSConnection(object): attrs=attrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find nTDSConnection for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Unable to find nTDSConnection for (%s) - (%s)" % + (self.dnstr, estr)) msg = res[0] @@ -925,8 +925,8 @@ class NTDSConnection(object): scope=ldb.SCOPE_BASE, attrs=attrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find transport (%s) - (%s)" % - (tdnstr, estr)) + raise KCCError("Unable to find transport (%s) - (%s)" % + (tdnstr, estr)) if "objectGUID" in res[0]: msg = res[0] @@ -952,8 +952,8 @@ class NTDSConnection(object): try: samdb.delete(self.dnstr) except ldb.LdbError, (enum, estr): - raise Exception("Could not delete nTDSConnection for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Could not delete nTDSConnection for (%s) - (%s)" % + (self.dnstr, estr)) def commit_added(self, samdb, ro=False): """Local helper routine for commit_connections() which @@ -977,11 +977,11 @@ class NTDSConnection(object): except ldb.LdbError, (enum, estr): if enum != ldb.ERR_NO_SUCH_OBJECT: - raise Exception("Unable to search for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Unable to search for (%s) - (%s)" % + (self.dnstr, estr)) if found: - raise Exception("nTDSConnection for (%s) already exists!" % - self.dnstr) + raise KCCError("nTDSConnection for (%s) already exists!" % + self.dnstr) if self.enabled: enablestr = "TRUE" @@ -1021,8 +1021,8 @@ class NTDSConnection(object): try: samdb.add(m) except ldb.LdbError, (enum, estr): - raise Exception("Could not add nTDSConnection for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Could not add nTDSConnection for (%s) - (%s)" % + (self.dnstr, estr)) def commit_modified(self, samdb, ro=False): """Local helper routine for commit_connections() which @@ -1090,8 +1090,8 @@ class NTDSConnection(object): try: samdb.modify(m) except ldb.LdbError, (enum, estr): - raise Exception("Could not modify nTDSConnection for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Could not modify nTDSConnection for (%s) - (%s)" % + (self.dnstr, estr)) def set_modified(self, truefalse): self.to_be_modified = truefalse @@ -1248,9 +1248,8 @@ class Partition(NamingContext): attrs=attrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find partition for (%s) - (%s)" % ( - self.partstr, estr)) - + raise KCCError("Unable to find partition for (%s) - (%s)" % + (self.partstr, estr)) msg = res[0] for k in msg.keys(): if k == "dn": @@ -1389,8 +1388,8 @@ class Site(object): self_res = samdb.search(base=self.site_dnstr, scope=ldb.SCOPE_BASE, attrs=['objectGUID']) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find site settings for (%s) - (%s)" % - (ssdn, estr)) + raise KCCError("Unable to find site settings for (%s) - (%s)" % + (ssdn, estr)) msg = res[0] if "options" in msg: @@ -1420,7 +1419,7 @@ class Site(object): scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)") except ldb.LdbError, (enum, estr): - raise Exception("Unable to find nTDSDSAs - (%s)" % estr) + raise KCCError("Unable to find nTDSDSAs - (%s)" % estr) for msg in res: dnstr = str(msg.dn) @@ -1615,7 +1614,7 @@ class Site(object): samdb.modify(m) except ldb.LdbError, estr: - raise Exception( + raise KCCError( "Could not set interSiteTopologyGenerator for (%s) - (%s)" % (ssdn, estr)) return True @@ -1812,8 +1811,8 @@ class Transport(object): attrs=attrs) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find Transport for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Unable to find Transport for (%s) - (%s)" % + (self.dnstr, estr)) msg = res[0] self.guid = misc.GUID(samdb.schema_format_value("objectGUID", @@ -2072,8 +2071,8 @@ class SiteLink(object): attrs=attrs, controls=['extended_dn:0']) except ldb.LdbError, (enum, estr): - raise Exception("Unable to find SiteLink for (%s) - (%s)" % - (self.dnstr, estr)) + raise KCCError("Unable to find SiteLink for (%s) - (%s)" % + (self.dnstr, estr)) msg = res[0] -- 2.1.4 >From a5cd069da7ec6b0f9042477e533c7d576c50bb5e Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 22 Jul 2015 17:01:02 +1200 Subject: [PATCH 25/32] KCC: Correct capitalisation of KCCError previously we had "raise KccError", which of course would raise a NameError. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/kcc_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/kcc/kcc_utils.py b/python/samba/kcc/kcc_utils.py index a430366..dddc487 100644 --- a/python/samba/kcc/kcc_utils.py +++ b/python/samba/kcc/kcc_utils.py @@ -1047,7 +1047,7 @@ class NTDSConnection(object): if enum == ldb.ERR_NO_SUCH_OBJECT: raise KCCError("nTDSConnection for (%s) doesn't exist!" % self.dnstr) - raise KccError("Unable to search for (%s) - (%s)" % + raise KCCError("Unable to search for (%s) - (%s)" % (self.dnstr, estr)) if self.enabled: -- 2.1.4 >From f7604f3390e55f30481bed9e418772fe05f2c19e Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 14:18:25 +1200 Subject: [PATCH 26/32] KCC: fix pep8 line length in load_ip_transport() You are right to sigh about this one. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 615a996..1d61c0e 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -165,11 +165,12 @@ class KCC(object): if transport.name == 'IP': self.ip_transport = transport elif transport.name == 'SMTP': - logger.debug("Samba KCC is ignoring the obsolete SMTP transport.") + logger.debug("Samba KCC is ignoring the obsolete " + "SMTP transport.") else: - logger.warning("Samba KCC does not support the transport called %r." - % (transport.name,)) + logger.warning("Samba KCC does not support the transport " + "called %r." % (transport.name,)) if self.ip_transport is None: raise KCCError("there doesn't seem to be an IP transport") -- 2.1.4 >From 22be840d6fa8ffd2e1ea1925be46a8f19acac480 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 14:51:33 +1200 Subject: [PATCH 27/32] KCC: whitespace for pep8 Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 2 -- source4/scripting/bin/samba_kcc | 1 - 2 files changed, 3 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 1d61c0e..15e05a9 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -586,7 +586,6 @@ class KCC(object): for dsa in self.my_site.dsa_table.values(): self._commit_changes(dsa) - def modify_repsFrom(self, n_rep, t_repsFrom, s_rep, s_dsa, cn_conn): """Update an repsFrom object if required. @@ -827,7 +826,6 @@ class KCC(object): return s_dsa return None - def translate_ntdsconn(self, current_dsa=None): """Adjust repsFrom to match NTDSConnections diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index b0bb616..108e288 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -46,7 +46,6 @@ from samba import getopt as options from samba.kcc.graph_utils import verify_and_dot, list_verify_tests from samba.kcc.graph_utils import GraphError - import logging from samba.kcc.debug import logger, DEBUG, DEBUG_FN from samba.kcc import KCC -- 2.1.4 >From 5ee00b51fc0ca71f4caaf147c81d7b6670a081b1 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 11:18:40 +1200 Subject: [PATCH 28/32] KCC: remove NTDSConnection API methods that are never used These are not used, and using them would not be considered Pythonic. The flags they alter are always changed directly. The similar set_modified() method IS used. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/kcc_utils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/python/samba/kcc/kcc_utils.py b/python/samba/kcc/kcc_utils.py index dddc487..1e00918 100644 --- a/python/samba/kcc/kcc_utils.py +++ b/python/samba/kcc/kcc_utils.py @@ -1096,12 +1096,6 @@ class NTDSConnection(object): def set_modified(self, truefalse): self.to_be_modified = truefalse - def set_added(self, truefalse): - self.to_be_added = truefalse - - def set_deleted(self, truefalse): - self.to_be_deleted = truefalse - def is_schedule_minimum_once_per_week(self): """Returns True if our schedule includes at least one replication interval within the week. False otherwise -- 2.1.4 >From 6d8cb9b9816be71292322bbc5b806ce9672a0c3e Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 11:50:12 +1200 Subject: [PATCH 29/32] KCC: kcc.import_ldif doesn't need creds Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- python/samba/kcc/__init__.py | 3 +-- python/samba/tests/kcc/ldif_import_export.py | 6 +++--- source4/scripting/bin/samba_kcc | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/python/samba/kcc/__init__.py b/python/samba/kcc/__init__.py index 15e05a9..c3e92b7 100644 --- a/python/samba/kcc/__init__.py +++ b/python/samba/kcc/__init__.py @@ -2582,7 +2582,7 @@ class KCC(object): return 0 - def import_ldif(self, dburl, lp, creds, ldif_file, forced_local_dsa=None): + def import_ldif(self, dburl, lp, ldif_file, forced_local_dsa=None): """Import relevant objects and attributes from an LDIF file. The point of this function is to allow a programmer/debugger to @@ -2595,7 +2595,6 @@ class KCC(object): :param dburl: path to the temporary abbreviated db to create :param lp: a loadparm object. - :param cred: a Credentials object. :param ldif_file: path to the ldif file to import :param forced_local_dsa: perform KCC from this DSA's point of view :return: zero on success, 1 on error diff --git a/python/samba/tests/kcc/ldif_import_export.py b/python/samba/tests/kcc/ldif_import_export.py index 56d2722..392502d 100644 --- a/python/samba/tests/kcc/ldif_import_export.py +++ b/python/samba/tests/kcc/ldif_import_export.py @@ -150,7 +150,7 @@ class KCCMultisiteLdifTests(samba.tests.TestCaseInTempDir): my_kcc = KCC(unix_now, readonly=readonly, verify=verify, dot_file_dir=dot_file_dir) tmpdb = os.path.join(self.tempdir, 'tmpdb') - my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) + my_kcc.import_ldif(tmpdb, self.lp, MULTISITE_LDIF) self.remove_files(tmpdb) return my_kcc @@ -166,7 +166,7 @@ class KCCMultisiteLdifTests(samba.tests.TestCaseInTempDir): """ my_kcc = self._get_kcc('test-verify', verify=True) tmpdb = os.path.join(self.tempdir, 'verify-tmpdb') - my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) + my_kcc.import_ldif(tmpdb, self.lp, MULTISITE_LDIF) my_kcc.run("tdb://%s" % tmpdb, self.lp, self.creds, @@ -179,7 +179,7 @@ class KCCMultisiteLdifTests(samba.tests.TestCaseInTempDir): my_kcc = self._get_kcc('test-dotfiles', dot_file_dir=self.tempdir) tmpdb = os.path.join(self.tempdir, 'dotfile-tmpdb') files = [tmpdb] - my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) + my_kcc.import_ldif(tmpdb, self.lp, MULTISITE_LDIF) my_kcc.run("tdb://%s" % tmpdb, self.lp, self.creds, attempt_live_connections=False) diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index 108e288..02e9556 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -286,7 +286,7 @@ if opts.importldif: logger.error("Specify a target temp database file with --tmpdb option") sys.exit(1) - rc = kcc.import_ldif(opts.tmpdb, lp, creds, opts.importldif, + rc = kcc.import_ldif(opts.tmpdb, lp, opts.importldif, forced_local_dsa=opts.forced_local_dsa) if rc != 0: sys.exit(rc) -- 2.1.4 >From 381d6bda253ca9d7568956c11b0a296b1c8ff35a Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 12:01:09 +1200 Subject: [PATCH 30/32] KCC: with --import-ldif, don't default to standard DB url Before samba_kcc would always assume `-H /usr/local/whatever`, and this interacted badly with the likes of `--test-all-reps-from` and `--forget-intersite-links`. When I say badly, I mean it crashed because the file is absent on my dev machine. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- source4/scripting/bin/samba_kcc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index 02e9556..d22d970 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -271,7 +271,13 @@ lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp, fallback_machine=True) if opts.dburl is None: - opts.dburl = lp.samdb_url() + if opts.importldif: + opts.dburl = opts.tmpdb + else: + opts.dburl = lp.samdb_url() +elif opts.importldif: + logger.error("Don't use -H/--URL with --importldif, use --tmpdb instead") + sys.exit(1) # Instantiate Knowledge Consistency Checker and perform run kcc = KCC(unix_now, readonly=opts.readonly, verify=opts.verify, -- 2.1.4 >From 37a4b9515a1fe7daac31e5e9e5d17c3995d53c46 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 12:07:41 +1200 Subject: [PATCH 31/32] KCC: samba_kcc --tmpdb X won't run if X already exists Part of an ongoing safety campaign, making it harder to overwrite your valuable things while keeping it easy enough to test crazy schemes. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- source4/scripting/bin/samba_kcc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index d22d970..a28b0f1 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -291,6 +291,10 @@ if opts.importldif: if opts.tmpdb is None or opts.tmpdb.startswith('ldap'): logger.error("Specify a target temp database file with --tmpdb option") sys.exit(1) + if os.path.exists(opts.tmpdb): + logger.error("The temp database file (%s) specified with --tmpdb " + "already exists. We refuse to clobber it." % opts.tmpdb) + sys.exit(1) rc = kcc.import_ldif(opts.tmpdb, lp, opts.importldif, forced_local_dsa=opts.forced_local_dsa) -- 2.1.4 >From a441dbc6beec69db279a8b9768c42c7265271a6b Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 23 Jul 2015 12:42:34 +1200 Subject: [PATCH 32/32] KCC: allow --test-all-reps-from to work with --import-ldif The ldif files lack information that a normal database has, which means the ldif import function has to use some trickery to set the local DSA. Once the local DSA is thus set, the fake database is a bit useless from the point of view of other DSAs. We get around this by re-importing it each time. This is doing something slightly different than the normal samdb --test-all-reps-from, in that the changes are not preserved between each DSA's run. With the samdb database (unless using --readonly), the later DSA's will see changes the early ones made. The ordering is arbitrary. Signed-off-by: Douglas Bagnall Reviewed-by: Garming Sam --- source4/scripting/bin/samba_kcc | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc index a28b0f1..05ca55a 100755 --- a/source4/scripting/bin/samba_kcc +++ b/source4/scripting/bin/samba_kcc @@ -53,7 +53,9 @@ from samba.kcc import KCC # If DEFAULT_RNG_SEED is None, /dev/urandom or system time is used. DEFAULT_RNG_SEED = None -def test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=None): + +def test_all_reps_from(kcc, dburl, lp, creds, unix_now, rng_seed=None, + ldif_file=None): """Run the KCC from all DSAs in read-only mode The behaviour depends on the global opts variable which contains @@ -89,8 +91,23 @@ def test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=None): kcc = KCC(unix_now, readonly=True, verify=opts.verify, debug=opts.debug, dot_file_dir=opts.dot_file_dir) - kcc.samdb = samdb - kcc.run(opts.dburl, lp, creds, forced_local_dsa=dsa_dn, + if ldif_file is not None: + try: + # The dburl in this case is a temporary database. + # Its non-existence is ensured at the script startup. + # If it exists, it is from a previous iteration of + # this loop -- unless we're in an unfortunate race. + # Because this database is temporary, it lacks some + # detail and needs to be re-created anew to set the + # local dsa. + os.unlink(dburl) + except OSError: + pass + + kcc.import_ldif(dburl, lp, ldif_file, dsa_dn) + else: + kcc.samdb = samdb + kcc.run(dburl, lp, creds, forced_local_dsa=dsa_dn, forget_local_links=opts.forget_local_links, forget_intersite_links=opts.forget_intersite_links, attempt_live_connections=opts.attempt_live_connections) @@ -305,7 +322,9 @@ if opts.importldif: kcc.load_samdb(opts.dburl, lp, creds, force=False) if opts.test_all_reps_from: - test_all_reps_from(kcc, lp, creds, unix_now, rng_seed=opts.seed) + test_all_reps_from(kcc, opts.dburl, lp, creds, unix_now, + rng_seed=opts.seed, + ldif_file=opts.importldif) sys.exit() if opts.list_valid_dsas: -- 2.1.4