mdb_util: Better error message if lmdb-utils not installed
[amitay/samba.git] / python / samba / uptodateness.py
index b3468639e92d6fbac9c1dc901d3e695373847959..d2662549dfcb57a7ea6945df2a61cd73262e2f9b 100644 (file)
@@ -26,6 +26,21 @@ from ldb import SCOPE_BASE, LdbError
 from samba import nttime2unix, dsdb
 from samba.netcmd import CommandError
 from samba.samdb import SamDB
+from samba.kcc import KCC
+
+
+def get_kcc_and_dsas(url, 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(url, 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 get_partition_maps(samdb):
@@ -117,7 +132,6 @@ def get_utdv_edges(local_kcc, dsas, part_dn, lp, creds):
 
 def get_utdv_distances(utdv_edges, dsas):
     distances = {}
-    max_distance = 0
     for dn1 in dsas:
         try:
             peak = utdv_edges[dn1][dn1]
@@ -130,12 +144,56 @@ def get_utdv_distances(utdv_edges, dsas):
                 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)
-    return distances, max_distance
+    return distances
+
+
+def get_utdv_max_distance(distances):
+    max_distance = 0
+    for vector in distances.values():
+        for distance in vector.values():
+            max_distance = max(max_distance, distance)
+    return max_distance
+
+
+def get_utdv_summary(distances, filters=None):
+    maximum = failure = 0
+    median = 0.0  # could be average of 2 median values
+    values = []
+    # put all values into a list, exclude self to self ones
+    for dn_outer, vector in distances.items():
+        for dn_inner, distance in vector.items():
+            if dn_outer != dn_inner:
+                values.append(distance)
+
+    if values:
+        values.sort()
+        maximum = values[-1]
+        length = len(values)
+        if length % 2 == 0:
+            index = length/2 - 1
+            median = (values[index] + values[index+1])/2.0
+            median = round(median, 1)  # keep only 1 decimal digit like 2.5
+        else:
+            index = (length - 1)/2
+            median = values[index]
+            median = float(median)  # ensure median is always a float like 1.0
+        # if value not exist, that's a failure
+        expected_length = len(distances) * (len(distances) - 1)
+        failure = expected_length - length
+
+    summary = {
+        'maximum': maximum,
+        'median': median,
+        'failure': failure,
+    }
+
+    if filters:
+        return {key: summary[key] for key in filters}
+    else:
+        return summary