[PATCH] samba-tool drs showrepl --summary
Douglas Bagnall
douglas.bagnall at catalyst.net.nz
Sat Jun 9 21:08:22 UTC 2018
A while ago I added a --json option to make `samba-tool drs showrepl`
produce machine readable output. This time I'm aiming for *human*
readable output with a --summary option. It differs from what is now
called --classic (still the default) in that it doesn't go on and on
describing things that are perfectly normal. When it finds a problem
it reverts to verbosity in the classic style.
A typical conversation with it should look like this:
$ samba-tool drs showrepl --summary $DC $CREDS
[ALL GOOD]
$
Soon I'll have a patch for `samba-tool drs showrepl --global` which
looks around the network and tries to verify that it is in good
health.
cheers,
Douglas
-------------- next part --------------
From f37952a4861262080dc9fdf2d937e4a7862f4c61 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date: Sat, 9 Jun 2018 20:07:37 +1200
Subject: [PATCH 1/6] python/kcc/graph_utils: short-cut edge failure test
without edges
Otherwise we get an exception because itertools.combinations is asked
to find combinations with negative size.
Instead we assert the graph is connected as-is, which in this case is
the same as asserting there are no vertices.
Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
---
python/samba/kcc/graph_utils.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/python/samba/kcc/graph_utils.py b/python/samba/kcc/graph_utils.py
index 727b342d4bb..65f5ee67207 100644
--- a/python/samba/kcc/graph_utils.py
+++ b/python/samba/kcc/graph_utils.py
@@ -93,6 +93,9 @@ def verify_graph_connected(edges, vertices, edge_vertices):
def verify_graph_connected_under_edge_failures(edges, vertices, edge_vertices):
"""The graph stays connected when any single edge is removed."""
+ if len(edges) == 0:
+ return verify_graph_connected(edges, vertices, edge_vertices)
+
for subset in itertools.combinations(edges, len(edges) - 1):
try:
verify_graph_connected(subset, vertices, edge_vertices)
--
2.11.0
From 50d1927a5e1112ada0717f995e1337b91a08c506 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date: Sat, 9 Jun 2018 20:35:30 +1200
Subject: [PATCH 2/6] samba-tool drs showrepl tests: don't assert existence of
DNS partitions
Because their existence is uncertain immediately after provision,
when these tests will run under some circumstances.
Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
---
source4/torture/drs/python/samba_tool_drs_showrepl.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/source4/torture/drs/python/samba_tool_drs_showrepl.py b/source4/torture/drs/python/samba_tool_drs_showrepl.py
index 93fbc4ef414..90bb0484a27 100644
--- a/source4/torture/drs/python/samba_tool_drs_showrepl.py
+++ b/source4/torture/drs/python/samba_tool_drs_showrepl.py
@@ -90,11 +90,12 @@ class SambaToolDrsShowReplTests(drs_base.DrsBaseTestCase):
r"DSA invocationId: %s" %
(HEX8_RE, GUID_RE, GUID_RE), header)
- for p in ['DC=DomainDnsZones,DC=samba,DC=example,DC=com',
- 'CN=Configuration,DC=samba,DC=example,DC=com',
+ # We don't assert the DomainDnsZones and ForestDnsZones are
+ # there because we don't know that they have been set up yet.
+
+ for p in ['CN=Configuration,DC=samba,DC=example,DC=com',
'DC=samba,DC=example,DC=com',
- 'CN=Schema,CN=Configuration,DC=samba,DC=example,DC=com',
- 'DC=ForestDnsZones,DC=samba,DC=example,DC=com']:
+ 'CN=Schema,CN=Configuration,DC=samba,DC=example,DC=com']:
self.assertRegex(r'%s\n'
r'\tDefault-First-Site-Name\\[A-Z]+ via RPC\n'
r'\t\tDSA object GUID: %s\n'
--
2.11.0
From c6fe397d9cf20df3bc42e4d271e1e0b70278c34d Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date: Fri, 9 Mar 2018 16:16:23 +1300
Subject: [PATCH 3/6] samba-tool drs showrepl: generalise the way output mode
is chosen
We have a couple more output modes coming along, so it makes senses to
untangle .run() into a number of independent sub-methods.
Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
---
python/samba/netcmd/drs.py | 57 ++++++++++++++++++++++++++++++++++------------
1 file changed, 42 insertions(+), 15 deletions(-)
diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py
index 9b3c6050af9..f00a159e616 100644
--- a/python/samba/netcmd/drs.py
+++ b/python/samba/netcmd/drs.py
@@ -23,6 +23,7 @@ import samba.getopt as options
import ldb
import logging
import common
+import json
from samba.auth import system_session
from samba.netcmd import (
@@ -81,8 +82,7 @@ def drs_parse_ntds_dn(ntds_dn):
return (site, server)
-
-
+DEFAULT_SHOWREPL_FORMAT = 'classic'
class cmd_drs_showrepl(Command):
"""Show replication status."""
@@ -96,7 +96,11 @@ class cmd_drs_showrepl(Command):
}
takes_options = [
- Option("--json", help="output in JSON format", action='store_true'),
+ Option("--json", help="replication details in JSON format",
+ dest='format', action='store_const', const='json'),
+ Option("--classic", help="print local replication details",
+ dest='format', action='store_const', const='classic',
+ default=DEFAULT_SHOWREPL_FORMAT),
]
takes_args = ["DC?"]
@@ -148,14 +152,30 @@ class cmd_drs_showrepl(Command):
return (info_type, info)
def run(self, DC=None, sambaopts=None,
- credopts=None, versionopts=None, json=False):
-
+ credopts=None, versionopts=None,
+ format=DEFAULT_SHOWREPL_FORMAT):
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)
+ output_function = {
+ 'json': self.json_output,
+ 'classic': self.classic_output,
+ }.get(format)
+ if output_function is None:
+ raise CommandError("unknown showrepl format %s" % format)
+
+ return output_function()
+
+ def json_output(self):
+ data = self.get_local_repl_data()
+ del data['site']
+ del data['server']
+ json.dump(data, self.outf, indent=2)
+
+ def get_local_repl_data(self):
drsuapi_connect(self)
samdb_connect(self)
@@ -213,16 +233,23 @@ class cmd_drs_showrepl(Command):
a = str(r).split(':')
d['replicates NC'].append((a[3], int(a[2])))
- if json:
- import json as json_mod
- data = {
- 'dsa': dsa_details,
- 'repsFrom': repsfrom,
- 'repsTo': repsto,
- 'NTDSConnections': conn_details
- }
- json_mod.dump(data, self.outf, indent=2)
- return
+ return {
+ 'dsa': dsa_details,
+ 'repsFrom': repsfrom,
+ 'repsTo': repsto,
+ 'NTDSConnections': conn_details,
+ 'site': site,
+ 'server': server
+ }
+
+ def classic_output(self):
+ data = self.get_local_repl_data()
+ dsa_details = data['dsa']
+ repsfrom = data['repsFrom']
+ repsto = data['repsTo']
+ conn_details = data['NTDSConnections']
+ site = data['site']
+ server = data['server']
self.message("%s\\%s" % (site, server))
self.message("DSA Options: 0x%08x" % dsa_details["options"])
--
2.11.0
From e6711c23af2acad4bb42e3026c7f33b2461b7d42 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date: Thu, 7 Jun 2018 14:35:38 +1200
Subject: [PATCH 4/6] samba-tool drs showrepl: add a --verbose flag
Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
---
python/samba/netcmd/drs.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py
index f00a159e616..45ec8f539a3 100644
--- a/python/samba/netcmd/drs.py
+++ b/python/samba/netcmd/drs.py
@@ -101,6 +101,7 @@ class cmd_drs_showrepl(Command):
Option("--classic", help="print local replication details",
dest='format', action='store_const', const='classic',
default=DEFAULT_SHOWREPL_FORMAT),
+ Option("-v", "--verbose", help="Be verbose", action="store_true"),
]
takes_args = ["DC?"]
@@ -153,12 +154,14 @@ class cmd_drs_showrepl(Command):
def run(self, DC=None, sambaopts=None,
credopts=None, versionopts=None,
- format=DEFAULT_SHOWREPL_FORMAT):
+ format=DEFAULT_SHOWREPL_FORMAT,
+ verbose=False):
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)
+ self.verbose = verbose
output_function = {
'json': self.json_output,
--
2.11.0
From f2d2537bd6f7bca17a205fdd87859d24301d0d50 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date: Thu, 7 Jun 2018 14:27:37 +1200
Subject: [PATCH 5/6] samba-tool drs showrepl: add a --color flag
Nothing is using it yet, but the next commit will
Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
---
python/samba/netcmd/drs.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py
index 45ec8f539a3..0cc43106bc2 100644
--- a/python/samba/netcmd/drs.py
+++ b/python/samba/netcmd/drs.py
@@ -38,6 +38,7 @@ from samba.dcerpc import drsuapi, misc
from samba.join import join_clone
from samba.ndr import ndr_unpack
from samba.dcerpc import drsblobs
+from samba import colour
def drsuapi_connect(ctx):
'''make a DRSUAPI connection to the server'''
@@ -102,6 +103,8 @@ class cmd_drs_showrepl(Command):
dest='format', action='store_const', const='classic',
default=DEFAULT_SHOWREPL_FORMAT),
Option("-v", "--verbose", help="Be verbose", action="store_true"),
+ Option("--color", help="Use colour output (yes|no|auto)",
+ default='no'),
]
takes_args = ["DC?"]
@@ -155,7 +158,8 @@ class cmd_drs_showrepl(Command):
def run(self, DC=None, sambaopts=None,
credopts=None, versionopts=None,
format=DEFAULT_SHOWREPL_FORMAT,
- verbose=False):
+ verbose=False, color='no'):
+ self.apply_colour_choice(color)
self.lp = sambaopts.get_loadparm()
if DC is None:
DC = common.netcmd_dnsname(self.lp)
--
2.11.0
From 5ed940a6fe928d07f99227eb22dc94280a42c709 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date: Thu, 7 Jun 2018 14:15:10 +1200
Subject: [PATCH 6/6] samba-tool drs showrepl --summary for a quick local check
The default output ("classic") gives you a lot of very uninteresting
detail when everything is fine. --summary shuts up about things that
are fine but shouts a little bit when things are broken. It doesn't
provide any new information, just tries to present it in a more useful
format.
Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
---
python/samba/netcmd/drs.py | 34 +++++++++
.../torture/drs/python/samba_tool_drs_showrepl.py | 84 ++++++++++++++++++++++
2 files changed, 118 insertions(+)
diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py
index 0cc43106bc2..83cfac8694a 100644
--- a/python/samba/netcmd/drs.py
+++ b/python/samba/netcmd/drs.py
@@ -99,6 +99,8 @@ class cmd_drs_showrepl(Command):
takes_options = [
Option("--json", help="replication details in JSON format",
dest='format', action='store_const', const='json'),
+ Option("--summary", help="summarize local DRS health",
+ dest='format', action='store_const', const='summary'),
Option("--classic", help="print local replication details",
dest='format', action='store_const', const='classic',
default=DEFAULT_SHOWREPL_FORMAT),
@@ -168,6 +170,7 @@ class cmd_drs_showrepl(Command):
self.verbose = verbose
output_function = {
+ 'summary': self.summary_output,
'json': self.json_output,
'classic': self.classic_output,
}.get(format)
@@ -182,6 +185,37 @@ class cmd_drs_showrepl(Command):
del data['server']
json.dump(data, self.outf, indent=2)
+ def summary_output(self):
+ """Print a short message if every seems fine, but print details of any
+ links that seem broken."""
+ failing_repsto = []
+ failing_repsfrom = []
+
+ local_data = self.get_local_repl_data()
+ for rep in local_data['repsTo']:
+ if rep["consecutive failures"] != 0 or rep["last success"] == 0:
+ failing_repsto.append(rep)
+
+ for rep in local_data['repsFrom']:
+ if rep["consecutive failures"] != 0 or rep["last success"] == 0:
+ failing_repsto.append(rep)
+
+ if failing_repsto or failing_repsfrom:
+ self.message(colour.c_RED("There are failing connections"))
+ if failing_repsto:
+ self.message(colour.c_RED("Failing outbound connections:"))
+ for rep in failing_repsto:
+ self.print_neighbour(rep)
+ if failing_repsfrom:
+ self.message(colour.c_RED("Failing inbound connection:"))
+ for rep in failing_repsfrom:
+ self.print_neighbour(rep)
+
+ return 1
+
+ self.message(colour.c_GREEN("[ALL GOOD]"))
+
+
def get_local_repl_data(self):
drsuapi_connect(self)
samdb_connect(self)
diff --git a/source4/torture/drs/python/samba_tool_drs_showrepl.py b/source4/torture/drs/python/samba_tool_drs_showrepl.py
index 90bb0484a27..1c24bd8d322 100644
--- a/source4/torture/drs/python/samba_tool_drs_showrepl.py
+++ b/source4/torture/drs/python/samba_tool_drs_showrepl.py
@@ -22,6 +22,7 @@ import samba.tests
import drs_base
import re
import json
+import random
from samba.compat import PY3
if PY3:
@@ -158,3 +159,86 @@ class SambaToolDrsShowReplTests(drs_base.DrsBaseTestCase):
self.assertTrue(isinstance(n['options'], int))
self.assertTrue(isinstance(n['replicates NC'], list))
self.assertRegex("^%s$" % DN_RE, n["remote DN"])
+
+ def test_samba_tool_showrepl_summary_all_good(self):
+ """Tests 'samba-tool drs showrepl --summary' command.
+ """
+ out = self.check_output("samba-tool drs showrepl --summary %s %s" %
+ (self.dc1, self.cmdline_creds))
+ self.assertStringsEqual(out, "[ALL GOOD]\n")
+
+ out = self.check_output("samba-tool drs showrepl --summary "
+ "--color=yes %s %s" %
+ (self.dc1, self.cmdline_creds))
+ self.assertStringsEqual(out, "\033[1;32m[ALL GOOD]\033[0m\n")
+
+ # ve
+ out = self.check_output("samba-tool drs showrepl --summary -v %s %s" %
+ (self.dc1, self.cmdline_creds))
+ self.assertStringsEqual(out, "[ALL GOOD]\n")
+ out = self.check_output("samba-tool drs showrepl --summary -v "
+ "--color=yes %s %s" %
+ (self.dc1, self.cmdline_creds))
+ self.assertStringsEqual(out, "\033[1;32m[ALL GOOD]\033[0m\n")
+
+ def test_samba_tool_showrepl_summary_forced_failure(self):
+ """Tests 'samba-tool drs showrepl --summary' command when we break the
+ network on purpose.
+ """
+ self.addCleanup(self._enable_all_repl, self.dc1)
+ self._disable_all_repl(self.dc1)
+
+ samdb1 = self.getSamDB("-H", "ldap://%s" % self.dc1, "-U",
+ self.cmdline_creds)
+ samdb2 = self.getSamDB("-H", "ldap://%s" % self.dc2, "-U",
+ self.cmdline_creds)
+ domain_dn = samdb1.domain_dn()
+
+ # Add some things to NOT replicate
+ ou1 = "OU=dc1.%x,%s" % (random.randrange(1 << 64), domain_dn)
+ ou2 = "OU=dc2.%x,%s" % (random.randrange(1 << 64), domain_dn)
+ samdb1.add({
+ "dn": ou1,
+ "objectclass": "organizationalUnit"
+ })
+ self.addCleanup(samdb1.delete, ou1, ['tree_delete:1'])
+ samdb2.add({
+ "dn": ou2,
+ "objectclass": "organizationalUnit"
+ })
+ self.addCleanup(samdb2.delete, ou2, ['tree_delete:1'])
+
+ dn1 = 'cn=u1.%%d,%s' % (ou1)
+ dn2 = 'cn=u2.%%d,%s' % (ou2)
+
+ try:
+ for i in range(100):
+ samdb1.add({
+ "dn": dn1 % i,
+ "objectclass": "user"
+ })
+ samdb2.add({
+ "dn": dn2 % i,
+ "objectclass": "user"
+ })
+ out = self.check_output("samba-tool drs showrepl --summary -v "
+ "%s %s" %
+ (self.dc1, self.cmdline_creds))
+ self.assertStringsEqual('[ALL GOOD]', out, strip=True)
+ out = self.check_output("samba-tool drs showrepl --summary -v "
+ "--color=yes %s %s" %
+ (self.dc2, self.cmdline_creds))
+ self.assertIn('[ALL GOOD]', out)
+
+ except samba.tests.BlackboxProcessError as e:
+ print("Good, failed as expected after %d rounds: %r" % (i, e.cmd))
+ self.assertIn('There are failing connections', e.stdout)
+ self.assertRegexpMatches(e.stdout,
+ r'result 845[67] '
+ r'\(WERR_DS_DRA_(SINK|SOURCE)_DISABLED\)',
+ msg=("The process should have failed "
+ "because replication was forced off, "
+ "but it failed for some other reason."))
+ self.assertIn('consecutive failure(s).', e.stdout)
+ else:
+ self.fail("No DRS failure noticed after 100 rounds of trying")
--
2.11.0
More information about the samba-technical
mailing list