smbd: Fix crossing automounter mount points
[Samba.git] / python / samba / kcc / ldif_import_export.py
blob41f0fd75778c3e6300a92bcc5b93226183a7c4eb
1 # LDIF helper functions for the samba_kcc tool
3 # Copyright (C) Dave Craft 2011
4 # Copyright (C) Andrew Bartlett 2015
6 # Andrew Bartlett's alleged work performed by his underlings Douglas
7 # Bagnall and Garming Sam.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 import os
24 from samba import Ldb, ldb, read_and_sub_file
25 from samba.auth import system_session
26 from samba.samdb import SamDB, dsdb_Dn
29 class LdifError(Exception):
30 pass
33 def write_search_result(samdb, f, res):
34 for msg in res:
35 lstr = samdb.write_ldif(msg, ldb.CHANGETYPE_NONE)
36 f.write("%s" % lstr)
39 def ldif_to_samdb(dburl, lp, ldif_file, forced_local_dsa=None):
40 """Routine to import all objects and attributes that are relevant
41 to the KCC algorithms from a previously exported LDIF file.
43 The point of this function is to allow a programmer/debugger to
44 import an LDIF file with non-security relevant information that
45 was previously extracted from a DC database. The LDIF file is used
46 to create a temporary abbreviated database. The KCC algorithm can
47 then run against this abbreviated database for debug or test
48 verification that the topology generated is computationally the
49 same between different OSes and algorithms.
51 :param dburl: path to the temporary abbreviated db to create
52 :param ldif_file: path to the ldif file to import
53 """
54 if os.path.exists(dburl):
55 raise LdifError("Specify a database (%s) that doesn't already exist." %
56 dburl)
58 # Use ["modules:"] as we are attempting to build a sam
59 # database as opposed to start it here.
60 tmpdb = Ldb(url=dburl, session_info=system_session(),
61 lp=lp, options=["modules:"])
63 tmpdb.transaction_start()
64 try:
65 data = read_and_sub_file(ldif_file, None)
66 tmpdb.add_ldif(data, None)
67 if forced_local_dsa:
68 tmpdb.modify_ldif("""dn: @ROOTDSE
69 changetype: modify
70 replace: dsServiceName
71 dsServiceName: CN=NTDS Settings,%s
72 """ % forced_local_dsa)
74 tmpdb.add_ldif("""dn: @MODULES
75 @LIST: rootdse,extended_dn_in,extended_dn_out_ldb,objectguid
77 """)
79 except Exception as estr:
80 tmpdb.transaction_cancel()
81 raise LdifError("Failed to import %s: %s" % (ldif_file, estr))
83 tmpdb.transaction_commit()
85 # We have an abbreviated list of options here because we have built
86 # an abbreviated database. We use the rootdse and extended-dn
87 # modules only during this re-open
88 samdb = SamDB(url=dburl, session_info=system_session(), lp=lp)
89 return samdb
92 def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file):
93 """Routine to extract all objects and attributes that are relevant
94 to the KCC algorithms from a DC database.
96 The point of this function is to allow a programmer/debugger to
97 extract an LDIF file with non-security relevant information from
98 a DC database. The LDIF file can then be used to "import" via
99 the import_ldif() function this file into a temporary abbreviated
100 database. The KCC algorithm can then run against this abbreviated
101 database for debug or test verification that the topology generated
102 is computationally the same between different OSes and algorithms.
104 :param dburl: LDAP database URL to extract info from
105 :param ldif_file: output LDIF file name to create
107 try:
108 samdb = SamDB(url=dburl,
109 session_info=system_session(),
110 credentials=creds, lp=lp)
111 except ldb.LdbError as e:
112 (enum, estr) = e.args
113 raise LdifError("Unable to open sam database (%s) : %s" %
114 (dburl, estr))
116 if os.path.exists(ldif_file):
117 raise LdifError("Specify a file (%s) that doesn't already exist." %
118 ldif_file)
120 try:
121 f = open(ldif_file, "w")
122 except IOError as ioerr:
123 raise LdifError("Unable to open (%s) : %s" % (ldif_file, str(ioerr)))
125 try:
126 # Query Partitions
127 attrs = ["objectClass",
128 "objectGUID",
129 "cn",
130 "whenChanged",
131 "objectSid",
132 "Enabled",
133 "systemFlags",
134 "dnsRoot",
135 "nCName",
136 "msDS-NC-Replica-Locations",
137 "msDS-NC-RO-Replica-Locations"]
139 sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
140 res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
141 attrs=attrs,
142 expression="(objectClass=crossRef)")
144 # Write partitions output
145 write_search_result(samdb, f, res)
147 # Query cross reference container
148 attrs = ["objectClass",
149 "objectGUID",
150 "cn",
151 "whenChanged",
152 "fSMORoleOwner",
153 "systemFlags",
154 "msDS-Behavior-Version",
155 "msDS-EnabledFeature"]
157 sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
158 res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
159 attrs=attrs,
160 expression="(objectClass=crossRefContainer)")
162 # Write cross reference container output
163 write_search_result(samdb, f, res)
165 # Query Sites
166 attrs = ["objectClass",
167 "objectGUID",
168 "cn",
169 "whenChanged",
170 "systemFlags"]
172 sstr = "CN=Sites,%s" % samdb.get_config_basedn()
173 sites = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
174 attrs=attrs,
175 expression="(objectClass=site)")
177 # Write sites output
178 write_search_result(samdb, f, sites)
180 # Query NTDS Site Settings
181 for msg in sites:
182 sitestr = str(msg.dn)
184 attrs = ["objectClass",
185 "objectGUID",
186 "cn",
187 "whenChanged",
188 "interSiteTopologyGenerator",
189 "interSiteTopologyFailover",
190 "schedule",
191 "options"]
193 sstr = "CN=NTDS Site Settings,%s" % sitestr
194 res = samdb.search(base=sstr, scope=ldb.SCOPE_BASE,
195 attrs=attrs)
197 # Write Site Settings output
198 write_search_result(samdb, f, res)
200 # Naming context list
201 nclist = []
203 # Query Directory Service Agents
204 for msg in sites:
205 sstr = str(msg.dn)
207 ncattrs = ["hasMasterNCs",
208 "msDS-hasMasterNCs",
209 "hasPartialReplicaNCs",
210 "msDS-HasDomainNCs",
211 "msDS-hasFullReplicaNCs",
212 "msDS-HasInstantiatedNCs"]
213 attrs = ["objectClass",
214 "objectGUID",
215 "cn",
216 "whenChanged",
217 "invocationID",
218 "options",
219 "msDS-isRODC",
220 "msDS-Behavior-Version"]
222 res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
223 attrs=attrs + ncattrs,
224 expression="(objectClass=nTDSDSA)")
226 # Spin thru all the DSAs looking for NC replicas
227 # and build a list of all possible Naming Contexts
228 # for subsequent retrieval below
229 for res_msg in res:
230 for k in res_msg.keys():
231 if k in ncattrs:
232 for value in res_msg[k]:
233 # Some of these have binary DNs so
234 # use dsdb_Dn to split out relevant parts
235 dsdn = dsdb_Dn(samdb, value.decode('utf8'))
236 dnstr = str(dsdn.dn)
237 if dnstr not in nclist:
238 nclist.append(dnstr)
240 # Write DSA output
241 write_search_result(samdb, f, res)
243 # Query NTDS Connections
244 for msg in sites:
245 sstr = str(msg.dn)
247 attrs = ["objectClass",
248 "objectGUID",
249 "cn",
250 "whenChanged",
251 "options",
252 "whenCreated",
253 "enabledConnection",
254 "schedule",
255 "transportType",
256 "fromServer",
257 "systemFlags"]
259 res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
260 attrs=attrs,
261 expression="(objectClass=nTDSConnection)")
262 # Write NTDS Connection output
263 write_search_result(samdb, f, res)
265 # Query Intersite transports
266 attrs = ["objectClass",
267 "objectGUID",
268 "cn",
269 "whenChanged",
270 "options",
271 "name",
272 "bridgeheadServerListBL",
273 "transportAddressAttribute"]
275 sstr = "CN=Inter-Site Transports,CN=Sites,%s" % \
276 samdb.get_config_basedn()
277 res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
278 attrs=attrs,
279 expression="(objectClass=interSiteTransport)")
281 # Write inter-site transport output
282 write_search_result(samdb, f, res)
284 # Query siteLink
285 attrs = ["objectClass",
286 "objectGUID",
287 "cn",
288 "whenChanged",
289 "systemFlags",
290 "options",
291 "schedule",
292 "replInterval",
293 "siteList",
294 "cost"]
296 sstr = "CN=Sites,%s" % \
297 samdb.get_config_basedn()
298 res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
299 attrs=attrs,
300 expression="(objectClass=siteLink)",
301 controls=['extended_dn:0'])
303 # Write siteLink output
304 write_search_result(samdb, f, res)
306 # Query siteLinkBridge
307 attrs = ["objectClass",
308 "objectGUID",
309 "cn",
310 "whenChanged",
311 "siteLinkList"]
313 sstr = "CN=Sites,%s" % samdb.get_config_basedn()
314 res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
315 attrs=attrs,
316 expression="(objectClass=siteLinkBridge)")
318 # Write siteLinkBridge output
319 write_search_result(samdb, f, res)
321 # Query servers containers
322 # Needed for samdb.server_site_name()
323 attrs = ["objectClass",
324 "objectGUID",
325 "cn",
326 "whenChanged",
327 "systemFlags"]
329 sstr = "CN=Sites,%s" % samdb.get_config_basedn()
330 res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
331 attrs=attrs,
332 expression="(objectClass=serversContainer)")
334 # Write servers container output
335 write_search_result(samdb, f, res)
337 # Query servers
338 # Needed because some transport interfaces refer back to
339 # attributes found in the server object. Also needed
340 # so extended-dn will be happy with dsServiceName in rootDSE
341 attrs = ["objectClass",
342 "objectGUID",
343 "cn",
344 "whenChanged",
345 "systemFlags",
346 "dNSHostName",
347 "mailAddress"]
349 sstr = "CN=Sites,%s" % samdb.get_config_basedn()
350 res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
351 attrs=attrs,
352 expression="(objectClass=server)")
354 # Write server output
355 write_search_result(samdb, f, res)
357 # Query Naming Context replicas
358 attrs = ["objectClass",
359 "objectGUID",
360 "cn",
361 "whenChanged",
362 "objectSid",
363 "fSMORoleOwner",
364 "msDS-Behavior-Version",
365 "repsFrom",
366 "repsTo"]
368 for sstr in nclist:
369 res = samdb.search(sstr, scope=ldb.SCOPE_BASE,
370 attrs=attrs)
372 # Write naming context output
373 write_search_result(samdb, f, res)
375 # Query rootDSE replicas
376 attrs = ["objectClass",
377 "objectGUID",
378 "cn",
379 "whenChanged",
380 "rootDomainNamingContext",
381 "configurationNamingContext",
382 "schemaNamingContext",
383 "defaultNamingContext",
384 "dsServiceName"]
386 sstr = ""
387 res = samdb.search(sstr, scope=ldb.SCOPE_BASE,
388 attrs=attrs)
390 # Record the rootDSE object as a dn as it
391 # would appear in the base ldb file. We have
392 # to save it this way because we are going to
393 # be importing as an abbreviated database.
394 res[0].dn = ldb.Dn(samdb, "@ROOTDSE")
396 # Write rootdse output
397 write_search_result(samdb, f, res)
399 except ldb.LdbError as e1:
400 (enum, estr) = e1.args
401 raise LdifError("Error processing (%s) : %s" % (sstr, estr))
403 f.close()