The branch, master has been updated
       via  384eaba s4: samba-tool user --help documenation improvements 
Signed-off-by: Theresa Halloran <thall...@linux.vnet.ibm.com>
       via  f0ae7e1 kcc: fixed program name
       via  eb9c35c kcc: fixed tabs/spaces in kcc python implementation
       via  d85b6ed samba_kcc addtion
       via  c78dac4 add python KCC utility classes and methods
       via  a5d6e42 python dsdb flag addtions (NTDSSETTINGS, NTDSCONN)
      from  0c1669e s3:smb2cli: ignore the NBT/Length header in 
smb2cli_inbuf_parse_compound()

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


- Log -----------------------------------------------------------------
commit 384eaba38e4da2a2e72e1ec5bfea7d7776d16a11
Author: Theresa Halloran <thall...@linux.vnet.ibm.com>
Date:   Wed Nov 2 10:33:12 2011 -0400

    s4: samba-tool user --help documenation improvements
    Signed-off-by: Theresa Halloran <thall...@linux.vnet.ibm.com>
    
    Autobuild-User: Andrew Tridgell <tri...@samba.org>
    Autobuild-Date: Mon Nov  7 01:39:25 CET 2011 on sn-devel-104

commit f0ae7e191682de1e56ca1ed2a9e9aa67e9d22b14
Author: Andrew Tridgell <tri...@samba.org>
Date:   Mon Nov 7 09:55:01 2011 +1100

    kcc: fixed program name
    
    kcccompute -> samba_kcc

commit eb9c35cbb77fa16f8301428ce163359008d1687f
Author: Andrew Tridgell <tri...@samba.org>
Date:   Mon Nov 7 09:53:06 2011 +1100

    kcc: fixed tabs/spaces in kcc python implementation
    
    as spotted by autobuild

commit d85b6edf8cdfd953ce96a50466595384db4c698b
Author: Dave Craft <wimber...@gmail.com>
Date:   Thu Nov 3 12:39:53 2011 -0500

    samba_kcc addtion
    
    Scaffolding and initial implementations of
    portions of the KCC in python.  This code currently
    properly computes the graph nodes for the intrasite
    topology as well as enumerating all steps for a full
    run of the KCC.
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit c78dac4fde0fdcdfe44a4bc98da30f43cf32ff6c
Author: Dave Craft <wimber...@gmail.com>
Date:   Thu Nov 3 12:37:24 2011 -0500

    add python KCC utility classes and methods
    
    New file source4/scripting/python/samba/kcc_utils.py
    contains classes and methods for:
       DirectoryServiceAgent
       NTDSConnection
       GraphNode
       NamingContext
       NCReplica
    These are consumed by a new samba_kcc python script
    for KCC topology computation
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit a5d6e427c5a60a0638d952c56e26cca421d3af9c
Author: Dave Craft <wimber...@gmail.com>
Date:   Thu Nov 3 12:33:38 2011 -0500

    python dsdb flag addtions (NTDSSETTINGS, NTDSCONN)
    
    Add missing flags present in C code base to python
    code base dsdb/pydsdb.c
       INSTANCE_TYPE...
       DS_NTDSSETTINGS_OPT...
       NTDSCONN_OPT...
    These are consumed by the python KCC scripts
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

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

Summary of changes:
 source4/dsdb/pydsdb.c                             |   27 +
 source4/scripting/bin/samba_kcc                   |  703 ++++++++++++++++
 source4/scripting/bin/wscript_build               |    1 +
 source4/scripting/python/samba/kcc_utils.py       |  890 +++++++++++++++++++++
 source4/scripting/python/samba/netcmd/__init__.py |   17 +-
 source4/scripting/python/samba/netcmd/user.py     |  136 +++-
 source4/scripting/wscript_build                   |    2 +-
 7 files changed, 1768 insertions(+), 8 deletions(-)
 create mode 100755 source4/scripting/bin/samba_kcc
 create mode 100644 source4/scripting/python/samba/kcc_utils.py


Changeset truncated at 500 lines:

diff --git a/source4/dsdb/pydsdb.c b/source4/dsdb/pydsdb.c
index 2ff6b82..57e08cc 100644
--- a/source4/dsdb/pydsdb.c
+++ b/source4/dsdb/pydsdb.c
@@ -1084,6 +1084,14 @@ void initdsdb(void)
        ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2008);
        ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2008_R2);
 
+        /* nc replica flags */
+       ADD_DSDB_FLAG(INSTANCE_TYPE_IS_NC_HEAD);
+       ADD_DSDB_FLAG(INSTANCE_TYPE_UNINSTANT);
+       ADD_DSDB_FLAG(INSTANCE_TYPE_WRITE);
+       ADD_DSDB_FLAG(INSTANCE_TYPE_NC_ABOVE);
+       ADD_DSDB_FLAG(INSTANCE_TYPE_NC_COMING);
+       ADD_DSDB_FLAG(INSTANCE_TYPE_NC_GOING);
+
        /* "systemFlags" */
        ADD_DSDB_FLAG(SYSTEM_FLAG_CR_NTDS_NC);
        ADD_DSDB_FLAG(SYSTEM_FLAG_CR_NTDS_DOMAIN);
@@ -1121,6 +1129,17 @@ void initdsdb(void)
        ADD_DSDB_FLAG(DS_FLAG_ATTR_REQ_PARTIAL_SET_MEMBER);
        ADD_DSDB_FLAG(DS_FLAG_ATTR_IS_CONSTRUCTED);
 
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_AUTO_TOPOLOGY_DISABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_TOPL_CLEANUP_DISABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_TOPL_MIN_HOPS_DISABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_TOPL_DETECT_STALE_DISABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_INTER_SITE_AUTO_TOPOLOGY_DISABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_GROUP_CACHING_ENABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_FORCE_KCC_WHISTLER_BEHAVIOR);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_RAND_BH_SELECTION_DISABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_SCHEDULE_HASHING_ENABLED);
+       ADD_DSDB_FLAG(DS_NTDSSETTINGS_OPT_IS_REDUNDANT_SERVER_TOPOLOGY_ENABLED);
+
        ADD_DSDB_FLAG(DS_NTDSDSA_OPT_IS_GC);
        ADD_DSDB_FLAG(DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL);
        ADD_DSDB_FLAG(DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL);
@@ -1138,6 +1157,14 @@ void initdsdb(void)
        ADD_DSDB_FLAG(NTDSCONN_KCC_SITE_FAILOVER_TOPOLOGY);
        ADD_DSDB_FLAG(NTDSCONN_KCC_REDUNDANT_SERVER_TOPOLOGY);
 
+        ADD_DSDB_FLAG(NTDSCONN_OPT_IS_GENERATED);
+        ADD_DSDB_FLAG(NTDSCONN_OPT_TWOWAY_SYNC);
+        ADD_DSDB_FLAG(NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT);
+        ADD_DSDB_FLAG(NTDSCONN_OPT_USE_NOTIFY);
+        ADD_DSDB_FLAG(NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION);
+        ADD_DSDB_FLAG(NTDSCONN_OPT_USER_OWNED_SCHEDULE);
+        ADD_DSDB_FLAG(NTDSCONN_OPT_RODC_TOPOLOGY);
+
        /* GPO policy flags */
        ADD_DSDB_FLAG(GPLINK_OPT_DISABLE);
        ADD_DSDB_FLAG(GPLINK_OPT_ENFORCE);
diff --git a/source4/scripting/bin/samba_kcc b/source4/scripting/bin/samba_kcc
new file mode 100755
index 0000000..30e6687
--- /dev/null
+++ b/source4/scripting/bin/samba_kcc
@@ -0,0 +1,703 @@
+#!/usr/bin/env python
+#
+# Compute our KCC topology
+#
+# Copyright (C) Dave Craft 2011
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+import random
+
+# ensure we get messages out immediately, so they get in the samba logs,
+# and don't get swallowed by a timeout
+os.environ['PYTHONUNBUFFERED'] = '1'
+
+# forcing GMT avoids a problem in some timezones with kerberos. Both MIT
+# heimdal can get mutual authentication errors due to the 24 second difference
+# between UTC and GMT when using some zone files (eg. the PDT zone from
+# the US)
+os.environ["TZ"] = "GMT"
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba, ldb
+import optparse
+import logging
+
+from samba           import getopt as options
+from samba.auth      import system_session
+from samba.samdb     import SamDB
+from samba.kcc_utils import *
+
+class KCC:
+    """The Knowledge Consistency Checker class.  A container for
+       objects and methods allowing a run of the KCC.  Produces
+       a set of connections in the samdb for which the Distributed
+       Replication Service can then utilize to replicate naming
+       contexts
+    """
+    def __init__(self, samdb):
+        """Initializes the partitions class which can hold
+           our local DCs partitions or all the partitions in
+           the forest
+        """
+        self.dsa_table     = {}    # dsa objects
+        self.part_table    = {}    # partition objects
+        self.site_table    = {}
+        self.my_dsa_dnstr  = None  # My dsa DN
+        self.my_site_dnstr = None
+        self.samdb         = samdb
+        return
+
+    def load_my_site(self):
+        """Loads the Site class for the local DSA
+           Raises an Exception on error
+        """
+        self.my_site_dnstr = "CN=%s,CN=Sites,%s" % (samdb.server_site_name(),
+                             samdb.get_config_basedn())
+        site = Site(self.my_site_dnstr)
+
+        site.load_site(samdb)
+        self.site_table[self.my_site_dnstr] = site
+        return
+
+    def load_my_dsa(self):
+        """Discover my nTDSDSA thru the rootDSE entry and
+           instantiate and load the DSA.  The dsa is inserted
+           into the dsa_table by dn string
+           Raises an Exception on error.
+        """
+        dn = ldb.Dn(self.samdb, "")
+        try:
+            res = samdb.search(base=dn, scope=ldb.SCOPE_BASE,
+                               attrs=["dsServiceName"])
+        except ldb.LdbError, (enum, estr):
+            raise Exception("Unable to find my nTDSDSA - (%s)" % estr)
+            return
+
+        dnstr = res[0]["dsServiceName"][0]
+
+        # already loaded
+        if dnstr in self.dsa_table.keys():
+            return
+
+        self.my_dsa_dnstr = dnstr
+        dsa = DirectoryServiceAgent(dnstr)
+
+        dsa.load_dsa(samdb)
+
+        # Assign this dsa to my dsa table
+        # and index by dsa dn
+        self.dsa_table[dnstr] = dsa
+
+        return
+
+    def load_all_dsa(self):
+        """Discover all nTDSDSA thru the sites entry and
+           instantiate and load the DSAs.  Each dsa is inserted
+           into the dsa_table by dn string.
+           Raises an Exception on error.
+        """
+        try:
+            res = self.samdb.search("CN=Sites,%s" %
+                                    self.samdb.get_config_basedn(),
+                                    scope=ldb.SCOPE_SUBTREE,
+                                    expression="(objectClass=nTDSDSA)")
+        except ldb.LdbError, (enum, estr):
+            raise Exception("Unable to find nTDSDSAs - (%s)" % estr)
+            return
+
+        for msg in res:
+            dnstr = str(msg.dn)
+
+            # already loaded
+            if dnstr in self.dsa_table.keys():
+                continue
+
+            dsa = DirectoryServiceAgent(dnstr)
+
+            dsa.load_dsa(self.samdb)
+
+            # Assign this dsa to my dsa table
+            # and index by dsa dn
+            self.dsa_table[dnstr] = dsa
+
+        return
+
+    def load_all_partitions(self):
+        """Discover all NCs thru the Partitions dn and
+           instantiate and load the NCs.  Each NC is inserted
+           into the part_table by partition dn string (not
+           the nCName dn string)
+           Raises an Exception on error
+        """
+        try:
+            res = self.samdb.search("CN=Partitions,%s" %
+                                    self.samdb.get_config_basedn(),
+                                    scope=ldb.SCOPE_SUBTREE,
+                                    expression="(objectClass=crossRef)")
+        except ldb.LdbError, (enum, estr):
+            raise Exception("Unable to find partitions - (%s)" % estr)
+
+        for msg in res:
+            partstr = str(msg.dn)
+
+            # already loaded
+            if partstr in self.part_table.keys():
+                continue
+
+            part = Partition(partstr)
+
+            part.load_partition(self.samdb)
+            self.part_table[partstr] = part
+
+        return
+
+    def should_be_present_test(self):
+        """Enumerate all loaded partitions and DSAs and test
+           if NC should be present as replica
+        """
+        for partdn, part in self.part_table.items():
+
+           for dsadn, dsa in self.dsa_table.items():
+               needed, ro, partial = part.should_be_present(dsa)
+
+               logger.info("dsadn:%s\nncdn:%s\nneeded=%s:ro=%s:partial=%s\n" % 
\
+                           (dsa.dsa_dnstr, part.nc_dnstr, needed, ro, partial))
+        return
+
+    def refresh_failed_links_connections(self):
+        # XXX - not implemented yet
+        return
+
+    def is_stale_link_connection(self, target_dsa):
+        """Returns False if no tuple z exists in the kCCFailedLinks or
+           kCCFailedConnections variables such that z.UUIDDsa is the
+           objectGUID of the target dsa, z.FailureCount > 0, and
+           the current time - z.TimeFirstFailure > 2 hours.
+        """
+        # XXX - not implemented yet
+        return False
+
+    def remove_unneeded_failed_links_connections(self):
+        # XXX - not implemented yet
+        return
+
+    def remove_unneeded_ntds_connections(self):
+        # XXX - not implemented yet
+        return
+
+    def translate_connections(self):
+        # XXX - not implemented yet
+        return
+
+    def intersite(self):
+        """The head method for generating the inter-site KCC replica
+           connection graph and attendant nTDSConnection objects
+           in the samdb
+        """
+        # XXX - not implemented yet
+        return
+
+    def update_rodc_connection(self):
+        """Runs when the local DC is an RODC and updates the RODC NTFRS
+           connection object.
+        """
+        # Given an nTDSConnection object cn1, such that cn1.options contains
+        # NTDSCONN_OPT_RODC_TOPOLOGY, and another nTDSConnection object cn2,
+        # does not contain NTDSCONN_OPT_RODC_TOPOLOGY, modify cn1 to ensure
+        # that the following is true:
+        #
+        #     cn1.fromServer = cn2.fromServer
+        #     cn1.schedule   = cn2.schedule
+        #
+        # If no such cn2 can be found, cn1 is not modified.
+        # If no such cn1 can be found, nothing is modified by this task.
+
+        # XXX - not implemented yet
+        return
+
+    def intrasite_max_node_edges(self, node_count):
+        """Returns the maximum number of edges directed to a node in
+           the intrasite replica graph.  The KCC does not create more
+           than 50 edges directed to a single DC. To optimize replication,
+           we compute that each node should have n+2 total edges directed
+           to it such that (n) is the smallest non-negative integer
+           satisfying (node_count <= 2*(n*n) + 6*n + 7)
+           :param node_count: total number of nodes in the replica graph
+        """
+        n = 0
+        while True:
+            if node_count <= (2 * (n * n) + (6 * n) + 7):
+                break
+            n = n + 1
+        n = n + 2
+        if n < 50:
+            return n
+        return 50
+
+    def construct_intrasite_graph(self, site_local, dc_local,
+                                  nc_x, gc_only, detect_stale):
+
+        # We're using the MS notation names here to allow
+        # correlation back to the published algorithm.
+        #
+        # nc_x     - naming context (x) that we are testing if it
+        #            "should be present" on the local DC
+        # f_of_x   - replica (f) found on a DC (s) for NC (x)
+        # dc_s     - DC where f_of_x replica was found
+        # dc_local - local DC that potentially needs a replica
+        #            (f_of_x)
+        # r_list   - replica list R
+        # p_of_x   - replica (p) is partial and found on a DC (s)
+        #            for NC (x)
+        # l_of_x   - replica (l) is the local replica for NC (x)
+        #            that should appear on the local DC
+        # r_len    = is length of replica list |R|
+        #
+        # If the DSA doesn't need a replica for this
+        # partition (NC x) then continue
+        needed, ro, partial = nc_x.should_be_present(dc_local)
+
+        logger.debug("construct_intrasite_graph:\n" + \
+                     "nc_x: %s\ndc_local: %s\n" % \
+                     (nc_x, dc_local) + \
+                     "gc_only: %s\nneeded: %s\nro: %s\npartial: %s" % \
+                     (gc_only, needed, ro, partial))
+
+        if needed == False:
+            return
+
+        # Create a NCReplica that matches what the local replica
+        # should say.  We'll use this below in our r_list
+        l_of_x = NCReplica(dc_local.dsa_dnstr, dc_local.dsa_guid, \
+                           nc_x.nc_dnstr, nc_x.nc_guid, nc_x.nc_sid)
+
+        l_of_x.identify_by_basedn(self.samdb)
+
+        l_of_x.rep_partial  = partial
+        l_of_x.rep_ro       = ro
+
+        # Empty replica sequence list
+        r_list = []
+
+        # We'll loop thru all the DSAs looking for
+        # writeable NC replicas that match the naming
+        # context dn for (nc_x)
+        #
+        for dc_s_dn, dc_s in self.dsa_table.items():
+
+            # If this partition (nc_x) doesn't appear as a
+            # replica (f_of_x) on (dc_s) then continue
+            if not nc_x.nc_dnstr in dc_s.rep_table.keys():
+                continue
+
+            # Pull out the NCReplica (f) of (x) with the dn
+            # that matches NC (x) we are examining.
+            f_of_x = dc_s.rep_table[nc_x.nc_dnstr]
+
+            # Replica (f) of NC (x) must be writable
+            if f_of_x.is_ro() == True:
+                continue
+
+            # Replica (f) of NC (x) must satisfy the
+            # "is present" criteria for DC (s) that
+            # it was found on
+            if f_of_x.is_present() == False:
+                continue
+
+            # DC (s) must be a writable DSA other than
+            # my local DC.  In other words we'd only replicate
+            # from other writable DC
+            if dc_s.is_ro() or dc_s is dc_local:
+                continue
+
+            # Certain replica graphs are produced only
+            # for global catalogs, so test against
+            # method input parameter
+            if gc_only and dc_s.is_gc() == False:
+                continue
+
+            # DC (s) must be in the same site as the local DC
+            # This is the intra-site algorithm.  We are not
+            # replicating across multiple sites
+            if site_local.is_same_site(dc_s) == False:
+                continue
+
+            # If NC (x) is intended to be read-only full replica
+            # for a domain NC on the target DC then the source
+            # DC should have functional level at minimum WIN2008
+            #
+            # Effectively we're saying that in order to replicate
+            # to a targeted RODC (which was introduced in Windows 2008)
+            # then we have to replicate from a DC that is also minimally
+            # at that level.
+            #
+            # You can also see this requirement in the MS special
+            # considerations for RODC which state that to deploy
+            # an RODC, at least one writable domain controller in
+            # the domain must be running Windows Server 2008
+            if ro and partial == False and nc_x.nc_type == NCType.domain:
+                if dc_s.is_minimum_behavior(DS_BEHAVIOR_WIN2008) == False:
+                    continue
+
+            # If we haven't been told to turn off stale connection
+            # detection and this dsa has a stale connection then
+            # continue
+            if detect_stale and self.is_stale_link_connection(dc_s) == True:
+               continue
+
+            # Replica meets criteria.  Add it to table indexed
+            # by the GUID of the DC that it appears on
+            r_list.append(f_of_x)
+
+        # If a partial (not full) replica of NC (x) "should be present"
+        # on the local DC, append to R each partial replica (p of x)
+        # such that p "is present" on a DC satisfying the same
+        # criteria defined above for full replica DCs.
+        if partial == True:
+
+            # Now we loop thru all the DSAs looking for
+            # partial NC replicas that match the naming
+            # context dn for (NC x)
+            for dc_s_dn, dc_s in self.dsa_table.items():
+
+                # If this partition NC (x) doesn't appear as a
+                # replica (p) of NC (x) on the dsa DC (s) then
+                # continue
+                if not nc_x.nc_dnstr in dc_s.rep_table.keys():
+                    continue
+
+                # Pull out the NCReplica with the dn that
+                # matches NC (x) we are examining.
+                p_of_x = dsa.rep_table[nc_x.nc_dnstr]
+
+                # Replica (p) of NC (x) must be partial
+                if p_of_x.is_partial() == False:
+                    continue
+
+                # Replica (p) of NC (x) must satisfy the
+                # "is present" criteria for DC (s) that
+                # it was found on
+                if p_of_x.is_present() == False:
+                    continue
+
+                # DC (s) must be a writable DSA other than
+                # my DSA.  In other words we'd only replicate
+                # from other writable DSA
+                if dc_s.is_ro() or dc_s is dc_local:
+                    continue
+
+                # Certain replica graphs are produced only
+                # for global catalogs, so test against
+                # method input parameter
+                if gc_only and dc_s.is_gc() == False:
+                    continue
+
+                # DC (s) must be in the same site as the local DC
+                # This is the intra-site algorithm.  We are not
+                # replicating across multiple sites
+                if site_local.is_same_site(dc_s) == False:
+                    continue
+
+                # This criteria is moot (a no-op) for this case
+                # because we are scanning for (partial = True).  The
+                # MS algorithm statement says partial replica scans
+                # should adhere to the "same" criteria as full replica
+                # scans so the criteria doesn't change here...its just
+                # rendered pointless.
+                #
+                # The case that is occurring would be a partial domain
+                # replica is needed on a local DC global catalog.  There
+                # is no minimum windows behavior for those since GCs
+                # have always been present.
+                if ro and partial == False and nc_x.nc_type == NCType.domain:
+                    if dc_s.is_minimum_behavior(DS_BEHAVIOR_WIN2008) == False:
+                        continue
+
+                # If we haven't been told to turn off stale connection
+                # detection and this dsa has a stale connection then
+                # continue
+                if detect_stale and self.is_stale_link_connection(dc_s) == 
True:
+                    continue
+
+                # Replica meets criteria.  Add it to table indexed
+                # by the GUID of the DSA that it appears on
+                r_list.append(p_of_x)
+
+        # Append to R the NC replica that "should be present"


-- 
Samba Shared Repository

Reply via email to