mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
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@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
2403b7c4fd
commit
b62b3da976
@ -101,6 +101,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),
|
||||
@ -170,6 +172,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)
|
||||
@ -184,6 +187,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)
|
||||
|
@ -20,8 +20,12 @@
|
||||
from __future__ import print_function
|
||||
import samba.tests
|
||||
import drs_base
|
||||
from samba.dcerpc import drsuapi
|
||||
from samba import drs_utils
|
||||
import re
|
||||
import json
|
||||
import ldb
|
||||
import random
|
||||
from samba.compat import PY3
|
||||
|
||||
if PY3:
|
||||
@ -158,3 +162,144 @@ 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 _force_all_reps(self, samdb, dc, direction):
|
||||
if direction == 'inbound':
|
||||
info_type = drsuapi.DRSUAPI_DS_REPLICA_INFO_NEIGHBORS
|
||||
elif direction == 'outbound':
|
||||
info_type = drsuapi.DRSUAPI_DS_REPLICA_INFO_REPSTO
|
||||
else:
|
||||
raise ValueError("expected 'inbound' or 'outbound'")
|
||||
|
||||
self._enable_all_repl(dc)
|
||||
lp = self.get_loadparm()
|
||||
creds = self.get_credentials()
|
||||
drsuapi_conn, drsuapi_handle, _ = drs_utils.drsuapi_connect(dc, lp, creds)
|
||||
req1 = drsuapi.DsReplicaGetInfoRequest1()
|
||||
req1.info_type = info_type
|
||||
_, info = drsuapi_conn.DsReplicaGetInfo(drsuapi_handle, 1, req1)
|
||||
for x in info.array:
|
||||
# you might think x.source_dsa_address was the thing, but no.
|
||||
# and we need to filter out RODCs and deleted DCs
|
||||
|
||||
res = []
|
||||
try:
|
||||
res = samdb.search(base=x.source_dsa_obj_dn,
|
||||
scope=ldb.SCOPE_BASE,
|
||||
attrs=['msDS-isRODC', 'isDeleted'],
|
||||
controls=['show_deleted:0'])
|
||||
except ldb.LdbError as e:
|
||||
if e.args[0] != ldb.ERR_NO_SUCH_OBJECT:
|
||||
raise
|
||||
|
||||
if (len(res) == 0 or
|
||||
len(res[0].get('msDS-isRODC', '')) > 0 or
|
||||
res[0]['isDeleted'] == 'TRUE'):
|
||||
continue
|
||||
|
||||
dsa_dn = str(ldb.Dn(samdb, x.source_dsa_obj_dn).parent())
|
||||
res = samdb.search(base=dsa_dn,
|
||||
scope=ldb.SCOPE_BASE,
|
||||
attrs=['dNSHostName'])
|
||||
|
||||
remote = res[0]['dNSHostName'][0]
|
||||
self._enable_all_repl(remote)
|
||||
if direction == 'inbound':
|
||||
src, dest = remote, dc
|
||||
else:
|
||||
src, dest = dc, remote
|
||||
self._net_drs_replicate(dest, src, forced=True)
|
||||
|
||||
def test_samba_tool_showrepl_summary_all_good(self):
|
||||
"""Tests 'samba-tool drs showrepl --summary' command."""
|
||||
# To be sure that all is good we need to force replication
|
||||
# with everyone (because others might have it turned off), and
|
||||
# turn replication on for them in case they suddenly decide to
|
||||
# try again.
|
||||
#
|
||||
# We don't restore them to the non-auto-replication state.
|
||||
samdb1 = self.getSamDB("-H", "ldap://%s" % self.dc1, "-U",
|
||||
self.cmdline_creds)
|
||||
self._enable_all_repl(self.dc1)
|
||||
self._force_all_reps(samdb1, self.dc1, 'inbound')
|
||||
self._force_all_reps(samdb1, self.dc1, 'outbound')
|
||||
|
||||
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")
|
||||
|
||||
# --verbose output is still quiet when all is good.
|
||||
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")
|
||||
|
Loading…
Reference in New Issue
Block a user