s4-samba-tool: remove unused imports
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / netcmd / dbcheck.py
blob5f75c3ee6cb4ae45fe6280e700045e4e8df9b643
1 #!/usr/bin/env python
3 # Samba4 AD database checker
5 # Copyright (C) Andrew Tridgell 2011
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/>.
21 import ldb, sys
22 import samba.getopt as options
23 from samba.auth import system_session
24 from samba.samdb import SamDB
25 from samba.netcmd import (
26 Command,
27 CommandError,
28 Option
31 def confirm(self, msg):
32 '''confirm an action with the user'''
33 if self.yes:
34 print("%s [YES]" % msg)
35 return True
36 v = raw_input(msg + ' [y/N] ')
37 return v.upper() in ['Y', 'YES']
39 class cmd_dbcheck(Command):
40 """check local AD database for errors"""
41 synopsis = "dbcheck <DN> [options]"
43 takes_optiongroups = {
44 "sambaopts": options.SambaOptions,
45 "versionopts": options.VersionOptions,
46 "credopts": options.CredentialsOptionsDouble,
49 takes_args = ["DN?"]
51 takes_options = [
52 Option("--scope", dest="scope", default="SUB",
53 help="Pass search scope that builds DN list. Options: SUB, ONE, BASE"),
54 Option("--fix", dest="fix", default=False, action='store_true',
55 help='Fix any errors found'),
56 Option("--yes", dest="yes", default=False, action='store_true',
57 help="don't confirm changes, just do them all"),
58 Option("--cross-ncs", dest="cross_ncs", default=False, action='store_true',
59 help="cross naming context boundaries"),
60 Option("-v", "--verbose", dest="verbose", action="store_true", default=False,
61 help="Print more details of checking"),
64 def run(self, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False,
65 scope="SUB", credopts=None, sambaopts=None, versionopts=None):
66 self.lp = sambaopts.get_loadparm()
67 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
69 self.samdb = SamDB(session_info=system_session(), url=None,
70 credentials=self.creds, lp=self.lp)
71 self.verbose = verbose
72 self.fix = fix
73 self.yes = yes
75 scope_map = { "SUB": ldb.SCOPE_SUBTREE, "BASE":ldb.SCOPE_BASE, "ONE":ldb.SCOPE_ONELEVEL }
76 scope = scope.upper()
77 if not scope in scope_map:
78 raise CommandError("Unknown scope %s" % scope)
79 self.search_scope = scope_map[scope]
81 controls = []
82 if cross_ncs:
83 controls.append("search_options:1:2")
85 res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls)
86 print('Checking %u objects' % len(res))
87 error_count = 0
88 for object in res:
89 error_count += self.check_object(object.dn)
90 if error_count != 0 and not self.fix:
91 print("Please use --fix to fix these errors")
92 print('Checked %u objects (%u errors)' % (len(res), error_count))
93 if error_count != 0:
94 sys.exit(1)
96 def empty_attribute(self, dn, attrname):
97 '''fix empty attributes'''
98 print("ERROR: Empty attribute %s in %s" % (attrname, dn))
99 if not self.fix:
100 return
101 if not confirm(self, 'Remove empty attribute %s from %s?' % (attrname, dn)):
102 print("Not fixing empty attribute %s" % attrname)
103 return
105 m = ldb.Message()
106 m.dn = dn
107 m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname)
108 try:
109 self.samdb.modify(m, controls=["relax:0"], validate=False)
110 except Exception, msg:
111 print("Failed to remove empty attribute %s : %s" % (attrname, msg))
112 return
113 print("Removed empty attribute %s" % attrname)
115 def check_object(self, dn):
116 '''check one object'''
117 if self.verbose:
118 print("Checking object %s" % dn)
119 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor'])
120 if len(res) != 1:
121 print("Object %s disappeared during check" % dn)
122 return 1
123 obj = res[0]
124 error_count = 0
125 for attrname in obj:
126 if attrname == 'dn':
127 continue
129 # check for empty attributes
130 for val in obj[attrname]:
131 if val == '':
132 self.empty_attribute(dn, attrname)
133 error_count += 1
134 continue
136 # check for incorrectly normalised attributes
137 for val in obj[attrname]:
138 normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val])
139 if len(normalised) != 1 or normalised[0] != val:
140 self.normalise_mismatch(dn, attrname, obj[attrname])
141 error_count += 1
142 break
143 return error_count
145 def normalise_mismatch(self, dn, attrname, values):
146 '''fix attribute normalisation errors'''
147 print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn))
148 mod_list = []
149 for val in values:
150 normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val])
151 if len(normalised) != 1:
152 print("Unable to normalise value '%s'" % val)
153 mod_list.append((val, ''))
154 elif (normalised[0] != val):
155 print("value '%s' should be '%s'" % (val, normalised[0]))
156 mod_list.append((val, normalised[0]))
157 if not self.fix:
158 return
159 if not self.confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)):
160 print("Not fixing attribute %s" % attrname)
161 return
163 m = ldb.Message()
164 m.dn = dn
165 for i in range(0, len(mod_list)):
166 (val, nval) = mod_list[i]
167 m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
168 if nval != '':
169 m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname)
171 try:
172 self.samdb.modify(m, controls=["relax:0"], validate=False)
173 except Exception, msg:
174 print("Failed to normalise attribute %s : %s" % (attrname, msg))
175 return
176 print("Normalised attribute %s" % attrname)