summaryrefslogtreecommitdiff
path: root/source4/scripting/python
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2011-06-24 16:26:23 +1000
committerAndrew Bartlett <abartlet@samba.org>2011-06-24 16:26:23 +1000
commit6da26870e0ae5acd6ff49a30ec2f6886b44d095e (patch)
tree850c71039563c16a5d563c47e7ba2ab645baf198 /source4/scripting/python
parent6925a799d04c6fa59dd2ddef1f5510f9bb7d17d1 (diff)
parent2610c05b5b95cc7036b3d6dfb894c6cfbdb68483 (diff)
downloadsamba-6da26870e0ae5acd6ff49a30ec2f6886b44d095e.tar.gz
samba-6da26870e0ae5acd6ff49a30ec2f6886b44d095e.tar.bz2
samba-6da26870e0ae5acd6ff49a30ec2f6886b44d095e.zip
Merge 2610c05b5b95cc7036b3d6dfb894c6cfbdb68483 as Samba-4.0alpha16samba-4.0.0alpha16
Diffstat (limited to 'source4/scripting/python')
-rw-r--r--source4/scripting/python/pyglue.c44
-rw-r--r--source4/scripting/python/samba/__init__.py7
-rw-r--r--source4/scripting/python/samba/common.py33
-rw-r--r--source4/scripting/python/samba/dbchecker.py317
-rw-r--r--source4/scripting/python/samba/hostconfig.py2
-rw-r--r--source4/scripting/python/samba/idmap.py2
-rw-r--r--source4/scripting/python/samba/join.py11
-rw-r--r--source4/scripting/python/samba/netcmd/__init__.py7
-rw-r--r--source4/scripting/python/samba/netcmd/dbcheck.py104
-rw-r--r--source4/scripting/python/samba/netcmd/drs.py40
-rw-r--r--source4/scripting/python/samba/netcmd/enableaccount.py60
-rw-r--r--source4/scripting/python/samba/netcmd/gpo.py2
-rw-r--r--source4/scripting/python/samba/netcmd/group.py4
-rw-r--r--source4/scripting/python/samba/netcmd/join.py24
-rw-r--r--source4/scripting/python/samba/netcmd/setexpiry.py67
-rw-r--r--source4/scripting/python/samba/netcmd/user.py89
-rw-r--r--source4/scripting/python/samba/provision/__init__.py251
-rw-r--r--source4/scripting/python/samba/samba3.py7
-rw-r--r--source4/scripting/python/samba/samdb.py26
-rw-r--r--source4/scripting/python/samba/tests/samba3sam.py312
-rw-r--r--source4/scripting/python/samba/tests/strings.py104
-rw-r--r--source4/scripting/python/samba/tests/unicodenames.py31
-rw-r--r--source4/scripting/python/samba/tests/upgradeprovisionneeddc.py4
-rwxr-xr-xsource4/scripting/python/samba/upgradehelpers.py166
24 files changed, 1221 insertions, 493 deletions
diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c
index f89785f971f..8a82f3502a5 100644
--- a/source4/scripting/python/pyglue.c
+++ b/source4/scripting/python/pyglue.c
@@ -25,6 +25,10 @@
void init_glue(void);
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
static PyObject *py_generate_random_str(PyObject *self, PyObject *args)
{
int len;
@@ -149,22 +153,22 @@ static PyObject *py_interface_ips(PyObject *self, PyObject *args)
return NULL;
}
- load_interfaces(tmp_ctx, lpcfg_interfaces(lp_ctx), &ifaces);
+ load_interface_list(tmp_ctx, lp_ctx, &ifaces);
- count = iface_count(ifaces);
+ count = iface_list_count(ifaces);
/* first count how many are not loopback addresses */
for (ifcount = i = 0; i<count; i++) {
- const char *ip = iface_n_ip(ifaces, i);
- if (!(!all_interfaces && iface_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
+ const char *ip = iface_list_n_ip(ifaces, i);
+ if (!(!all_interfaces && iface_list_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
ifcount++;
}
}
pylist = PyList_New(ifcount);
for (ifcount = i = 0; i<count; i++) {
- const char *ip = iface_n_ip(ifaces, i);
- if (!(!all_interfaces && iface_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
+ const char *ip = iface_list_n_ip(ifaces, i);
+ if (!(!all_interfaces && iface_list_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
PyList_SetItem(pylist, ifcount, PyString_FromString(ip));
ifcount++;
}
@@ -173,6 +177,30 @@ static PyObject *py_interface_ips(PyObject *self, PyObject *args)
return pylist;
}
+static PyObject *py_strcasecmp_m(PyObject *self, PyObject *args)
+{
+ char *s1, *s2;
+
+ if (!PyArg_ParseTuple(args, "ss", &s1, &s2))
+ return NULL;
+
+ return PyInt_FromLong(strcasecmp_m(s1, s2));
+}
+
+static PyObject *py_strstr_m(PyObject *self, PyObject *args)
+{
+ char *s1, *s2, *ret;
+
+ if (!PyArg_ParseTuple(args, "ss", &s1, &s2))
+ return NULL;
+
+ ret = strstr_m(s1, s2);
+ if (!ret) {
+ Py_RETURN_NONE;
+ }
+ return PyString_FromString(ret);
+}
+
static PyMethodDef py_misc_methods[] = {
{ "generate_random_str", (PyCFunction)py_generate_random_str, METH_VARARGS,
"generate_random_str(len) -> string\n"
@@ -192,6 +220,10 @@ static PyMethodDef py_misc_methods[] = {
"get debug level" },
{ "interface_ips", (PyCFunction)py_interface_ips, METH_VARARGS,
"get interface IP address list"},
+ { "strcasecmp_m", (PyCFunction)py_strcasecmp_m, METH_VARARGS,
+ "(for testing) compare two strings using Samba's strcasecmp_m()"},
+ { "strstr_m", (PyCFunction)py_strstr_m, METH_VARARGS,
+ "(for testing) find one string in another with Samba's strstr_m()"},
{ NULL }
};
diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py
index 2a54f47d2bb..76eb44ce928 100644
--- a/source4/scripting/python/samba/__init__.py
+++ b/source4/scripting/python/samba/__init__.py
@@ -26,6 +26,7 @@ __docformat__ = "restructuredText"
import os
import sys
+import samba.param
def source_tree_topdir():
'''return the top level directory (the one containing the source4 directory)'''
@@ -77,8 +78,8 @@ class Ldb(_Ldb):
if modules_dir is not None:
self.set_modules_dir(modules_dir)
- elif lp is not None:
- self.set_modules_dir(os.path.join(lp.get("modules dir"), "ldb"))
+ else:
+ self.set_modules_dir(os.path.join(samba.param.modules_dir(), "ldb"))
if session_info is not None:
self.set_session_info(session_info)
@@ -348,3 +349,5 @@ nttime2string = _glue.nttime2string
nttime2unix = _glue.nttime2unix
unix2nttime = _glue.unix2nttime
generate_random_password = _glue.generate_random_password
+strcasecmp_m = _glue.strcasecmp_m
+strstr_m = _glue.strstr_m
diff --git a/source4/scripting/python/samba/common.py b/source4/scripting/python/samba/common.py
new file mode 100644
index 00000000000..a2a49627972
--- /dev/null
+++ b/source4/scripting/python/samba/common.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# Samba common functions
+#
+# Copyright (C) Matthieu Patou <mat@matws.net>
+#
+# 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/>.
+#
+
+def confirm(msg, forced = False):
+ """confirm an action with the user
+ :param msg: A string to print to the user
+ :param forced: Are the answer forced
+ """
+ if forced:
+ print("%s [YES]" % msg)
+ return True
+
+ v = raw_input(msg + ' [y/N] ')
+ return v.upper() in ['Y', 'YES']
+
+
diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py
new file mode 100644
index 00000000000..88fd0edf003
--- /dev/null
+++ b/source4/scripting/python/samba/dbchecker.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+#
+# Samba4 AD database checker
+#
+# Copyright (C) Andrew Tridgell 2011
+#
+# 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 ldb
+from samba import dsdb
+from samba import common
+from samba.dcerpc import misc
+
+
+class dsdb_DN(object):
+ '''a class to manipulate DN components'''
+
+ def __init__(self, samdb, dnstring, syntax_oid):
+ if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_STRING_DN ]:
+ colons = dnstring.split(':')
+ if len(colons) < 4:
+ raise Exception("invalid DN prefix")
+ prefix_len = 4 + len(colons[1]) + int(colons[1])
+ self.prefix = dnstring[0:prefix_len]
+ self.dnstring = dnstring[prefix_len:]
+ else:
+ self.dnstring = dnstring
+ self.prefix = ''
+ try:
+ self.dn = ldb.Dn(samdb, self.dnstring)
+ except Exception, msg:
+ print("ERROR: bad DN string '%s'" % self.dnstring)
+ raise
+
+ def __str__(self):
+ return self.prefix + str(self.dn.extended_str(mode=1))
+
+class dbcheck(object):
+ """check a SAM database for errors"""
+
+ def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False, yes=False, quiet=False):
+ self.samdb = samdb
+ self.samdb_schema = (samdb_schema or samdb)
+ self.verbose = verbose
+ self.fix = fix
+ self.yes = yes
+ self.quiet = quiet
+
+ def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[], attrs=['*']):
+ '''perform a database check, returning the number of errors found'''
+
+ res = self.samdb.search(base=DN, scope=scope, attrs=['dn'], controls=controls)
+ self.report('Checking %u objects' % len(res))
+ error_count = 0
+ for object in res:
+ error_count += self.check_object(object.dn, attrs=attrs)
+ if error_count != 0 and not self.fix:
+ self.report("Please use --fix to fix these errors")
+ self.report('Checked %u objects (%u errors)' % (len(res), error_count))
+
+ return error_count
+
+
+ def report(self, msg):
+ '''print a message unless quiet is set'''
+ if not self.quiet:
+ print(msg)
+
+
+ ################################################################
+ # a local confirm function that obeys the --fix and --yes options
+ def confirm(self, msg):
+ '''confirm a change'''
+ if not self.fix:
+ return False
+ if self.quiet:
+ return self.yes
+ return common.confirm(msg, forced=self.yes)
+
+
+ ################################################################
+ # handle empty attributes
+ def err_empty_attribute(self, dn, attrname):
+ '''fix empty attributes'''
+ self.report("ERROR: Empty attribute %s in %s" % (attrname, dn))
+ if not self.confirm('Remove empty attribute %s from %s?' % (attrname, dn)):
+ self.report("Not fixing empty attribute %s" % attrname)
+ return
+
+ m = ldb.Message()
+ m.dn = dn
+ m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname)
+ if self.verbose:
+ self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
+ try:
+ self.samdb.modify(m, controls=["relax:0"], validate=False)
+ except Exception, msg:
+ self.report("Failed to remove empty attribute %s : %s" % (attrname, msg))
+ return
+ self.report("Removed empty attribute %s" % attrname)
+
+
+ ################################################################
+ # handle normalisation mismatches
+ def err_normalise_mismatch(self, dn, attrname, values):
+ '''fix attribute normalisation errors'''
+ self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn))
+ mod_list = []
+ for val in values:
+ normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
+ if len(normalised) != 1:
+ self.report("Unable to normalise value '%s'" % val)
+ mod_list.append((val, ''))
+ elif (normalised[0] != val):
+ self.report("value '%s' should be '%s'" % (val, normalised[0]))
+ mod_list.append((val, normalised[0]))
+ if not self.confirm('Fix normalisation for %s from %s?' % (attrname, dn)):
+ self.report("Not fixing attribute %s" % attrname)
+ return
+
+ m = ldb.Message()
+ m.dn = dn
+ for i in range(0, len(mod_list)):
+ (val, nval) = mod_list[i]
+ m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
+ if nval != '':
+ m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname)
+
+ if self.verbose:
+ self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
+ try:
+ self.samdb.modify(m, controls=["relax:0"], validate=False)
+ except Exception, msg:
+ self.report("Failed to normalise attribute %s : %s" % (attrname, msg))
+ return
+ self.report("Normalised attribute %s" % attrname)
+
+ def is_deleted_objects_dn(self, dsdb_dn):
+ '''see if a dsdb_DN is the special Deleted Objects DN'''
+ return dsdb_dn.prefix == "B:32:18E2EA80684F11D2B9AA00C04F79F805:"
+
+
+ ################################################################
+ # handle a missing GUID extended DN component
+ def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr):
+ self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val))
+ controls=["extended_dn:1:1"]
+ if self.is_deleted_objects_dn(dsdb_dn):
+ controls.append("show_deleted:1")
+ try:
+ res = self.samdb.search(base=str(dsdb_dn.dn), scope=ldb.SCOPE_BASE,
+ attrs=[], controls=controls)
+ except ldb.LdbError, (enum, estr):
+ self.report("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr))
+ return
+ dsdb_dn.dn = res[0].dn
+
+ if not self.confirm('Change DN to %s?' % str(dsdb_dn)):
+ self.report("Not fixing %s" % errstr)
+ return
+ m = ldb.Message()
+ m.dn = dn
+ m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
+ m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
+ if self.verbose:
+ self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
+ try:
+ self.samdb.modify(m)
+ except Exception, msg:
+ self.report("Failed to fix %s on attribute %s : %s" % (errstr, attrname, msg))
+ return
+ self.report("Fixed %s on attribute %s" % (errstr, attrname))
+
+
+ ################################################################
+ # handle a DN pointing to a deleted object
+ def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn):
+ self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
+ self.report("Target GUID points at deleted DN %s" % correct_dn)
+ if not self.confirm('Remove DN?'):
+ self.report("Not removing")
+ return
+ m = ldb.Message()
+ m.dn = dn
+ m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
+ if self.verbose:
+ self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
+ try:
+ self.samdb.modify(m)
+ except Exception, msg:
+ self.report("Failed to remove deleted DN attribute %s : %s" % (attrname, msg))
+ return
+ self.report("Removed deleted DN on attribute %s" % attrname)
+
+
+ ################################################################
+ # handle a DN string being incorrect
+ def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, errstr):
+ self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val))
+ dsdb_dn.dn = correct_dn
+
+ if not self.confirm('Change DN to %s?' % str(dsdb_dn)):
+ self.report("Not fixing %s" % errstr)
+ return
+ m = ldb.Message()
+ m.dn = dn
+ m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
+ m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
+ if self.verbose:
+ self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
+ try:
+ self.samdb.modify(m)
+ except Exception, msg:
+ self.report("Failed to fix incorrect DN string on attribute %s : %s" % (attrname, msg))
+ return
+ self.report("Fixed incorrect DN string on attribute %s" % (attrname))
+
+
+ ################################################################
+ # specialised checking for a dn attribute
+ def check_dn(self, obj, attrname, syntax_oid):
+ '''check a DN attribute for correctness'''
+ error_count = 0
+ for val in obj[attrname]:
+ dsdb_dn = dsdb_DN(self.samdb, val, syntax_oid)
+
+ # all DNs should have a GUID component
+ guid = dsdb_dn.dn.get_extended_component("GUID")
+ if guid is None:
+ error_count += 1
+ self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "missing GUID")
+ continue
+
+ guidstr = str(misc.GUID(guid))
+
+ # check its the right GUID
+ try:
+ res = self.samdb.search(base="<GUID=%s>" % guidstr, scope=ldb.SCOPE_BASE,
+ attrs=['isDeleted'], controls=["extended_dn:1:1", "show_deleted:1"])
+ except ldb.LdbError, (enum, estr):
+ error_count += 1
+ self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "incorrect GUID")
+ continue
+
+ # the target DN might be deleted
+ if ((not self.is_deleted_objects_dn(dsdb_dn)) and
+ 'isDeleted' in res[0] and
+ res[0]['isDeleted'][0].upper() == "TRUE"):
+ # note that we don't check this for the special wellKnownObjects prefix
+ # for Deleted Objects, as we expect that to be deleted
+ error_count += 1
+ self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn)
+ continue
+
+ # check the DN matches in string form
+ if res[0].dn.extended_str() != dsdb_dn.dn.extended_str():
+ error_count += 1
+ self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn,
+ res[0].dn, "incorrect string version of DN")
+ continue
+
+ return error_count
+
+
+
+ ################################################################
+ # check one object - calls to individual error handlers above
+ def check_object(self, dn, attrs=['*']):
+ '''check one object'''
+ if self.verbose:
+ self.report("Checking object %s" % dn)
+ res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=attrs)
+ if len(res) != 1:
+ self.report("Object %s disappeared during check" % dn)
+ return 1
+ obj = res[0]
+ error_count = 0
+ for attrname in obj:
+ if attrname == 'dn':
+ continue
+
+ # check for empty attributes
+ for val in obj[attrname]:
+ if val == '':
+ self.err_empty_attribute(dn, attrname)
+ error_count += 1
+ continue
+
+ # get the syntax oid for the attribute, so we can can have
+ # special handling for some specific attribute types
+ syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
+
+ if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME,
+ dsdb.DSDB_SYNTAX_STRING_DN, ldb.LDB_SYNTAX_DN ]:
+ # it's some form of DN, do specialised checking on those
+ error_count += self.check_dn(obj, attrname, syntax_oid)
+
+ # check for incorrectly normalised attributes
+ for val in obj[attrname]:
+ normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
+ if len(normalised) != 1 or normalised[0] != val:
+ self.err_normalise_mismatch(dn, attrname, obj[attrname])
+ error_count += 1
+ break
+ return error_count
diff --git a/source4/scripting/python/samba/hostconfig.py b/source4/scripting/python/samba/hostconfig.py
index 3e6dc6b1ddd..c50b944c987 100644
--- a/source4/scripting/python/samba/hostconfig.py
+++ b/source4/scripting/python/samba/hostconfig.py
@@ -37,7 +37,7 @@ class Hostconfig(object):
:param session_info: Session info to use
:param credentials: Credentials to access the SamDB with
"""
- return SamDB(url=self.lp.get("sam database"),
+ return SamDB(url=self.lp.samdb_url(),
session_info=session_info, credentials=credentials,
lp=self.lp)
diff --git a/source4/scripting/python/samba/idmap.py b/source4/scripting/python/samba/idmap.py
index 93fca46edd3..9d957341de8 100644
--- a/source4/scripting/python/samba/idmap.py
+++ b/source4/scripting/python/samba/idmap.py
@@ -41,7 +41,7 @@ class IDmapDB(samba.Ldb):
self.lp = lp
if url is None:
- url = lp.get("idmap database")
+ url = lp.private_path("idmap.ldb")
super(IDmapDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
session_info=session_info, credentials=credentials, flags=flags,
diff --git a/source4/scripting/python/samba/join.py b/source4/scripting/python/samba/join.py
index c0aee714070..b586e2cd5b0 100644
--- a/source4/scripting/python/samba/join.py
+++ b/source4/scripting/python/samba/join.py
@@ -36,6 +36,11 @@ import talloc
# this makes debugging easier
talloc.enable_null_tracking()
+class DCJoinException(Exception):
+
+ def __init__(self, msg):
+ super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
+
class dc_join(object):
'''perform a DC join'''
@@ -62,6 +67,12 @@ class dc_join(object):
session_info=system_session(),
credentials=ctx.creds, lp=ctx.lp)
+ try:
+ ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
+ except ldb.LdbError, (enum, estr):
+ raise DCJoinException(estr)
+
+
ctx.myname = netbios_name
ctx.samname = "%s$" % ctx.myname
ctx.base_dn = str(ctx.samdb.get_default_basedn())
diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py
index cf514d5c49d..1373cb289b6 100644
--- a/source4/scripting/python/samba/netcmd/__init__.py
+++ b/source4/scripting/python/samba/netcmd/__init__.py
@@ -2,6 +2,7 @@
# Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009
+# Copyright (C) Theresa Halloran <theresahalloran@gmail.com> 2011
#
# 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
@@ -179,10 +180,6 @@ from samba.netcmd.domainlevel import cmd_domainlevel
commands["domainlevel"] = cmd_domainlevel()
from samba.netcmd.setpassword import cmd_setpassword
commands["setpassword"] = cmd_setpassword()
-from samba.netcmd.setexpiry import cmd_setexpiry
-commands["setexpiry"] = cmd_setexpiry()
-from samba.netcmd.enableaccount import cmd_enableaccount
-commands["enableaccount"] = cmd_enableaccount()
from samba.netcmd.newuser import cmd_newuser
commands["newuser"] = cmd_newuser()
from samba.netcmd.netacl import cmd_acl
@@ -215,3 +212,5 @@ from samba.netcmd.ldapcmp import cmd_ldapcmp
commands["ldapcmp"] = cmd_ldapcmp()
from samba.netcmd.testparm import cmd_testparm
commands["testparm"] = cmd_testparm()
+from samba.netcmd.dbcheck import cmd_dbcheck
+commands["dbcheck"] = cmd_dbcheck()
diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py
new file mode 100644
index 00000000000..3cc50eb814a
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/dbcheck.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+#
+# Samba4 AD database checker
+#
+# Copyright (C) Andrew Tridgell 2011
+#
+# 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 ldb, sys
+import samba.getopt as options
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba.netcmd import (
+ Command,
+ CommandError,
+ Option
+ )
+from samba.dbchecker import dbcheck
+
+
+class cmd_dbcheck(Command):
+ """check local AD database for errors"""
+ synopsis = "dbcheck <DN> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "versionopts": options.VersionOptions,
+ "credopts": options.CredentialsOptionsDouble,
+ }
+
+ takes_args = ["DN?"]
+
+ takes_options = [
+ Option("--scope", dest="scope", default="SUB",
+ help="Pass search scope that builds DN list. Options: SUB, ONE, BASE"),
+ Option("--fix", dest="fix", default=False, action='store_true',
+ help='Fix any errors found'),
+ Option("--yes", dest="yes", default=False, action='store_true',
+ help="don't confirm changes, just do them all as a single transaction"),
+ Option("--cross-ncs", dest="cross_ncs", default=False, action='store_true',
+ help="cross naming context boundaries"),
+ Option("-v", "--verbose", dest="verbose", action="store_true", default=False,
+ help="Print more details of checking"),
+ Option("--quiet", dest="quiet", action="store_true", default=False,
+ help="don't print details of checking"),
+ Option("--attrs", dest="attrs", default=None, help="list of attributes to check (space separated)"),
+ Option("-H", help="LDB URL for database or target server (defaults to local SAM database)", type=str),
+ ]
+
+ def run(self, DN=None, H=None, verbose=False, fix=False, yes=False, cross_ncs=False, quiet=False,
+ scope="SUB", credopts=None, sambaopts=None, versionopts=None, attrs=None):
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp, fallback_machine=True)
+
+ samdb = SamDB(session_info=system_session(), url=H,
+ credentials=creds, lp=lp)
+ if H is None:
+ samdb_schema = samdb
+ else:
+ samdb_schema = SamDB(session_info=system_session(), url=None,
+ credentials=creds, lp=lp)
+
+ scope_map = { "SUB": ldb.SCOPE_SUBTREE, "BASE":ldb.SCOPE_BASE, "ONE":ldb.SCOPE_ONELEVEL }
+ scope = scope.upper()
+ if not scope in scope_map:
+ raise CommandError("Unknown scope %s" % scope)
+ search_scope = scope_map[scope]
+
+ controls = []
+ if H is not None:
+ controls.append('paged_results:1:1000')
+ if cross_ncs:
+ controls.append("search_options:1:2")
+
+ if not attrs:
+ attrs = ['*']
+ else:
+ attrs = attrs.split()
+
+ if yes and fix:
+ samdb.transaction_start()
+
+ chk = dbcheck(samdb, samdb_schema=samdb_schema, verbose=verbose, fix=fix, yes=yes, quiet=quiet)
+ error_count = chk.check_database(DN=DN, scope=search_scope, controls=controls, attrs=attrs)
+
+ if yes and fix:
+ samdb.transaction_commit()
+
+ if error_count != 0:
+ sys.exit(1)
+
diff --git a/source4/scripting/python/samba/netcmd/drs.py b/source4/scripting/python/samba/netcmd/drs.py
index 56c0e39a591..61717a70e98 100644
--- a/source4/scripting/python/samba/netcmd/drs.py
+++ b/source4/scripting/python/samba/netcmd/drs.py
@@ -233,6 +233,39 @@ class cmd_drs_kcc(Command):
self.message("Consistency check on %s successful." % DC)
+def drs_local_replicate(self, SOURCE_DC, NC):
+ '''replicate from a source DC to the local SAM'''
+ self.server = SOURCE_DC
+ drsuapi_connect(self)
+
+ self.local_samdb = SamDB(session_info=system_session(), url=None,
+ credentials=self.creds, lp=self.lp)
+
+ self.samdb = SamDB(url="ldap://%s" % self.server,
+ session_info=system_session(),
+ credentials=self.creds, lp=self.lp)
+
+ # work out the source and destination GUIDs
+ res = self.local_samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
+ self.ntds_dn =