[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Thu Oct 18 08:03:02 UTC 2018


The branch, master has been updated
       via  e404b6a selftest: add tests for samba-tool drs uptodateness
       via  219b52c netcmd/drs: add cmd_drs_uptodateness with json support
       via  f4f5f17 uptodateness: add get_utdv_summary function
       via  74f7080 uptodateness: migrate get_kcc_and_dsas as a function
       via  90975eb uptodateness: extract get_utdv_max_distance
       via  bbef7d6 uptodateness: extract function get_utdv_distances
       via  ac1fba3 uptodateness: extract function get_utdv_edges
       via  ff311f1 netcmd/visualize: rm unused code line
       via  6ed82c7 uptodateness: migrate more methods from visualize
       via  da875ed uptodateness: add new module and migrate functions from visualize
       via  10a9cc4 join: Sanity-check LDB connection before failed join cleanup
       via  30277fe join: Avoid searching for more than strictly required during sanity check
       via  d8ea16a join: LDAP connection to remote DC can timeout in large join
      from  85ec864 gencache: Remove a redundant check

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit e404b6aaf51015f43e1a1f8434c42bcbbe05efdb
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Thu Oct 4 15:37:49 2018 +1300

    selftest: add tests for samba-tool drs uptodateness
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Thu Oct 18 10:02:19 CEST 2018 on sn-devel-144

commit 219b52cfb5419ee6649d4739dd6d8d9576d5b3a5
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Thu Oct 4 11:28:44 2018 +1300

    netcmd/drs: add cmd_drs_uptodateness with json support
    
    Add cmd to print uptodateness summary with json support.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit f4f5f17450f6890b4ba0192c95edab473039816a
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Thu Oct 4 11:24:33 2018 +1300

    uptodateness: add get_utdv_summary function
    
    Get utdv summary from distances matrix and support attr filters.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit 74f7080b1593b446c89f0d940bbdcdb025168cfd
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Thu Oct 4 00:42:08 2018 +1300

    uptodateness: migrate get_kcc_and_dsas as a function
    
    We need to reuse it in drs cmd.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit 90975eb0887562bd704b95a4ff34882670de09c7
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Wed Oct 3 23:45:12 2018 +1300

    uptodateness: extract get_utdv_max_distance
    
    To avoid returning 2 values from get_utdv_distances.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit bbef7d6727bee12c3dee14cd9b67440d0548a8b4
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Wed Oct 3 23:21:11 2018 +1300

    uptodateness: extract function get_utdv_distances
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit ac1fba31c4e87070cfc0dee32150785bedb8e249
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Wed Oct 3 23:09:56 2018 +1300

    uptodateness: extract function get_utdv_edges
    
    Extract function to reuse later.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit ff311f1dc7229497b3b0fb05933177459370372e
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Wed Oct 3 22:49:46 2018 +1300

    netcmd/visualize: rm unused code line
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit 6ed82c7c0dae5f6c0feca1d94f85ca88237f3d1d
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Wed Oct 3 22:39:04 2018 +1300

    uptodateness: migrate more methods from visualize
    
    Move methods from cmd_uptodateness to new module.
    Will reuse in drs cmd later.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit da875eda6a65f283492497f57df06c7ff57562a3
Author: Joe Guo <joeg at catalyst.net.nz>
Date:   Wed Oct 3 22:21:54 2018 +1300

    uptodateness: add new module and migrate functions from visualize
    
    Both visualize and drs cmd will have uptodateness functions.
    Create a new module to reuse code.
    
    Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13658

commit 10a9cc44abd29f46f075823fce55b496e760c7bd
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu Oct 18 13:07:20 2018 +1300

    join: Sanity-check LDB connection before failed join cleanup
    
    Joining a large DB can take so long that the LDAP connection times out.
    The previous patch fixed the 'happy case' where the join succeeds.
    However, if the commit or replication fails (throwing an exception),
    then the cleanup code can also fail when it tries to delete objects from
    the remote DC. This then gives you an error pointing to
    cleanup_old_accounts() rather than what actually went wrong.
    
    This patch adds a sanity-check that if the join fails, that the LDB
    connection to the remote DC is still alive, before we start deleting
    objects.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13612
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 30277feb839f7460638c777b048e45d557dfdea0
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Oct 18 16:50:19 2018 +1300

    join: Avoid searching for more than strictly required during sanity check
    
    We check for the default base DN as this does require authentication, but
    we do not need to search for more than just that (so use SCOPE_BASE) and
    we need no attributes, so ask for none
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit d8ea16a3fb6d8ad9b738e8e71adc07079a292079
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Wed Oct 17 14:41:12 2018 +1300

    join: LDAP connection to remote DC can timeout in large join
    
    When joining a very large domain (e.g. 100K users), the replication can
    take so long that the LDAP connection to the remote DC times out.
    
    This patch avoids the problem by adding in a sanity-check after the
    replication finishes that the LDB connection is still alive. If not,
    then we reconnect.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13612
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 python/samba/join.py                           |  31 +++-
 python/samba/netcmd/drs.py                     |  93 ++++++++++++
 python/samba/netcmd/visualize.py               | 136 ++---------------
 python/samba/tests/samba_tool/visualize_drs.py | 143 +++++++++++++++++-
 python/samba/uptodateness.py                   | 199 +++++++++++++++++++++++++
 5 files changed, 474 insertions(+), 128 deletions(-)
 create mode 100644 python/samba/uptodateness.py


Changeset truncated at 500 lines:

diff --git a/python/samba/join.py b/python/samba/join.py
index 3869947..01636fe 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -112,9 +112,9 @@ class DCJoinContext(object):
             ctx.site = DEFAULTSITE
 
         try:
-            ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
-        except ldb.LdbError as e4:
-            (enum, estr) = e4.args
+            ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
+        except ldb.LdbError as e:
+            (enum, estr) = e.args
             raise DCJoinException(estr)
 
         ctx.base_dn = str(ctx.samdb.get_default_basedn())
@@ -1020,6 +1020,27 @@ class DCJoinContext(object):
         else:
             ctx.local_samdb.transaction_commit()
 
+        # A large replication may have caused our LDB connection to the
+        # remote DC to timeout, so check the connection is still alive
+        ctx.refresh_ldb_connection()
+
+    def refresh_ldb_connection(ctx):
+        try:
+            # query the rootDSE to check the connection
+            ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
+        except ldb.LdbError as e:
+            (enum, estr) = e.args
+
+            # if the connection was disconnected, then reconnect
+            if (enum == ldb.ERR_OPERATIONS_ERROR and
+                'NT_STATUS_CONNECTION_DISCONNECTED' in estr):
+                ctx.logger.warning("LDB connection disconnected. Reconnecting")
+                ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
+                                  session_info=system_session(),
+                                  credentials=ctx.creds, lp=ctx.lp)
+            else:
+                raise DCJoinException(estr)
+
     def send_DsReplicaUpdateRefs(ctx, dn):
         r = drsuapi.DsReplicaUpdateRefsRequest1()
         r.naming_context = drsuapi.DsReplicaObjectIdentifier()
@@ -1422,6 +1443,10 @@ class DCJoinContext(object):
                 print("Join failed - cleaning up")
             except IOError:
                 pass
+
+            # cleanup the failed join (checking we still have a live LDB
+            # connection to the remote DC first)
+            ctx.refresh_ldb_connection()
             ctx.cleanup_old_join()
             raise
 
diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py
index 0f7a243..0b2ed37 100644
--- a/python/samba/netcmd/drs.py
+++ b/python/samba/netcmd/drs.py
@@ -18,6 +18,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
+from __future__ import print_function
+
 import samba.getopt as options
 import ldb
 import logging
@@ -39,6 +41,14 @@ from samba.ndr import ndr_unpack
 from samba.dcerpc import drsblobs
 from samba import colour
 
+from samba.uptodateness import (
+    get_partition_maps,
+    get_utdv_edges,
+    get_utdv_distances,
+    get_utdv_summary,
+    get_kcc_and_dsas,
+)
+
 
 def drsuapi_connect(ctx):
     '''make a DRSUAPI connection to the server'''
@@ -769,6 +779,88 @@ class cmd_drs_clone_dc_database(Command):
                    targetdir=targetdir, include_secrets=include_secrets)
 
 
+class cmd_drs_uptodateness(Command):
+    """Show uptodateness status"""
+
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+    }
+
+    takes_options = [
+        Option("-H", "--URL", metavar="URL", dest="H",
+               help="LDB URL for database or target server"),
+        Option("-p", "--partition",
+               help="restrict to this partition"),
+        Option("--json", action='store_true',
+               help="Print data in json format"),
+        Option("--maximum", action='store_true',
+               help="Print maximum out-of-date-ness only"),
+        Option("--median", action='store_true',
+               help="Print median out-of-date-ness only"),
+        Option("--full", action='store_true',
+               help="Print full out-of-date-ness data"),
+    ]
+
+    def format_as_json(self, partitions_summaries):
+        return json.dumps(partitions_summaries, indent=2)
+
+    def format_as_text(self, partitions_summaries):
+        lines = []
+        for part_name, summary in partitions_summaries.items():
+            items = ['%s: %s' % (k, v) for k, v in summary.items()]
+            line = '%-15s %s' % (part_name, '  '.join(items))
+            lines.append(line)
+        return '\n'.join(lines)
+
+    def run(self, H=None, partition=None,
+            json=False, maximum=False, median=False, full=False,
+            sambaopts=None, credopts=None, versionopts=None,
+            quiet=False, verbose=False):
+
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp, fallback_machine=True)
+        local_kcc, dsas = get_kcc_and_dsas(H, lp, creds)
+        samdb = local_kcc.samdb
+        short_partitions, _ = get_partition_maps(samdb)
+        if partition:
+            if partition in short_partitions:
+                part_dn = short_partitions[partition]
+                # narrow down to specified partition only
+                short_partitions = {partition: part_dn}
+            else:
+                raise CommandError("unknown partition %s" % partition)
+
+        filters = []
+        if maximum:
+            filters.append('maximum')
+        if median:
+            filters.append('median')
+
+        partitions_distances = {}
+        partitions_summaries = {}
+        for part_name, part_dn in short_partitions.items():
+            utdv_edges = get_utdv_edges(local_kcc, dsas, part_dn, lp, creds)
+            distances = get_utdv_distances(utdv_edges, dsas)
+            summary = get_utdv_summary(distances, filters=filters)
+            partitions_distances[part_name] = distances
+            partitions_summaries[part_name] = summary
+
+        if full:
+            # always print json format
+            output = self.format_as_json(partitions_distances)
+        else:
+            if json:
+                output = self.format_as_json(partitions_summaries)
+            else:
+                output = self.format_as_text(partitions_summaries)
+
+        print(output, file=self.outf)
+
+
 class cmd_drs(SuperCommand):
     """Directory Replication Services (DRS) management."""
 
@@ -779,3 +871,4 @@ class cmd_drs(SuperCommand):
     subcommands["showrepl"] = cmd_drs_showrepl()
     subcommands["options"] = cmd_drs_options()
     subcommands["clone-dc-database"] = cmd_drs_clone_dc_database()
+    subcommands["uptodateness"] = cmd_drs_uptodateness()
diff --git a/python/samba/netcmd/visualize.py b/python/samba/netcmd/visualize.py
index 8292802..a0eb676 100644
--- a/python/samba/netcmd/visualize.py
+++ b/python/samba/netcmd/visualize.py
@@ -39,6 +39,16 @@ import re
 from samba.kcc import KCC, ldif_import_export
 from samba.kcc.kcc_utils import KCCError
 from samba.compat import text_type
+from samba.uptodateness import (
+    get_partition_maps,
+    get_partition,
+    get_own_cursor,
+    get_utdv,
+    get_utdv_edges,
+    get_utdv_distances,
+    get_utdv_max_distance,
+    get_kcc_and_dsas,
+)
 
 COMMON_OPTIONS = [
     Option("-H", "--URL", help="LDB URL for database or target server",
@@ -91,19 +101,6 @@ class GraphCommand(Command):
         samdb = SamDB(url=H, credentials=creds, lp=lp)
         return samdb
 
-    def get_kcc_and_dsas(self, H, lp, creds):
-        """Get a readonly KCC object and the list of DSAs it knows about."""
-        unix_now = int(time.time())
-        kcc = KCC(unix_now, readonly=True)
-        kcc.load_samdb(H, lp, creds)
-
-        dsa_list = kcc.list_dsas()
-        dsas = set(dsa_list)
-        if len(dsas) != len(dsa_list):
-            print("There seem to be duplicate dsas", file=sys.stderr)
-
-        return kcc, dsas
-
     def write(self, s, fn=None, suffix='.dot'):
         """Decide whether we're dealing with a filename, a tempfile, or
         stdout, and write accordingly.
@@ -207,36 +204,6 @@ def colour_hash(x):
     return '#%06x' % c
 
 
-def get_partition_maps(samdb):
-    """Generate dictionaries mapping short partition names to the
-    appropriate DNs."""
-    base_dn = samdb.domain_dn()
-    short_to_long = {
-        "DOMAIN": base_dn,
-        "CONFIGURATION": str(samdb.get_config_basedn()),
-        "SCHEMA": "CN=Schema,%s" % samdb.get_config_basedn(),
-        "DNSDOMAIN": "DC=DomainDnsZones,%s" % base_dn,
-        "DNSFOREST": "DC=ForestDnsZones,%s" % base_dn
-    }
-
-    long_to_short = {}
-    for s, l in short_to_long.items():
-        long_to_short[l] = s
-
-    return short_to_long, long_to_short
-
-
-def get_partition(samdb, part):
-    # Allow people to say "--partition=DOMAIN" rather than
-    # "--partition=DC=blah,DC=..."
-    if part is not None:
-        short_partitions, long_partitions = get_partition_maps(samdb)
-        part = short_partitions.get(part.upper(), part)
-        if part not in long_partitions:
-            raise CommandError("unknown partition %s" % part)
-    return part
-
-
 class cmd_reps(GraphCommand):
     "repsFrom/repsTo from every DSA"
 
@@ -254,7 +221,7 @@ class cmd_reps(GraphCommand):
         # 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)
+        local_kcc, dsas = get_kcc_and_dsas(H, lp, creds)
         unix_now = local_kcc.unix_now
 
         partition = get_partition(local_kcc.samdb, partition)
@@ -463,7 +430,7 @@ class cmd_ntdsconn(GraphCommand):
             creds = None
             H = self.import_ldif_db(importldif, lp)
 
-        local_kcc, dsas = self.get_kcc_and_dsas(H, lp, creds)
+        local_kcc, dsas = get_kcc_and_dsas(H, lp, creds)
         local_dsa_dn = local_kcc.my_dsa_dnstr.split(',', 1)[1]
         vertices = set()
         attested_edges = []
@@ -684,36 +651,6 @@ class cmd_uptodateness(GraphCommand):
                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 = str(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,
@@ -729,7 +666,7 @@ class cmd_uptodateness(GraphCommand):
         # 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)
+        local_kcc, dsas = get_kcc_and_dsas(H, lp, creds)
         self.samdb = local_kcc.samdb
         partition = get_partition(self.samdb, partition)
 
@@ -742,52 +679,11 @@ class cmd_uptodateness(GraphCommand):
             if partition not in (part_dn, None):
                 continue  # we aren't doing this partition
 
-            cursors = self.get_utdv(self.samdb, part_dn)
+            utdv_edges = get_utdv_edges(local_kcc, dsas, part_dn, lp, creds)
 
-            # 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 = get_utdv_distances(utdv_edges, dsas)
 
-            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)
+            max_distance = get_utdv_max_distance(distances)
 
             digits = min(max_digits, len(str(max_distance)))
             if digits < 1:
diff --git a/python/samba/tests/samba_tool/visualize_drs.py b/python/samba/tests/samba_tool/visualize_drs.py
index 42facac..0f566ea 100644
--- a/python/samba/tests/samba_tool/visualize_drs.py
+++ b/python/samba/tests/samba_tool/visualize_drs.py
@@ -27,6 +27,7 @@ assertions.
 from __future__ import print_function
 import os
 import re
+import json
 import random
 import subprocess
 from samba.tests.samba_tool.base import SambaToolCmdTest
@@ -40,6 +41,14 @@ ENV_DSAS = {
                    'CN=LOCALVAMPIREDC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=samba,DC=example,DC=com'],
 }
 
+PARTITION_NAMES = [
+    "DOMAIN",
+    "CONFIGURATION",
+    "SCHEMA",
+    "DNSDOMAIN",
+    "DNSFOREST",
+]
+
 
 def set_auto_replication(dc, allow):
     credstring = '-U%s%%%s' % (os.environ["USERNAME"], os.environ["PASSWORD"])
@@ -151,11 +160,7 @@ class SambaToolVisualizeDrsTest(SambaToolCmdTest):
     def test_uptodateness_partitions(self):
         creds = "%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"])
         dc1 = os.environ["SERVER"]
-        for part in ["CONFIGURATION",
-                     "SCHEMA",
-                     "DNSDOMAIN",
-                     "DNSFOREST"]:
-
+        for part in PARTITION_NAMES:
             (result, out, err) = self.runsubcmd("visualize", "uptodateness",
                                                 "-r",
                                                 '-H', "ldap://%s" % dc1,
@@ -164,6 +169,134 @@ class SambaToolVisualizeDrsTest(SambaToolCmdTest):
                                                 '--partition', part)
             self.assertCmdSuccess(result, out, err)
 
+    def test_drs_uptodateness(self):
+        """
+        Test cmd `drs uptodateness`
+
+        It should print info like this:
+
+            DNSDOMAIN       failure: 4  median: 1.5  maximum: 2
+            SCHEMA          failure: 4  median: 220.0  maximum: 439
+            DOMAIN          failure: 1  median: 25  maximum: 25
+            CONFIGURATION   failure: 1  median: 25  maximum: 25
+            DNSFOREST       failure: 4  median: 1.5  maximum: 2
+
+        """
+        creds = "%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"])
+        dc1 = os.environ["SERVER"]
+        dc2 = os.environ["DC_SERVER"]
+        for dc in [dc1, dc2]:
+            (result, out, err) = self.runsubcmd("drs", "uptodateness",
+                                                '-H', "ldap://%s" % dc,
+                                                '-U', creds)
+            self.assertCmdSuccess(result, out, err)
+            # each partition name should be in output
+            for part_name in PARTITION_NAMES:
+                self.assertIn(part_name, out, msg=out)
+
+            for line in out.splitlines():
+                # check keyword in output
+                for attr in ['maximum', 'median', 'failure']:
+                    self.assertIn(attr, line)
+
+    def test_drs_uptodateness_partition(self):
+        """
+        Test cmd `drs uptodateness --partition DOMAIN`
+
+        It should print info like this:
+
+            DOMAIN          failure: 1  median: 25  maximum: 25
+
+        """
+        creds = "%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"])
+        dc1 = os.environ["SERVER"]
+        dc2 = os.environ["DC_SERVER"]
+        for dc in [dc1, dc2]:
+            (result, out, err) = self.runsubcmd("drs", "uptodateness",
+                                                '-H', "ldap://%s" % dc,
+                                                '-U', creds,
+                                                '--partition', 'DOMAIN')
+            self.assertCmdSuccess(result, out, err)
+
+            lines = out.splitlines()
+            self.assertEquals(len(lines), 1)
+
+            line = lines[0]
+            self.assertTrue(line.startswith('DOMAIN'))
+
+    def test_drs_uptodateness_json(self):
+        """
+        Test cmd `drs uptodateness --json`
+
+        Example output:
+
+            {
+                "DNSDOMAIN": {
+                    "failure": 0,
+                    "median": 0.0,
+                    "maximum": 0
+                },
+                ...
+                "SCHEMA": {
+                    "failure": 0,
+                    "median": 0.0,
+                    "maximum": 0
+                }
+            }
+        """
+        creds = "%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"])
+        dc1 = os.environ["SERVER"]
+        dc2 = os.environ["DC_SERVER"]
+        for dc in [dc1, dc2]:
+            (result, out, err) = self.runsubcmd("drs", "uptodateness",


-- 
Samba Shared Repository



More information about the samba-cvs mailing list