diff options
| author | Douglas Bagnall <douglas.bagnall@catalyst.net.nz> | 2018-06-01 17:20:56 +1200 |
|---|---|---|
| committer | Andrew Bartlett <abartlet@samba.org> | 2018-06-10 19:02:20 +0200 |
| commit | 2d8cc50d392c9434993e2084d4390ce7337cb1b8 (patch) | |
| tree | 08fd1d4a1b4fcc4f4fa9bea7e0ff8fd9e11af152 /python/samba/netcmd | |
| parent | 04a773f30fdb7d03c0526ca1f73353ce5f0d29d5 (diff) | |
| download | samba-2d8cc50d392c9434993e2084d4390ce7337cb1b8.tar.gz samba-2d8cc50d392c9434993e2084d4390ce7337cb1b8.tar.bz2 samba-2d8cc50d392c9434993e2084d4390ce7337cb1b8.zip | |
sambatool visualize: add up-to-dateness visualization
Or more accurately, out-of-dateness visualization, which shows how far
each DCs is from every other using the difference in the up-to-dateness
vectors.
An example usage is
samba-tool visualize uptodateness -r -S -H ldap://somewhere \
-UAdministrator --color=auto --partition=DOMAIN
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'python/samba/netcmd')
| -rw-r--r-- | python/samba/netcmd/visualize.py | 138 |
1 files changed, 137 insertions, 1 deletions
diff --git a/python/samba/netcmd/visualize.py b/python/samba/netcmd/visualize.py index bfd7d3bf341..a24962ea58a 100644 --- a/python/samba/netcmd/visualize.py +++ b/python/samba/netcmd/visualize.py @@ -25,12 +25,14 @@ from collections import defaultdict import subprocess import tempfile -import samba import samba.getopt as options +from samba import dsdb +from samba import nttime2unix from samba.netcmd import Command, SuperCommand, CommandError, Option from samba.samdb import SamDB from samba.graph import dot_graph from samba.graph import distance_matrix, COLOUR_SETS +from samba.graph import full_matrix from ldb import SCOPE_BASE, SCOPE_SUBTREE, LdbError import time import re @@ -672,6 +674,140 @@ class cmd_ntdsconn(GraphCommand): self.write(s, output) +class cmd_uptodateness(GraphCommand): + """visualize uptodateness vectors""" + + takes_options = COMMON_OPTIONS + [ + Option("-p", "--partition", help="restrict to this partition", + default=None), + Option("--max-digits", default=3, type=int, + help="display this many digits of out-of-date-ness"), + ] + + def get_utdv(self, samdb, dn): + """This finds the uptodateness vector in the database.""" + cursors = [] + config_dn = samdb.get_config_basedn() + for c in dsdb._dsdb_load_udv_v2(samdb, dn): + inv_id = str(c.source_dsa_invocation_id) + res = samdb.search(base=config_dn, + expression=("(&(invocationId=%s)" + "(objectClass=nTDSDSA))" % inv_id), + attrs=["distinguishedName", "invocationId"]) + settings_dn = res[0]["distinguishedName"][0] + prefix, dsa_dn = settings_dn.split(',', 1) + if prefix != 'CN=NTDS Settings': + raise CommandError("Expected NTDS Settings DN, got %s" % + settings_dn) + + cursors.append((dsa_dn, + inv_id, + int(c.highest_usn), + nttime2unix(c.last_sync_success))) + return cursors + + def get_own_cursor(self, samdb): + res = samdb.search(base="", + scope=SCOPE_BASE, + attrs=["highestCommittedUSN"]) + usn = int(res[0]["highestCommittedUSN"][0]) + now = int(time.time()) + return (usn, now) + + def run(self, H=None, output=None, shorten_names=False, + key=True, talk_to_remote=False, + sambaopts=None, credopts=None, versionopts=None, + color=None, color_scheme=None, + utf8=False, format=None, importldif=None, + xdot=False, partition=None, max_digits=3): + if not talk_to_remote: + print("this won't work without talking to the remote servers " + "(use -r)", file=self.outf) + return + + # We use the KCC libraries in readonly mode to get the + # replication graph. + lp = sambaopts.get_loadparm() + creds = credopts.get_credentials(lp, fallback_machine=True) + local_kcc, dsas = self.get_kcc_and_dsas(H, lp, creds) + self.samdb = local_kcc.samdb + partition = get_partition(self.samdb, partition) + + short_partitions, long_partitions = get_partition_maps(self.samdb) + color_scheme = self.calc_distance_color_scheme(color, + color_scheme, + output) + + for part_name, part_dn in short_partitions.items(): + if partition not in (part_dn, None): + continue # we aren't doing this partition + + cursors = self.get_utdv(self.samdb, part_dn) + + # we talk to each remote and make a matrix of the vectors + # -- for each partition + # normalise by oldest + utdv_edges = {} + for dsa_dn in dsas: + res = local_kcc.samdb.search(dsa_dn, + scope=SCOPE_BASE, + attrs=["dNSHostName"]) + ldap_url = "ldap://%s" % res[0]["dNSHostName"][0] + try: + samdb = self.get_db(ldap_url, sambaopts, credopts) + cursors = self.get_utdv(samdb, part_dn) + own_usn, own_time = self.get_own_cursor(samdb) + remotes = {dsa_dn: own_usn} + for dn, guid, usn, t in cursors: + remotes[dn] = usn + except LdbError as e: + print("Could not contact %s (%s)" % (ldap_url, e), + file=sys.stderr) + continue + utdv_edges[dsa_dn] = remotes + + distances = {} + max_distance = 0 + for dn1 in dsas: + try: + peak = utdv_edges[dn1][dn1] + except KeyError as e: + peak = 0 + d = {} + distances[dn1] = d + for dn2 in dsas: + if dn2 in utdv_edges: + if dn1 in utdv_edges[dn2]: + dist = peak - utdv_edges[dn2][dn1] + d[dn2] = dist + if dist > max_distance: + max_distance = dist + else: + print("Missing dn %s from UTD vector" % dn1, + file=sys.stderr) + else: + print("missing dn %s from UTD vector list" % dn2, + file=sys.stderr) + + digits = min(max_digits, len(str(max_distance))) + if digits < 1: + digits = 1 + c_scale = 10 ** digits + + s = full_matrix(distances, + utf8=utf8, + colour=color_scheme, + shorten_names=shorten_names, + generate_key=key, + grouping_function=get_dnstr_site, + colour_scale=c_scale, + digits=digits, + ylabel='DC', + xlabel='out-of-date-ness') + + self.write('\n%s\n\n%s' % (part_name, s), output) + + class cmd_visualize(SuperCommand): """Produces graphical representations of Samba network state""" subcommands = {} |
