3 # Copyright (C) Andrew Bartlett 2015, 2018
4 # Copyright (C) Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
5 # Copyright (C) Joe Guo <joeg@catalyst.net.nz>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 from __future__ import print_function
24 from ldb import SCOPE_BASE, LdbError
26 from samba import nttime2unix, dsdb
27 from samba.netcmd import CommandError
28 from samba.samdb import SamDB
31 def get_partition_maps(samdb):
32 """Generate dictionaries mapping short partition names to the
34 base_dn = samdb.domain_dn()
37 "CONFIGURATION": str(samdb.get_config_basedn()),
38 "SCHEMA": "CN=Schema,%s" % samdb.get_config_basedn(),
39 "DNSDOMAIN": "DC=DomainDnsZones,%s" % base_dn,
40 "DNSFOREST": "DC=ForestDnsZones,%s" % base_dn
44 for s, l in short_to_long.items():
47 return short_to_long, long_to_short
50 def get_partition(samdb, part):
51 # Allow people to say "--partition=DOMAIN" rather than
52 # "--partition=DC=blah,DC=..."
54 short_partitions, long_partitions = get_partition_maps(samdb)
55 part = short_partitions.get(part.upper(), part)
56 if part not in long_partitions:
57 raise CommandError("unknown partition %s" % part)
61 def get_utdv(samdb, dn):
62 """This finds the uptodateness vector in the database."""
64 config_dn = samdb.get_config_basedn()
65 for c in dsdb._dsdb_load_udv_v2(samdb, dn):
66 inv_id = str(c.source_dsa_invocation_id)
67 res = samdb.search(base=config_dn,
68 expression=("(&(invocationId=%s)"
69 "(objectClass=nTDSDSA))" % inv_id),
70 attrs=["distinguishedName", "invocationId"])
71 settings_dn = str(res[0]["distinguishedName"][0])
72 prefix, dsa_dn = settings_dn.split(',', 1)
73 if prefix != 'CN=NTDS Settings':
74 raise CommandError("Expected NTDS Settings DN, got %s" %
77 cursors.append((dsa_dn,
80 nttime2unix(c.last_sync_success)))
84 def get_own_cursor(samdb):
85 res = samdb.search(base="",
87 attrs=["highestCommittedUSN"])
88 usn = int(res[0]["highestCommittedUSN"][0])
89 now = int(time.time())
93 def get_utdv_edges(local_kcc, dsas, part_dn, lp, creds):
94 # we talk to each remote and make a matrix of the vectors
99 res = local_kcc.samdb.search(dsa_dn,
101 attrs=["dNSHostName"])
102 ldap_url = "ldap://%s" % res[0]["dNSHostName"][0]
104 samdb = SamDB(url=ldap_url, credentials=creds, lp=lp)
105 cursors = get_utdv(samdb, part_dn)
106 own_usn, own_time = get_own_cursor(samdb)
107 remotes = {dsa_dn: own_usn}
108 for dn, guid, usn, t in cursors:
110 except LdbError as e:
111 print("Could not contact %s (%s)" % (ldap_url, e),
114 utdv_edges[dsa_dn] = remotes