The branch, master has been updated via 4d058ca s4/tests: Implement a blackbox test for 'samba-tool drs' command via 099644f s4/tests: Move command line processing into separate method to be reused via 6b15746 s4/tests: Implement BlackboxTestCase.check_output() method via d0867e5 s4/samba-tool/drs: Make use of Command.message() method instead of using 'print' via ad48c70 s4/samba-tool/drs: Move get_dsServiceName function at module level to be re-used via f3db67e s4/samba-tool: 'drs options' command implementation from 6c89bb8 waf Remove debugging hacks left in the top level build
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 4d058ca7c043e7d28385bec21c9e1d7575895625 Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed Feb 9 03:40:52 2011 +0200 s4/tests: Implement a blackbox test for 'samba-tool drs' command Autobuild-User: Kamen Mazdrashki <kame...@samba.org> Autobuild-Date: Wed Feb 9 11:45:30 CET 2011 on sn-devel-104 commit 099644f0a7d16d865a39cfab069ab762dc26377f Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed Feb 9 03:40:17 2011 +0200 s4/tests: Move command line processing into separate method to be reused commit 6b1574636a8493d043795b7f397657846a637f28 Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed Feb 9 03:01:16 2011 +0200 s4/tests: Implement BlackboxTestCase.check_output() method I am going to need this to check if output is OK (kind of) commit d0867e5c6c164fdd927a7e961d60287a8bd7e9f5 Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed Feb 9 03:00:06 2011 +0200 s4/samba-tool/drs: Make use of Command.message() method instead of using 'print' commit ad48c70db61a5e35a85a317c4c0f4a179a2aae77 Author: Kamen Mazdrashki <kame...@samba.org> Date: Mon Feb 7 14:13:06 2011 +0200 s4/samba-tool/drs: Move get_dsServiceName function at module level to be re-used commit f3db67e14fd9ebcf5720e82b125d2939fdc2ac17 Author: Kamen Mazdrashki <kame...@samba.org> Date: Fri Feb 4 04:14:13 2011 +0200 s4/samba-tool: 'drs options' command implementation Current implementation handle only one flag change per call ----------------------------------------------------------------------- Summary of changes: source4/scripting/python/samba/netcmd/drs.py | 152 ++++++++++++++------ source4/scripting/python/samba/tests/__init__.py | 13 ++- .../python/samba/tests/blackbox/samba_tool_drs.py | 100 +++++++++++++ source4/selftest/tests.py | 1 + 4 files changed, 223 insertions(+), 43 deletions(-) create mode 100644 source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py Changeset truncated at 500 lines: diff --git a/source4/scripting/python/samba/netcmd/drs.py b/source4/scripting/python/samba/netcmd/drs.py index 740bd20..7dea9de 100644 --- a/source4/scripting/python/samba/netcmd/drs.py +++ b/source4/scripting/python/samba/netcmd/drs.py @@ -83,6 +83,12 @@ def drs_parse_ntds_dn(ntds_dn): return (site, server) +def get_dsServiceName(samdb): + '''get the NTDS DN from the rootDSE''' + res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) + return res[0]["dsServiceName"][0] + + class cmd_drs_showrepl(Command): """show replication status""" @@ -98,22 +104,18 @@ class cmd_drs_showrepl(Command): def print_neighbour(self, n): '''print one set of neighbour information''' - print("%s" % n.naming_context_dn) + self.message("%s" % n.naming_context_dn) try: (site, server) = drs_parse_ntds_dn(n.source_dsa_obj_dn) - print("\t%s\%s via RPC" % (site, server)) + self.message("\t%s\%s via RPC" % (site, server)) except RuntimeError: - print("\tNTDS DN: %s" % n.source_dsa_obj_dn) - print("\t\tDSA object GUID: %s" % n.source_dsa_obj_guid) - print("\t\tLast attempt @ %s %s" % (nttime2string(n.last_attempt), drs_errmsg(n.result_last_attempt))) - print("\t\t%u consecutive failure(s)." % n.consecutive_sync_failures) - print("\t\tLast success @ %s" % nttime2string(n.last_success)) - print("") - - def get_dsServiceName(ctx): - '''get the NTDS DN from the rootDSE''' - res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) - return res[0]["dsServiceName"][0] + self.message("\tNTDS DN: %s" % n.source_dsa_obj_dn) + self.message("\t\tDSA object GUID: %s" % n.source_dsa_obj_guid) + self.message("\t\tLast attempt @ %s %s" % (nttime2string(n.last_attempt), + drs_errmsg(n.result_last_attempt))) + self.message("\t\t%u consecutive failure(s)." % n.consecutive_sync_failures) + self.message("\t\tLast success @ %s" % nttime2string(n.last_success)) + self.message("") def drsuapi_ReplicaInfo(ctx, info_type): '''call a DsReplicaInfo''' @@ -140,7 +142,7 @@ class cmd_drs_showrepl(Command): samdb_connect(self) # show domain information - ntds_dn = self.get_dsServiceName() + ntds_dn = get_dsServiceName(self.samdb) server_dns = self.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])[0]['dnsHostName'][0] (site, server) = drs_parse_ntds_dn(ntds_dn) @@ -150,18 +152,18 @@ class cmd_drs_showrepl(Command): raise CommandError("Failed to search NTDS DN %s" % ntds_dn) conn = self.samdb.search(base=ntds_dn, expression="(objectClass=nTDSConnection)") - print("%s\\%s" % (site, server)) - print("DSA Options: 0x%08x" % int(attr_default(ntds[0], "options", 0))) - print("DSA object GUID: %s" % self.samdb.schema_format_value("objectGUID", ntds[0]["objectGUID"][0])) - print("DSA invocationId: %s\n" % self.samdb.schema_format_value("objectGUID", ntds[0]["invocationId"][0])) + self.message("%s\\%s" % (site, server)) + self.message("DSA Options: 0x%08x" % int(attr_default(ntds[0], "options", 0))) + self.message("DSA object GUID: %s" % self.samdb.schema_format_value("objectGUID", ntds[0]["objectGUID"][0])) + self.message("DSA invocationId: %s\n" % self.samdb.schema_format_value("objectGUID", ntds[0]["invocationId"][0])) - print("==== INBOUND NEIGHBORS ====\n") + self.message("==== INBOUND NEIGHBORS ====\n") (info_type, info) = self.drsuapi_ReplicaInfo(drsuapi.DRSUAPI_DS_REPLICA_INFO_NEIGHBORS) for n in info.array: self.print_neighbour(n) - print("==== OUTBOUND NEIGHBORS ====\n") + self.message("==== OUTBOUND NEIGHBORS ====\n") (info_type, info) = self.drsuapi_ReplicaInfo(drsuapi.DRSUAPI_DS_REPLICA_INFO_REPSTO) for n in info.array: self.print_neighbour(n) @@ -177,25 +179,25 @@ class cmd_drs_showrepl(Command): 'NTDSCONN_KCC_SITE_FAILOVER_TOPOLOGY', 'NTDSCONN_KCC_REDUNDANT_SERVER_TOPOLOGY'] - print("==== KCC CONNECTION OBJECTS ====\n") + self.message("==== KCC CONNECTION OBJECTS ====\n") for c in conn: - print("Connection --") - print("\tConnection name: %s" % c['name'][0]) - print("\tEnabled : %s" % attr_default(c, 'enabledConnection', 'TRUE')) - print("\tServer DNS name : %s" % server_dns) - print("\tServer DN name : %s" % c['fromServer'][0]) - print("\t\tTransportType: RPC") - print("\t\toptions: 0x%08X" % int(attr_default(c, 'options', 0))) + self.message("Connection --") + self.message("\tConnection name: %s" % c['name'][0]) + self.message("\tEnabled : %s" % attr_default(c, 'enabledConnection', 'TRUE')) + self.message("\tServer DNS name : %s" % server_dns) + self.message("\tServer DN name : %s" % c['fromServer'][0]) + self.message("\t\tTransportType: RPC") + self.message("\t\toptions: 0x%08X" % int(attr_default(c, 'options', 0))) if not 'mS-DS-ReplicatesNCReason' in c: - print("Warning: No NC replicated for Connection!") + self.message("Warning: No NC replicated for Connection!") continue for r in c['mS-DS-ReplicatesNCReason']: a = str(r).split(':') - print("\t\tReplicatesNC: %s" % a[3]) - print("\t\tReason: 0x%08x" % int(a[2])) + self.message("\t\tReplicatesNC: %s" % a[3]) + self.message("\t\tReason: 0x%08x" % int(a[2])) for s in reasons: if getattr(dsdb, s, 0) & int(a[2]): - print("\t\t\t%s" % s) + self.message("\t\t\t%s" % s) class cmd_drs_kcc(Command): @@ -228,7 +230,7 @@ class cmd_drs_kcc(Command): self.drsuapi.DsExecuteKCC(self.drsuapi_handle, 1, req1) except Exception, e: raise CommandError("DsExecuteKCC failed", e) - print("Consistency check on %s successful." % DC) + self.message("Consistency check on %s successful." % DC) @@ -297,7 +299,7 @@ class cmd_drs_replicate(Command): self.drsuapi.DsReplicaSync(self.drsuapi_handle, 1, req1) except Exception, estr: raise CommandError("DsReplicaSync failed", estr) - print("Replicate from %s to %s was successful." % (SOURCE_DC, DEST_DC)) + self.message("Replicate from %s to %s was successful." % (SOURCE_DC, DEST_DC)) @@ -373,30 +375,95 @@ class cmd_drs_bind(Command): ("DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2", "DRS_EXT_LH_BETA2"), ("DRSUAPI_SUPPORTED_EXTENSION_RECYCLE_BIN", "DRS_EXT_RECYCLE_BIN")] - print("Bind to %s succeeded." % DC) - print("Extensions supported:") + self.message("Bind to %s succeeded." % DC) + self.message("Extensions supported:") for (opt, str) in optmap: optval = getattr(drsuapi, opt, 0) if info.info.supported_extensions & optval: yesno = "Yes" else: yesno = "No " - print(" %-60s: %s (%s)" % (opt, yesno, str)) + self.message(" %-60s: %s (%s)" % (opt, yesno, str)) if isinstance(info.info, drsuapi.DsBindInfo48): - print("\nExtended Extensions supported:") + self.message("\nExtended Extensions supported:") for (opt, str) in optmap_ext: optval = getattr(drsuapi, opt, 0) if info.info.supported_extensions_ext & optval: yesno = "Yes" else: yesno = "No " - print(" %-60s: %s (%s)" % (opt, yesno, str)) + self.message(" %-60s: %s (%s)" % (opt, yesno, str)) - print("\nSite GUID: %s" % info.info.site_guid) - print("Repl epoch: %u" % info.info.repl_epoch) + self.message("\nSite GUID: %s" % info.info.site_guid) + self.message("Repl epoch: %u" % info.info.repl_epoch) if isinstance(info.info, drsuapi.DsBindInfo48): - print("Forest GUID: %s" % info.info.config_dn_guid) + self.message("Forest GUID: %s" % info.info.config_dn_guid) + + + +class cmd_drs_options(Command): + """query or change 'options' for NTDS Settings object of a domain controller""" + + synopsis = ("%prog drs options <DC>" + " [--dsa-option={+|-}IS_GC | {+|-}DISABLE_INBOUND_REPL" + " |{+|-}DISABLE_OUTBOUND_REPL | {+|-}DISABLE_NTDSCONN_XLATE]") + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "versionopts": options.VersionOptions, + "credopts": options.CredentialsOptions, + } + + takes_args = ["DC"] + + takes_options = [ + Option("--dsa-option", help="DSA option to enable/disable", type="str"), + ] + + option_map = {"IS_GC": 0x00000001, + "DISABLE_INBOUND_REPL": 0x00000002, + "DISABLE_OUTBOUND_REPL": 0x00000004, + "DISABLE_NTDSCONN_XLATE": 0x00000008} + + def run(self, DC, dsa_option=None, + sambaopts=None, credopts=None, versionopts=None): + + self.lp = sambaopts.get_loadparm() + if DC is None: + DC = common.netcmd_dnsname(self.lp) + self.server = DC + self.creds = credopts.get_credentials(self.lp, fallback_machine=True) + + samdb_connect(self) + + ntds_dn = get_dsServiceName(self.samdb) + res = self.samdb.search(base=ntds_dn, scope=ldb.SCOPE_BASE, attrs=["options"]) + dsa_opts = int(res[0]["options"][0]) + + # print out current DSA options + cur_opts = [x for x in self.option_map if self.option_map[x] & dsa_opts] + self.message("Current DSA options: " + ", ".join(cur_opts)) + + # modify options + if dsa_option: + if dsa_option[:1] not in ("+", "-"): + raise CommandError("Unknown option %s" % dsa_option) + flag = dsa_option[1:] + if flag not in self.option_map.keys(): + raise CommandError("Unknown option %s" % dsa_option) + if dsa_option[:1] == "+": + dsa_opts |= self.option_map[flag] + else: + dsa_opts &= ~self.option_map[flag] + #save new options + m = ldb.Message() + m.dn = ldb.Dn(self.samdb, ntds_dn) + m["options"]= ldb.MessageElement(str(dsa_opts), ldb.FLAG_MOD_REPLACE, "options") + self.samdb.modify(m) + # print out new DSA options + cur_opts = [x for x in self.option_map if self.option_map[x] & dsa_opts] + self.message("New DSA options: " + ", ".join(cur_opts)) class cmd_drs(SuperCommand): @@ -407,3 +474,4 @@ class cmd_drs(SuperCommand): subcommands["kcc"] = cmd_drs_kcc() subcommands["replicate"] = cmd_drs_replicate() subcommands["showrepl"] = cmd_drs_showrepl() + subcommands["options"] = cmd_drs_options() diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py index d0362ef..d6b962c 100644 --- a/source4/scripting/python/samba/tests/__init__.py +++ b/source4/scripting/python/samba/tests/__init__.py @@ -124,14 +124,25 @@ class ValidNetbiosNameTests(TestCase): class BlackboxTestCase(TestCase): """Base test case for blackbox tests.""" - def check_run(self, line): + def _make_cmdline(self, line): bindir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../bin")) parts = line.split(" ") if os.path.exists(os.path.join(bindir, parts[0])): parts[0] = os.path.join(bindir, parts[0]) line = " ".join(parts) + return line + + def check_run(self, line): + line = self._make_cmdline(line) subprocess.check_call(line, shell=True) + def check_output(self, line): + line = self._make_cmdline(line) + p = subprocess.Popen(line, stdout=subprocess.PIPE, shell=True, close_fds=True) + retcode = p.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, line) + return p.stdout.read() def connect_samdb(samdb_url, lp=None, session_info=None, credentials=None, flags=0, ldb_options=None, ldap_only=False): diff --git a/source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py b/source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py new file mode 100644 index 0000000..51274cc --- /dev/null +++ b/source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python + +# Blackbox tests for "samba-tool drs" command +# Copyright (C) Kamen Mazdrashki <kame...@samba.org> 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/>. +# + +"""Blackbox tests for samba-tool drs.""" + +import os +import samba.tests + + +class SambaToolDrsTests(samba.tests.BlackboxTestCase): + """Blackbox test case for samba-tool drs.""" + + def setUp(self): + super(SambaToolDrsTests, self).setUp() + + self.dc1 = samba.tests.env_get_var_value("DC1") + self.dc2 = samba.tests.env_get_var_value("DC2") + + creds = self.get_credentials() + self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), + creds.get_username(), creds.get_password()) + + def _get_rootDSE(self, dc): + samdb = samba.tests.connect_samdb(dc, lp=self.get_loadparm(), + credentials=self.get_credentials(), + ldap_only=True) + return samdb.search(base="", scope=samba.tests.ldb.SCOPE_BASE)[0] + + def test_samba_tool_bind(self): + """Tests 'samba-tool drs bind' command + Output should be like: + Extensions supported: + <list-of-supported-extensions> + Site GUID: <GUID> + Repl epoch: 0""" + out = self.check_output("samba-tool drs bind %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue("Site GUID:" in out) + self.assertTrue("Repl epoch:" in out) + + def test_samba_tool_kcc(self): + """Tests 'samba-tool drs kcc' command + Output should be like 'Consistency check on <DC> successful.'""" + out = self.check_output("samba-tool drs kcc %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue("Consistency check on" in out) + self.assertTrue("successful" in out) + + def test_samba_tool_showrepl(self): + """Tests 'samba-tool drs showrepl' command + Output should be like: + <site-name>/<domain-name> + DSA Options: <hex-options> + DSA object GUID: <DSA-object-GUID> + DSA invocationId: <DSA-invocationId> + <Inbound-connections-list> + <Outbound-connections-list> + <KCC-objects> + ... + TODO: Perhaps we should check at least for + DSA's objectGUDI and invocationId""" + out = self.check_output("samba-tool drs showrepl %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue("DSA Options:" in out) + self.assertTrue("DSA object GUID:" in out) + self.assertTrue("DSA invocationId:" in out) + + def test_samba_tool_options(self): + """Tests 'samba-tool drs options' command + Output should be like 'Current DSA options: IS_GC <OTHER_FLAGS>'""" + out = self.check_output("samba-tool drs options %s %s" % (self.dc1, + self.cmdline_creds)) + self.assertTrue("Current DSA options:" in out) + + def test_samba_tool_replicate(self): + """Tests 'samba-tool drs replicate' command + Output should be like 'Replicate from <DC-SRC> to <DC-DEST> was successful.'""" + nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] + out = self.check_output("samba-tool drs replicate %s %s %s %s" % (self.dc1, + self.dc2, + nc_name, + self.cmdline_creds)) + self.assertTrue("Replicate from" in out) + self.assertTrue("was successful" in out) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 33524e6..aa7ee5a 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -536,6 +536,7 @@ plantestsuite("samba4.blackbox.spn.py(dc:local)", "dc:local", ["PYTHON=%s" % pyt plantestsuite("samba4.ldap.bind(dc)", "dc", [python, os.path.join(samba4srcdir, "auth/credentials/tests/bind.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"']) # DRS python tests +plansambapythontestsuite("samba4.blackbox.samba-tool.drs(vampire_dc)", "vampire_dc", os.path.join(samba4srcdir, 'scripting/python'), "samba.tests.blackbox.samba_tool_drs", environ={'DC1': '$DC_SERVER', 'DC2': '$VAMPIRE_DC_SERVER'}, extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD']) plansambapythontestsuite("samba4.drs.delete_object.python(vampire_dc)", "vampire_dc", os.path.join(samba4srcdir, 'torture/drs/python'), "delete_object", environ={'DC1': '$DC_SERVER', 'DC2': '$VAMPIRE_DC_SERVER'}, extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD']) plansambapythontestsuite("samba4.drs.fsmo.python(vampire_dc)", "vampire_dc", os.path.join(samba4srcdir, 'torture/drs/python'), "fsmo", environ={'DC1': "$DC_SERVER", 'DC2': "$VAMPIRE_DC_SERVER"}, extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD']) plansambapythontestsuite("samba4.drs.repl_schema.python(vampire_dc)", "vampire_dc", os.path.join(samba4srcdir, 'torture/drs/python'), "repl_schema", environ={'DC1': "$DC_SERVER", 'DC2': '$VAMPIRE_DC_SERVER'}, extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD']) -- Samba Shared Repository