
    b                    Z   d dl Z d dlZd dlmZ d dlZd dlmZmZ d dlmZm	Z	m
Z
 d dlmZ d dlmZ d dlmZmZ d dlmZmZmZmZ d d	lmZmZmZmZ d d
lmZmZmZ d dlmZ d dl m!Z! d dl"m#Z# d dl$m%Z% d dlm&Z&m'Z' d dlm(Z( d dl)m*Z*m+Z+m,Z, d dl$m-Z- d dl.m/Z/ d Z0d Z1 G d de2          Z3dS )    N)
cmp_to_key)unix2nttimenttime2unix)ldbdsdb	drs_utils)system_session)SamDB)drsuapimisc)Site	Partition	TransportSiteLink)	NCReplicaNCType
nctype_lut	GraphNode)
RepsFromToKCCErrorKCCFailedObject)convert_schedule_to_repltimes)ndr_pack)verify_and_dot)ldif_import_export)setup_graphget_spanning_tree_edges)Vertex)DEBUGDEBUG_FNlogger)debug)cmpc                    |                                  r|                                 sdS |                                  s|                                 rdS t          t          | j                  t          |j                            S )a   Helper to sort DSAs by guid global catalog status

    GC DSAs come before non-GC DSAs, other than that, the guids are
    sorted in NDR form.

    :param dsa1: A DSA object
    :param dsa2: Another DSA
    :return: -1, 0, or 1, indicating sort order.
       )is_gcr#   r   dsa_guid)dsa1dsa2s     4/usr/lib/python3/dist-packages/samba/kcc/__init__.pysort_dsa_by_gc_and_guidr,   2   sp     zz|| DJJLL r::<< DJJLL rx&&(?(?@@@    c                      dS )zCan the KCC use SMTP replication?

    Currently always returns false because Samba doesn't implement
    SMTP transfer for NC changes between DCs.

    :return: Boolean (always False)
    F r/   r-   r+   is_smtp_replication_availabler0   C   s	     5r-   c                      e Zd ZdZ	 	 d.dZd Zd Zd Zd Zd	 Z	d
 Z
d Zd/dZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd/dZd/dZd Zd Zd Zd Zd Zd Zd Zd  Z d! Z!d0d#Z"d$ Z#d% Z$d& Z%d' Z&d1d(Z'd2d*Z(	 	 	 d3d+Z)d/d,Z*d- Z+dS )4KCCa;  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

    :param unix_now: The putative current time in seconds since 1970.
    :param readonly: Don't write to the database.
    :param verify: Check topological invariants for the generated graphs
    :param debug: Write verbosely to stderr.
    :param dot_file_dir: write diagnostic Graphviz files in this directory
    FNc                    i | _         i | _        d| _        i | _        i | _        i | _        | j        j        | _        | j        j        | _        i | _	        t                      | _        t                      | _        d| _        d| _        d| _        d| _        d| _        || _        t'          |          | _        || _        || _        || _        || _        dS )zInitializes the partitions class which can hold
        our local DCs partitions or all the partitions in
        the forest
        N)
part_table
site_tableip_transportsitelink_tabledsa_by_dnstrdsa_by_guidgetget_dsa_by_guidstrget_dsakcc_failed_linkssetkcc_failed_connectionskept_connectionsmy_dsa_dnstrmy_dsamy_site_dnstrmy_sitesamdbunix_nowr   nt_nowreadonlyverifyr"   dot_file_dir)selfrF   rH   rI   r"   rJ   s         r+   __init__zKCC.__init__[   s       "&"2"6(,
 !#&)ee#
 !$ !
 !(++ 
(r-   c                 B   	 | j                             d| j                                         z  t          j        d          }n3# t          j        $ r!}|j        \  }}t          d|z            d}~ww xY w|D ]}t          |j	                  }t          |          }|                    | j                    |j        dk    r|| _        R|j        dk    rt          j        d           rt          j        d	|j        d
           | j        t          d          dS )zLoads the inter-site transport objects for Sites

        :return: None
        :raise KCCError: if no IP transport is found
        $CN=Inter-Site Transports,CN=Sites,%sz (objectClass=interSiteTransport)scope
expressionz+Unable to find inter-site transports - (%s)NIPSMTPz2Samba KCC is ignoring the obsolete SMTP transport.z0Samba KCC does not support the transport called .z(there doesn't seem to be an IP transport)rE   searchget_config_basednr   SCOPE_SUBTREELdbErrorargsr   strdnr   load_transportnamer6   r!   r"   warning)rK   rese2enumestrmsgdnstr	transports           r+   load_ip_transportzKCC.load_ip_transport   sm   	!*##$J$(J$@$@$B$B%C*-*;/Q $ S SCC | 	! 	! 	!7LT4H  ! ! !	!
  	A 	ACKKE!%((I$$TZ000~%%$-!!6)) / 0 0 0 0 /8~~~ @ A A A A $EFFF %$   AA A4A//A4c                    	 | j                             d| j                                         z  t          j        d          }n3# t          j        $ r!}|j        \  }}t          d|z            d}~ww xY w|D ]S}t          |j	                  }|| j
        v r t          |          }|                    | j                    || j
        |<   TdS )zyLoads the inter-site siteLink objects

        :return: None
        :raise KCCError: if site-links aren't found
        rN   z(objectClass=siteLink)rO   z*Unable to find inter-site siteLinks - (%s)N)rE   rU   rV   r   rW   rX   rY   r   rZ   r[   r7   r   load_sitelink)rK   r_   e3ra   rb   rc   rd   sitelinks           r+   load_all_sitelinkszKCC.load_all_sitelinks   s    	P*##$J$(J$@$@$B$B%C*-*;/G $ I ICC | 	P 	P 	P7LT4G$NOOO	P  	2 	2CKKE +++H""4:... *2D&&	2 	2rg   c                 |   t          || j                  }|                    | j                   t	          |j                  }|| j        vrd|| j        |<   | j                            |j	                   | j
                            d |j	                                        D                        | j        |         S )zHelper for load_my_site and load_all_sites.

        Put all the site's DSAs into the KCC indices.

        :param dn_str: a site dn_str
        :return: the Site object pertaining to the dn_str
        c              3   B   K   | ]}t          |j                  |fV  d S NrZ   r(   .0xs     r+   	<genexpr>z KCC.load_site.<locals>.<genexpr>   sK       $F $F() &)__a$8 $F $F $F $F $F $Fr-   )r   rF   	load_siterE   rZ   	site_guidr5   r8   update	dsa_tabler9   values)rK   dn_strsiteguids       r+   ru   zKCC.load_site   s     FDM**tz"""
 4>""t&&$(DOD!$$T^444## $F $F-1^-B-B-D-D$F $F $F F F F t$$r-   c                     d| j                                         d| j                                         | _        |                     | j                  | _        dS )zGLoad the Site object for the local DSA.

        :return: None
        zCN=z
,CN=Sites,N)rE   server_site_namerV   rC   ru   rD   rK   s    r+   load_my_sitezKCC.load_my_site   sW      J''))))J((***, ~~d&899r-   c                 N   	 | j                             d| j                                         z  t          j        d          }n3# t          j        $ r!}|j        \  }}t          d|z            d}~ww xY w|D ]+}t          |j	                  }| 
                    |           ,dS )z|Discover all sites and create Site objects.

        :return: None
        :raise: KCCError if sites can't be found
        zCN=Sites,%sz(objectClass=site)rO   zUnable to find sites - (%s)N)rE   rU   rV   r   rW   rX   rY   r   rZ   r[   ru   )rK   r_   e4ra   rb   rc   sitestrs          r+   load_all_siteszKCC.load_all_sites   s    	A*##M$(J$@$@$B$B%C*-*;/C $ E ECC | 	A 	A 	A7LT484?@@@	A  	$ 	$C#&kkGNN7####	$ 	$rg   c           
      V   d| j                                         z  }t          j        | j         |          }	 | j                             |t          j        dg          }n# t          j        $ r}|j        \  }}t          d|d|d|d           	 | j                             dt          j        d	g          }t          j        | j         |d
         d	         d
         	                    d                    }| j                             |t          j        dg          }n3# t          j        $ r!}|j        \  }}t          d|z            d}~ww xY wY d}~nd}~ww xY wt          |          dk    r$t          d|                                z            t          j        | j                                                   }	t          j        |d
         d         d
                   |	k    rt          d          t          |d
         j                  | _        | j                            | j                  | _        | j        | j        vrXt-          j        d| j        z             | j        | j        | j        <   | j        | j        t          | j        j                  <   dS dS )z|Discover my nTDSDSA dn thru the rootDSE entry

        :return: None
        :raise: KCCError if DSA can't be found
        z	<GUID=%s>
objectGUID)baserP   attrszSearch for dn 'z' [from z
] failed: zL. This typically happens in --importldif mode due to lack of module support. dsServiceNamer   utf8z Unable to find my nTDSDSA - (%s)Nr&   zUnable to find my nTDSDSA at %sz>Did not find the GUID we expected, perhaps due to --importldifzmy_dsa %s isn't in self.dsas_by_dnstr: it must be RODC.
Let's add it, because my_dsa is special!
(likewise for self.dsa_by_guid))rE   get_ntds_GUIDr   DnrU   
SCOPE_BASErX   rY   r    decoder   lenextended_strr   GUIDrZ   r[   rA   rD   r<   rB   r8   r"   DEBUG_DARK_YELLOWr9   r(   )
rK   dn_queryr[   r_   e5ra   rb   service_name_rese	ntds_guids
             r+   load_my_dsazKCC.load_my_dsa   s    !9!9!;!;;VDJ))	J*##3>+7. $ : :CC| 	J 	J 	J7LT4H57RR444I J J JJ $(:#4#4";>><K;L $5 $N $N  VDJ,Q/@CJJ6RRT T j''Rs~/;n ( > >< J J J vtADHIIIJ %	J0 s88q==<??,,- . . . Idj668899	9SVL)!,--:: : ; ; ;  A	NNl**4+<==D$555# %H %)$5	%6 7 7 7 48;Dd/0:>+DS!566777 65s<   (A" "E"1#EBD%$E%E4EEEE"c                    	 | j                             d| j                                         z  t          j        d          }n3# t          j        $ r!}|j        \  }}t          d|z            d}~ww xY w|D ]S}t          |j	                  }|| j
        v r t          |          }|                    | j                    || j
        |<   TdS )zDiscover and load all partitions.

        Each NC is inserted into the part_table by partition
        dn string (not the nCName dn string)

        :return: None
        :raise: KCCError if partitions can't be found
        zCN=Partitions,%sz(objectClass=crossRef)rO   z Unable to find partitions - (%s)N)rE   rU   rV   r   rW   rX   rY   r   rZ   r[   r4   r   load_partition)rK   r_   e6ra   rb   rc   partstrparts           r+   load_all_partitionszKCC.load_all_partitions5  s    	F*##$6$(J$@$@$B$B%C*-*;/G $ I ICC | 	F 	F 	F7LT4=DEEE	F  
	, 
	,C#&kkG $/))W%%D
+++'+DOG$$
	, 
	,rg   c           	         i | _         | j                                        \  }}|                                D ]}|j        D ]}|j        }|dk    rt          |j                  }|j        }|j	        }	|j
        }
| j                             |          }|t          ||||	|
          }|| j         |<   st          |j        |          |_        t          |j        |          |_        |	|_        t%                      }|Pt'          d           | j        D ]8} ||j                  r|                    |           (|xj        dz  c_        9nt'          d           | j                            |           dS )zEnsure the failed links list is up to date

        Based on MS-ADTS 6.2.2.1

        :param ping: An oracle function of remote site availability
        :return: None
        r   Nz6refresh_failed_links: checking if links are still downr&   zdrefresh_failed_links: not checking live links because we
weren't asked to --attempt-live-connections)r=   rB   get_rep_tablesry   rep_repsFromconsecutive_sync_failuresrZ   source_dsa_obj_guidlast_successlast_attempt	dns_name1r:   r   maxfailure_countmintime_first_failurelast_resultr>   r   r?   dns_nameadddifference_update)rK   pingcurrentneededreplica	reps_fromr   r(   r   r   r   frestore_connections
connections                 r+    refresh_failed_links_connectionsz$KCC.refresh_failed_links_connectionsS  s    !#+4466~~'' 	0 	0G$1 0 0	 ) C A%%y<==%.%;"'4$.)--h779'-(:K(02 2A 78D)(33&)!/=&I&IAO+.q/C/A,C ,CA($/AMM)0. "eeJKKK"9 2 2
4
+,, 2'++J7777,,1,,,2  @ A A A 	#556IJJJJJr-   c                     | j                             t          |j                            }|rN|j        dk    rCt          |j                  }|| j        k    rt          j	        d           | j        |z
  dk    rdS dS )aL  Check whether a link to a remote DSA is stale

        Used in MS-ADTS 6.2.2.2 Intrasite Connection Creation

        Returns True if the remote seems to have been down for at
        least two hours, otherwise False.

        :param target_dsa: the remote DSA object
        :return: True if link is stale, otherwise False
        r   z_The last success time attribute for                                  repsFrom is in the future!i   TF)
r=   r:   rZ   r(   r   r   r   rF   r!   error)rK   
target_dsafailed_linkunix_first_failures       r+   is_stale_link_connectionzKCC.is_stale_link_connection  s     +//J4G0H0HII 	 (1,, >?? # &55L "= > > > M$66+EE4 ur-   c                     d S ro   r/   r   s    r+   (remove_unneeded_failed_links_connectionsz,KCC.remove_unneeded_failed_links_connections  s	     	r-   c                     |D ]n}|j         e| j        rDt          j        t	          t          j                                        |_         | j        |_        T|	                    | j
                   odS )aT  Load or fake-load NTDSConnections lacking GUIDs

        New connections don't have GUIDs and created times which are
        needed for sorting. If we're in read-only mode, we make fake
        GUIDs, otherwise we ask SamDB to do it for us.

        :param connections: an iterable of NTDSConnection objects.
        :return: None
        N)r|   rH   r   r   rZ   uuiduuid4rG   whenCreatedload_connectionrE   )rK   connectionscn_conns      r+   _ensure_connections_are_loadedz"KCC._ensure_connections_are_loaded  sr     # 	8 	8G|#= 8#'9S->->#?#?GL*.+G''++DJ777	8 	8r-   c                     | j         j                                        D ]8}|                                }| t	          | j         d|           d|_        9dS )zFind NTDS Connections that lack a remote

        I'm not sure how they appear. Let's be rid of them by marking
        them with the to_be_deleted attribute.

        :return: None
        Nz has phantom connection T)rB   connect_tablery   get_from_dnstrr    to_be_deleted)rK   r   s_dnstrs      r+   _mark_broken_ntdsconnzKCC._mark_broken_ntdsconn  su     {07799 	- 	-G,,..G4;;;;B7D E E E(,%	- 	-r-   c                 4   | j                                         rt          d| j         z             dS | j        }	 |                     |j                                                   n'# t          $ r |                                rY dS Y nw xY wg }|j                                        D ]z}|	                                }|| j         j
        v rV|                                p|                                 }t          |j                  }|                    ||||f           {t!          |          dk     rdS t#          j        |d          D ]H\  }}|\  }}}}|\  }	}
}}|r||
k    r|j        |	j        k     s|j        |	j        k    r||k     rd|_        IdS )a  Find unneeded intrasite NTDS Connections for removal

        Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections.
        Every DC removes its own unnecessary intrasite connections.
        This function tags them with the to_be_deleted attribute.

        :return: None
        z>not doing ntdsconn cleanup for site %s, because it is disabledN   T)rD   is_cleanup_ntdsconn_disabledr    rB   r   r   ry   r   is_ror   rx   is_generatedis_rodc_topologyr   r|   appendr   	itertoolspermutationsr   r   )rK   mydsalocal_connectionsr   r   	removablepacked_guidabcn_conn2s_dnstr2packed_guid2
removable2s                r+   _mark_unneeded_local_ntdsconnz!KCC._mark_unneeded_local_ntdsconn  s    <4466 	 .04= > > >F	//0C0J0J0L0LMMMM 	 	 	{{}}  	
 *1133 	C 	CG,,..G$,000!(!5!5!7!7 "<!(!9!9!;!;=	&w|44!(('7*5y*B C C C
 !""Q&&F*+<a@@ 	- 	-DAq784GWk9;<8Hhj -8###h&:::$(<<<|++(,%	- 	-s   ,A( (BBc                 H   | j                                         rdS | j        j        }g }|                                D ]}|j                                        D ]w}|j        r
|                                }|!||vrR|                     |          }|d|v rt          j
        d|z             d|_        _|                    |||f           x|                     d |D                        |D ]\  }}}|                                r|                                r/|| j        v r-|                     |d          s|                     |d          se|j        }| j                                        D ](}	||	j        v r|D ]\  }
}}||
ur||	j        v rd|_        )dS )a(  find unneeded intersite NTDS Connections for removal

        Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections. The
        intersite topology generator removes links for all DCs in its
        site. Here we just tag them with the to_be_deleted attribute.

        :return: None
        N\0ADELz+DSA appears deleted, removing connection %sTc              3   &   K   | ]}|d          V  dS )r   Nr/   rq   s     r+   rt   z8KCC._mark_unneeded_intersite_ntdsconn.<locals>.<genexpr>'  s&      +O+OQAaD+O+O+O+O+O+Or-   )rB   r   rD   rx   ry   r   r   r   r<   r!   infor   r   r   r   r@   is_bridgehead_failed	dsa_dnstrr5   rw_dsa_table)rK   
local_dsasconnections_and_dsasdsacnr   from_dsato_dsa
from_dnstrr{   cn2to_dsa2	from_dsa2s                r+   !_mark_unneeded_intersite_ntdsconnz%KCC._mark_unneeded_intersite_ntdsconn  s1    ; 	F \+
!$$&& 	E 	EC'..00 E E# ++--?*,,#||G44H'9+?+?$Q&-%. / / /+/( (//S(0CDDDE  	+++O+O:N+O+O+OOOO$8 	4 	4 B??$$ (;(;(=(=  d+++..vt<< ,..x>> , 
 "+J..00 4 4!2223G 4 4/WicMM%):::/3B,4	4 	4r-   c                    |                                 s| j        r|j                                        D ]\}|j        rt          j        d|z             |j        rt          j        d|z             |j        rt          j        d|z             ]|	                    | j
        d           d S |	                    | j
                   d S )NTO BE DELETED:
%sTO BE ADDED:
%sTO BE MODIFIED:
%sTro)r   rH   r   ry   r   r!   r   to_be_addedto_be_modifiedcommit_connectionsrE   )rK   r   connects      r+   _commit_changeszKCC._commit_changes>  s    99;; 	/$- 	/,3355 A A( @K 4w >???& >K 2W <===) AK 5 ?@@@ ""4:$"77777 ""4:.....r-   c                     |                                   |                                  | j                                        r|r|                                  | j        j                                        D ]}|                     |           dS )zRemove unneeded NTDS Connections once topology is calculated

        Based on MS-ADTS 6.2.2.4 Removing Unnecessary Connections

        :param all_connected: indicates whether all sites are connected
        :return: None
        N)	r   r   rB   is_istgr   rD   rx   ry   r   )rK   all_connectedr   s      r+   remove_unneeded_ntdsconnzKCC.remove_unneeded_ntdsconnO  s     	""$$$**,,, ;   	5] 	522444<)0022 	& 	&C  %%%%	& 	&r-   c                    |j         }|| j        j        v }t          |j                  }||j        k    r||_        |j        t          j        z  dk    r|xj        t          j        z  c_        |                                r2|j        t          j	        z  dk    r|xj        t          j	        z  c_        |s|
                    |          r2|j        t          j        z  dk    r|xj        t          j        z  c_        |j        t          j        z  dk    rK|j        t          j        z  dk    r2|j        t          j        z  dk    r|xj        t          j        z  c_        n4|s2|j        t          j        z  dk    r|xj        t          j        z  c_        |sJ|j        t          j        z  dk    r2|j        t          j        z  dk    r|xj        t          j        z  c_        |j        t          j        z  dk    r2|j        t          j        z  dk    r|xj        t          j        z  c_        |                                sd|j        t          j        z  dk    r|xj        t          j        z  c_        |j        t          j        z  dk    r|xj        t          j        z  c_        |j        d| j                                        }	|j        t          j        z  dk    r|xj        t          j         z  c_        t9          j                    |_        |j        |	k    r|	|_        |j         dk    r|j!        |	k    r|	|_!        |"                                rtG          d|z             dS dS )a$  Update an repsFrom object if required.

        Part of MS-ADTS 6.2.2.5.

        Update t_repsFrom if necessary to satisfy requirements. Such
        updates are typically required when the IDL_DRSGetNCChanges
        server has moved from one site to another--for example, to
        enable compression when the server is moved from the
        client's site to another site.

        The repsFrom.update_flags bit field may be modified
        auto-magically if any changes are made here. See
        kcc_utils.RepsFromTo for gory details.


        :param n_rep: NC replica we need
        :param t_repsFrom: repsFrom tuple to modify
        :param s_rep: NC replica at source DSA
        :param s_dsa: source DSA
        :param cn_conn: Local DSA NTDSConnection child

        :return: None
        r   z._msdcs.r&   zmodify_repsFrom(): %sN)$r   rD   rx   r   schedulereplica_flagsr   DRSUAPI_DRS_ADD_REF!is_schedule_minimum_once_per_weekDRSUAPI_DRS_PER_SYNCis_fsmo_role_ownerDRSUAPI_DRS_INIT_SYNCoptionsr   $NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULTNTDSCONN_OPT_USE_NOTIFYDRSUAPI_DRS_NEVER_NOTIFY*NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSIONDRSUAPI_DRS_USE_COMPRESSIONNTDSCONN_OPT_TWOWAY_SYNCDRSUAPI_DRS_TWOWAY_SYNC
is_enabledDRSUAPI_DRS_DISABLE_AUTO_SYNC!DRSUAPI_DRS_DISABLE_PERIODIC_SYNCr(   rE   forest_dns_nameDRSUAPI_DRS_MAIL_REPr   r   transport_guidr   version	dns_name2is_modifiedr    )
rK   n_rep
t_repsFroms_reps_dsar   r   	same_sitetimesnastrs
             r+   modify_repsFromzKCC.modify_repsFroma  s   0 /t|55	 .g.>??J'''"'J %()-01 1$$(CC$$ 4466 	I)-.256 6((G,HH((  	J0099 	J)./367 7((G,II(( _67;>? ? $">>3FF  -56:=> >,,89,,  	M)1269: :((G,LL((  	P_<=ADE E )459<= =((G,OO(( Od;;CC)01589 9((G,KK(( !!## 
	>)67;>? ?((9:(( ):;?BC C((=>((~ #(...$*2L2L2N2N2NO%)*.12 2$$)E(EE$$$(IKK
! 5((#(J ##
(<(E(E#(J !!## 	;,z9:::::	; 	;r-   c                 h   |                                 r|                                rdS |                                }|                     |          }|dS |                    |j                  }|>|                                r*|                                r|                                r|S dS )a  If a connection imply a replica, find the relevant DSA

        Given a NC replica and NTDS Connection, determine if the
        connection implies a repsFrom tuple should be present from the
        source DSA listed in the connection to the naming context. If
        it should be, return the DSA; otherwise return None.

        Based on part of MS-ADTS 6.2.2.5

        :param n_rep: NC replica
        :param cn_conn: NTDS Connection
        :return: source DSA or None
        N)	r  r   r   r<   get_current_replicanc_dnstr
is_presentr   
is_partial)rK   r  r   r   r   r  s         r+   get_dsa_for_implied_replicazKCC.get_dsa_for_implied_replica0  s    * !!## 	w'?'?'A'A 	4((**W%% =4))%.99  "'"2"2"4"4  Ltr-   c           	         d}|| j         }|                                rd}|                                rt          d           dS t          d           |                                \  }}t                      }|D ]}|r||         }|                    | j                   |j        D ]T}t          j
        t          j        z  t          j        z  t          j        z  t          j        z  }	|j        |	k    r|	|_        U|                    | j        | j                   ||vr|                    |           t          dt'          |          t'          |          t'          |          fz             |rt)          d|z             |D ]}||= |                                D ]}
|
                    | j                   |
                    | j                   |
j        D ]}t/          |j                  }|                     |          }|t5          j        d	|z             d|_        M|j        }|                    |          }|D ]}|                                s n	d|_        |                     |
j!                  }|<|"                                r(|
                                s|#                                rd|_        | $                    |
||||           |j%                                        D ]}| &                    |
|          }||
j        D ]1}t/          |j                  }||                     |          u rd} n2|WtO          |
j!                  }|j(        |_        |                     |
j!                  }| $                    |
||||           |)                                r|
j        *                    |           | j        s|rx|
+                                }|rt5          j,        d
|z             |
-                                }|rt5          j,        d|z             |
                    | j        d           |
                    | j                   |rdS |                                D ]7}
|
.                    | j                   |
j/        D ]}t/          |j                  }|                     |          }|t5          j        d|z             d|_        L|j        }d|v rt5          j        d|z             d|_        v|                    | j0                  }t'          |          dk    rd|_        | j        rF|
j/        D ] }|j        rt5          j,        d|z             !|
1                    | j        d           |
1                    | j                   9dS )a[  Adjust repsFrom to match NTDSConnections

        This function adjusts values of repsFrom abstract attributes of NC
        replicas on the local DC to match those implied by
        nTDSConnection objects.

        Based on [MS-ADTS] 6.2.2.5

        :param current_dsa: optional DSA on whose behalf we are acting.
        :return: None
        FNTz;skipping translate_ntdsconn() because disabling flag is setztranslate_ntdsconn(): enterr   zcurrent %d needed %d delete %dzdeleting these reps: %sz'repsFrom source DSA guid (%s) not foundr   r   z%repsTo source DSA guid (%s) not foundr   z+repsTo source DSA guid (%s) appears deletedr   zREMOVING REPS-TO: %s)2rB   r   is_translate_ntdsconn_disabledr    r   r>   load_repsFromrE   r   r   r  r	  r  %DRSUAPI_DRS_SPECIAL_SECRET_PROCESSINGDRSUAPI_DRS_NONGC_RO_REPr  commit_repsFromrH   r   r   r   ry   load_fsmo_rolesrZ   r   r;   r!   r^   r   r   get_connection_by_from_dnstrr   r&  r'  r(  r)  r$  r   r*  r   r(   r  r   dumpstr_to_be_deletedr   dumpstr_to_be_modifiedload_repsTo
rep_repsTorA   commit_repsTo)rK   current_dsar   current_rep_tableneeded_rep_tabledelete_repsrd   c_repr  r  r  guidstrr   r   r   r   r  textt_repsTorts                       r+   translate_ntdsconnzKCC.translate_ntdsconnW  s    +K 	B5577 	 5 6 6 6F.///.9.H.H.J.J++ ee" ' 	+ 	+E +)%0##DJ///"'"4 A AJ%,%B%,%A&B%,%@&A &-%R&S &-%E	&FM
 "/=@@3@
0%%djT]%CCCC 000OOE***1S9J5K5K%&&K(8(85: : 	; 	; 	;  	-+k9:::$ - -%e,, &,,.. k	2 k	2E 
+++!!$*---
 $0 -O -O
 j<==//88 =N#L#*$+ , , ,/3J,
  /)FFwOO*  G"3355  04J, 11%.AA=(8(8(:(:= !*/*:*:*<*< ! 04J, $$UJugNNNN
 '4;;== : :88HH= #("4  J!*"@AAG 7 7 @ @@@ $ A = (77
16
.11%.AA$$UJugNNN ))++ :&--j999} 2 22244 =K 4t ;<<<3355 >K 5 <=== %%djT%:::: %%dj1111  	F%,,.. 5	0 5	0E dj)))
 ", 2 2 h:;;//88 =N#J#*$+ , , ,-1H*
  /''N#P#*$+ , , ,-1H*#@@ARSS{##a'' .2H**} 0* A AB' A$:R$?@@@ ##DJ4#8888 ##DJ////k5	0 5	0r-   c                 T    |t          j        d           dS t          d           dS )a  Merge of kCCFailedLinks and kCCFailedLinks from bridgeheads.

        The KCC on a writable DC attempts to merge the link and connection
        failure information from bridgehead DCs in its own site to help it
        identify failed bridgehead DCs.

        Based on MS-ADTS 6.2.2.3.2 "Merge of kCCFailedLinks and kCCFailedLinks
        from Bridgeheads"

        :param ping: An oracle of current bridgehead availability
        :return: None
        Nz'merge_failed_links() is NOT IMPLEMENTEDz}skipping merge_failed_links() because it requires real network connections
and we weren't asked to --attempt-live-connections)r"   	DEBUG_REDr    )rK   r   s     r+   merge_failed_linkszKCC.merge_failed_linksV  sH    $ OEFFFFF J K K K K Kr-   c           
         | j         j        dz  dk    }t          | j        j                  }t          || j        || j        |          }| j        s| j	        g }|j
        D ]M}t          j        |j        d          D ]0\  }}|                    |j        j        |j        j        f           1Nd}	d|j        z  }
t%          |
|d| j        |	t(          | j        | j	                   |S )	a  Set up an intersite graph

        An intersite graph has a Vertex for each site object, a
        MultiEdge for each SiteLink object, and a MutliEdgeSet for
        each siteLinkBridge object (or implied siteLinkBridge). It
        reflects the intersite topology in a slightly more abstract
        graph form.

        Roughly corresponds to MS-ADTS 6.2.2.3.4.3

        :param part: a Partition object
        :returns: an InterSiteGraph object
        i  r   Nr   r/   zsite_edges_%sFdirectedlabel
propertiesr"   rI   rJ   )rD   site_optionsrZ   r6   r|   r   r5   r7   rI   rJ   edgesr   combinationsverticesr   r{   
site_dnstrr   r   rA   r   )rK   r   bridges_requiredr  g	dot_edgesedger   r   verify_propertiesr]   s              r+   r   zKCC.setup_grapho  s   &  <4zAQFT.344do~+-=? ? ; 	;$+7I M M%24=!DD M MDAq$$af&79J%KLLLLM ""T\1D4U!%!2&7u"&+(,(9	; ; ; ; r-   c                     |                      |||||          }|st          j        d|j        z             dS t          j        d|j        d|d         j                   |d         S )a  Get a bridghead DC for a site.

        Part of MS-ADTS 6.2.2.3.4.4

        :param site: site object representing for which a bridgehead
            DC is desired.
        :param part: crossRef for NC to replicate.
        :param transport: interSiteTransport object for replication
            traffic.
        :param partial_ok: True if a DC containing a partial
            replica or a full replica will suffice, False if only
            a full replica will suffice.
        :param detect_failed: True to detect failed DCs and route
            replication traffic around them, False to assume no DC
            has failed.
        :return: dsa object for the bridgehead DC or None
        z"get_bridgehead FAILED:
sitedn = %sNzget_bridgehead:
	sitedn = z	
	bhdn = r   )get_all_bridgeheadsr"   DEBUG_MAGENTArN  DEBUG_GREENr   )rK   r{   r   re   
partial_okdetect_failedbhss          r+   get_bridgeheadzKCC.get_bridgehead  s    & &&tT9'1=B B 	 E $!0 1 1 14???CF,<,<> 	? 	? 	?1vr-   c                 >   g }|j         dk    rt          d|j                   t          |j                   |j                                        D ]d}|                                }t          |j                  dk    r
||j        vr9| j        	                    |          r;|
                    |          \  }	}
}|	r|r|ss|                    |j                  }n3|                    |j                  }||                                r|s| j                                        r7|5|                                r!|                    t$          j                  s|                     ||          rt+          d           8t          d|j        z             |                    |           f|                                r)|                    t5          t6                               nt9          j        |           t=          j        |           |S )a  Get all bridghead DCs on a site satisfying the given criteria

        Part of MS-ADTS 6.2.2.3.4.4

        :param site: site object representing the site for which
            bridgehead DCs are desired.
        :param part: partition for NC to replicate.
        :param transport: interSiteTransport object for
            replication traffic.
        :param partial_ok: True if a DC containing a partial
            replica or a full replica will suffice, False if
            only a full replica will suffice.
        :param detect_failed: True to detect failed DCs and route
            replication traffic around them, FALSE to assume
            no DC has failed.
        :return: list of dsa object for available bridgehead DCs
        rR   z5get_all_bridgeheads has run into a non-IP transport! r   Nzbridgehead is failedzfound a bridgehead: %skey) r]   r   r    r   ry   get_parent_dnstrr   bridgehead_listrD   r!  should_be_presentr&  r'  r)  rB   r   
is_defaultis_minimum_behaviorr   DS_DOMAIN_FUNCTION_2008r   r   r   r   is_random_bridgehead_disabledsortr   r,   randomshuffler"   DEBUG_YELLOW)rK   r{   r   re   rX  rY  rZ  r   pdnstrr   r   partialreps                r+   rU  zKCC.get_all_bridgeheads  s+   & >T!!('nn/ 0 0 0 	"###$++-- /	 /	C))++F
 Y.//144y888 |%%c** &*&<&<S&A&A#G ' * --dm<< --dm<<;3>>#3#3;J; {  "" s3>>;K;K..t/KLL  ((m<< ,----=>>>JJsOOOO --// 	 HH$;<<H====N33
r-   c                 V    |sdS | j         j        dz  rdS |                     |          S )ad  Determine whether a given DC is known to be in a failed state

        :param dsa: the bridgehead to test
        :param detect_failed: True to really check, False to assume no failure
        :return: True if and only if the DC should be considered failed

        Here we DEPART from the pseudo code spec which appears to be
        wrong. It says, in full:

    /***** BridgeheadDCFailed *****/
    /* Determine whether a given DC is known to be in a failed state.
     * IN: objectGUID - objectGUID of the DC's nTDSDSA object.
     * IN: detectFailedDCs - TRUE if and only failed DC detection is
     *     enabled.
     * RETURNS: TRUE if and only if the DC should be considered to be in a
     *          failed state.
     */
    BridgeheadDCFailed(IN GUID objectGUID, IN bool detectFailedDCs) : bool
    {
        IF bit NTDSSETTINGS_OPT_IS_TOPL_DETECT_STALE_DISABLED is set in
        the options attribute of the site settings object for the local
        DC's site
            RETURN FALSE
        ELSEIF a tuple z exists in the kCCFailedLinks or
        kCCFailedConnections variables such that z.UUIDDsa =
        objectGUID, z.FailureCount > 1, and the current time -
        z.TimeFirstFailure > 2 hours
            RETURN TRUE
        ELSE
            RETURN detectFailedDCs
        ENDIF
    }

        where you will see detectFailedDCs is not behaving as
        advertised -- it is acting as a default return code in the
        event that a failure is not detected, not a switch turning
        detection on or off. Elsewhere the documentation seems to
        concur with the comment rather than the code.
        F   )rD   rJ  r   )rK   r   rY  s      r+   r   zKCC.is_bridgehead_failed  s?    P  	5
 <$z1 	5,,S111r-   c                 ,   |                      ||||	d          }t          d |D                       }t          j        dt	          |          dd |D                        |                      ||||	d          }|                                r|                    |           t          j        dt	          |          dd |D                        |D ]G}|j                                        D ])}|	                    |j
                  }| t          j        d	|j        z             |                                r|                                s|j        |j        k    r|                                s1|                    |          s||_        |                    d
           |                                re|                                rQ|t.          j        z  dk    r=|xj        t.          j        t.          j        z   z  c_        |                    d
           nO|t.          j        z  dk    r<|xj        t.          j        t.          j        z  z  c_        |                    d
           |                                rD|t.          j        z  dk    r0|xj        t.          j         z  c_        |                    d
           nB|t.          j        z  dk    r/|xj        t.          j        z  c_        |                    d
           |                                rD|t.          j         z  dk    r0|xj        t.          j!         z  c_        |                    d
           nB|t.          j         z  dk    r/|xj        t.          j!        z  c_        |                    d
           | j"        s|                                r<|j#        rtI          j%        d|z             |&                    | j'        d
           |&                    | j'                   +Id}|D ]}|j                                        D ]}|	                    |j
                  }|t          j        d|j        z             |                                r|j        |j        k    r_|                                sK| (                    ||
          s| (                    ||
          s|dz  }| j)        *                    |           t          j+        d|z             tY          d| j)                   |dk    r^t.          j-        }|t.          j        z  dk    r|t.          j        t.          j        z  z  }|t.          j        z  dk    r|t.          j        z  }|t.          j         z  dk    r|t.          j!        z  }t]          d| j/        j        z             t.          j0        t.          j1        z  }|2                    ||||j        |          }| j"        s|                                r;|j3        rtI          j%        d|z             |&                    | j'        d
           n|&                    | j'                   | j)        *                    |           dS dS )a  Create an nTDSConnection object as specified if it doesn't exist.

        Part of MS-ADTS 6.2.2.3.4.5

        :param part: crossRef object for the NC to replicate.
        :param rbh: nTDSDSA object for DC to act as the
            IDL_DRSGetNCChanges server (which is in a site other
            than the local DC's site).
        :param rsite: site of the rbh
        :param transport: interSiteTransport object for the transport
            to use for replication traffic.
        :param lbh: nTDSDSA object for DC to act as the
            IDL_DRSGetNCChanges client (which is in the local DC's site).
        :param lsite: site of the lbh
        :param link_opt: Replication parameters (aggregated siteLink options,
                                                 etc.)
        :param link_sched: Schedule specifying the times at which
            to begin replicating.
        :partial_ok: True if bridgehead DCs containing partial
            replicas of the NC are acceptable.
        :param detect_failed: True to detect failed DCs and route
            replication traffic around them, FALSE to assume no DC
            has failed.
        Fc              3   (   K   | ]}|j         |fV  d S ro   r   rq   s     r+   rt   z(KCC.create_connection.<locals>.<genexpr>^  s)      <<a!+q)<<<<<<r-   z
rbhs_all:  c                     g | ]	}|j         
S r/   rq  rq   s     r+   
<listcomp>z)KCC.create_connection.<locals>.<listcomp>a      .M.M.Mqq{.M.M.Mr-   z
lbhs_all: c                     g | ]	}|j         
S r/   rq  rq   s     r+   rt  z)KCC.create_connection.<locals>.<listcomp>m  ru  r-   Nz
rdsa is %sTr   r   r   zround 2: rdsa is %sr&   zvalid connections %dzkept_connections:
znew connection, KCC dsa: %sr   )4rU  dictr"   
DEBUG_GREYr   r   r   r   ry   r:   r   r   r   r   r   r  r|   is_user_owned_scheduleis_equivalent_scheduler  set_modifiedis_override_notify_defaultis_use_notifyr   NTDSSITELINK_OPT_USE_NOTIFYr  r  r  is_twoway_syncNTDSSITELINK_OPT_TWOWAY_SYNCr  !is_intersite_compression_disabled$NTDSSITELINK_OPT_DISABLE_COMPRESSIONr  rH   r   r!   r   r   rE   r   r@   r   rC  r   NTDSCONN_OPT_IS_GENERATEDr    rB   SYSTEM_FLAG_CONFIG_ALLOW_RENAMESYSTEM_FLAG_CONFIG_ALLOW_MOVEnew_connectionr   )rK   r   rbhrsitere   lbhlsitelink_opt
link_schedrX  rY  rbhs_all	rbh_tablelbhs_allldsar   rdsavalid_connectionsoptsystem_flagss                       r+   create_connectionzKCC.create_connectionA  s   6 ++E4,6? ?<<8<<<<<	c(mmmm.M.MH.M.M.M.MO 	P 	P 	P ++E4,6? ?99;; 	!OOC   c(mmmm.M.MH.M.M.M.MO 	P 	P 	P  o	< o	<D(//11 n< n< }}R]33<'t~(EFFF __&& d<,,..d<&).88  6688 .66zBB.&0--- 4466 2''))2 %t'GGAMMJJ"&"K"&">#? !@@JJ OOD111 %t'GGAMMJJ!%!J!%!=">?JJ OOD111 ((** 2 %t'HHQNNJJ4+H*HHJJOOD111 %t'HHQNNJJ$*GGJJOOD111 ;;== 2 &!FGKLM MJJ!%!P PQJJOOD111 &!FGKLM MJJ $ OPJJOOD111 } <

 <, D"K(=(BCCC//
t/DDDD//
;;;]n<b   	2 	2D(//11 2 2 }}R]33<'(=(NOOO
 //++ 2'9>99,,.. : "66t]KK /!66t]KK/)Q.) )--b11192> 	.1BBCCC)>)>@AAA!! 0C
 4;;AAA45 6
 4<<BBt44
 :;?@A AtFF 2T[5JJKKK @ >?L ##Cy$'M:? ?B } 3		 3> 9K 2R 7888&&tzd&;;;;&&tz222 !%%b)))))[ "!r-   c                     g |_         g |_        d}||j        v rt          | j        j                  }|                     |j        |j        | j        |	                                |          }|7|j        
                                r|j                             |           n7d}n4|j                             |           |j                            |           |j                             d           |j                            d           |S )a~  Build a Vertex's transport lists

        Each vertex has accept_red_red and accept_black lists that
        list what transports they accept under various conditions. The
        only transport that is ever accepted is IP, and a dummy extra
        transport called "EDGE_TYPE_ALL".

        Part of MS-ADTS 6.2.2.3.4.3 -- ColorVertices

        :param vertex: the remote vertex we are thinking about
        :param local_vertex: the vertex relating to the local site.
        :param graph: the intersite graph
        :param detect_failed: whether to detect failed links
        :return: True if some bridgeheads were not found
        FNTEDGE_TYPE_ALL)accept_red_redaccept_blackconnected_verticesrZ   r6   r|   r[  r{   r   is_blackis_rodc_siter   )rK   vertexlocal_vertexgraphrY  found_failedt_guidbhs           r+   add_transportszKCC.add_transports=  s   2 !# U---*/00F$$V[&+%)%6%+__%6%6G GB z;++-- ()008888#'LL%,,V444#**6222 	$$_555""?333r-   c                    d}d}t          d|j        d|           t          | j        |          }|                                 |j        D ]0}|                                 |                     ||||          rd}1|                                r||fS t          || j        |j	                  \  }}	t          d|j        |	fz             |	dk    rd}|
                                }
| j        }t          d|z             |D ]{}|j        r|j        d	         j        | j        u r$|j        d	         j        | j        u r|j        d         j        }n|j        d	         j        }|| j        u rt          d
           {|                     ||||
|          }|| j                                        r| j        }| j        }n | j        }|                     ||||
|          }|t%          j        d            dS t          d|d|           t          d|j                   t%          j        d|d|dd           |j        }|d	}d}n|j        }|j        }|                     |||||||||
|
  
         }||fS )a1  Create intersite NTDSConnections as needed by a partition

        Construct an NC replica graph for the NC identified by
        the given crossRef, then create any additional nTDSConnection
        objects required.

        :param graph: site graph.
        :param part: crossRef object for NC.
        :param detect_failed:  True to detect failed DCs and route
            replication traffic around them, False to assume no DC
            has failed.

        Modifies self.kept_connections by adding any connections
        deemed to be "in use".

        :return: (all_connected, found_failed_dc)
        (all_connected) True if the resulting NC replica graph
            connects all sites that need to be connected.
        (found_failed_dc) True if one or more failed DCs were
            detected.
        TFz$create_connections(): enter
	partdn=z
	detect_failed=)rH  z%s Number of components: %dr&   zedge_list %sr   zrsite is my_siteNzDISASTER! lbh is None)FTzlsite: z
rsite: z	vertices zbridgeheads

zF----------------------------------------------------------------------)r    r'  r   rD   color_vertexrM  r  is_whiter   r   r  r6   r   rG  r{   r[  rB   r   r"   rC  
DEBUG_BLUE	site_linkr  r  r  )rK   r  r   rY  r  r  	my_vertexv	edge_listn_componentsrX  re   r   r  r  r  r  rk   r  r  s                       r+   create_connectionszKCC.create_connectionso  s.   , ---0 	1 	1 	1 4<..	    	$ 	$ANN""1iFF $#  	/ ,.."9%:>,@D#N #N #N	< 	.-./ 	0 	0 	0!!M ''))
 %	ny())) 6	> 6	>Az ajm0DL@@z!}!T\11
1*
1* $$())) %%eT9&0-A AC{ {  "" Ek))%y*4mE E { 7888"{{Huuuee<===Hajj2333###sssHHMNNN{H!

#+%.
""4eY#&x#-}> > > > l**r-   c                    d}t                      | _        | j                                        D ]}|                                s|                                r,|                     |          }d}|                     ||d          \  }}t          d|d|           |sd}|r|                     ||d           |S )a  Create NTDSConnections as necessary for all partitions.

        Computes an NC replica graph for each NC replica that "should be
        present" on the local DC or "is present" on any DC in the same site
        as the local DC. For each edge directed to an NC replica on such a
        DC from an NC replica on a DC in another site, the KCC creates an
        nTDSConnection object to imply that edge if one does not already
        exist.

        Modifies self.kept_connections - A set of nTDSConnection
        objects for edges that are directed
        to the local DC's site in one or more NC replica graphs.

        :return: True if spanning trees were created for all NC replica
                 graphs, otherwise False.
        TFzwith detect_failed: connected z Found failed )	r>   r@   r4   ry   r  
is_foreignr   r  r   )rK   r  r   r  r  	connecteds         r+   create_intersite_connectionsz KCC.create_intersite_connections  s    "  # O**,, 	@ 	@D??$$    $$T**E !L&*&=&=e>BD'J 'J#I| E99ll, - - - @ % @ ++E4???r-   c                    | j         }| j        }d}t          d           | j        r|                    | j        |d           n|                    | j        |d           |                                rt          d|z             |S |                                st          d|z             |S |                     |           | 	                                }t          d|z             |S )am  Generate the inter-site KCC replica graph and nTDSConnections

        As per MS-ADTS 6.2.2.3.

        If self.readonly is False, the connections are added to self.samdb.

        Produces self.kept_connections which is a set of NTDS
        Connections that should be kept during subsequent pruning
        process.

        After this has run, all sites should be connected in a minimum
        spanning tree.

        :param ping: An oracle function of remote site availability
        :return (True or False):  (True) if the produced NC replica
            graph connects all sites that need to be connected
        Tzintersite(): enterr   Fz+intersite(): exit disabled all_connected=%dz+intersite(): exit not istg all_connected=%dz"intersite(): exit all_connected=%d)
rB   rD   r    rH   select_istgrE   is_intersite_topology_disabledr  rD  r  )rK   r   r   mysiter  s        r+   	intersitezKCC.intersite%  s   ( %&&& = 	<tz5T::::tz5U;;; 0022 	!B"# $ $ $  }} 	!B"# $ $ $  %%% 99;;5EFFFr-   Tc                 X   | j                                         sdS | j         j                                        }d |D             fd|D             }|rQrQD ])}|d         }|j        |_        |j        |_        d|_        *| j                             | j        |           dS dS dS )zoUpdates the RODC NTFRS connection object.

        If the local DSA is not an RODC, this does nothing.
        Nc                 :    g | ]}|                                 |S r/   )r   rq   s     r+   rt  z.KCC.update_rodc_connection.<locals>.<listcomp>t  s)    MMM8J8J8L8LM!MMMr-   c                     g | ]}|v|	S r/   r/   )rr   rs   ro_connectionss     r+   rt  z.KCC.update_rodc_connection.<locals>.<listcomp>u  s-     6 6 6n44 444r-   r   Tr   )	rB   r   r   ry   r   r  r   r   rE   )rK   r   all_connectionsrw_connectionsconr   r  s         @r+   update_rodc_connectionzKCC.update_rodc_connection`  s    
 {  "" 	F +3::<<MM_MMM6 6 6 6_ 6 6 6  	>n 	>% * *$Q'!$"|%)""K**4:"*=====	> 	> 	> 	>r-   c                 ^    d}	 |d||z  z  d|z  z   dz   k    rn|dz   }|dz   }|dk     r|S dS )am  Find the maximum number of edges directed to an intrasite node

        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)

        (If the number of edges is m (i.e. n + 2), that is the same as
        2 * m*m - 2 * m + 3). We think in terms of n because that is
        the number of extra connections over the double directed ring
        that exists by default.

        edges  n   nodecount
          2    0    7
          3    1   15
          4    2   27
          5    3   43
                  ...
         50   48 4903

        :param node_count: total number of nodes in the replica graph

        The intention is that there should be no more than 3 hops
        between any two DSAs at a site. With up to 7 nodes the 2 edges
        of the ring are enough; any configuration of extra edges with
        8 nodes will be enough. It is less clear that the 3 hop
        guarantee holds at e.g. 15 nodes in degenerate cases, but
        those are quite unlikely given the extra edges are randomly
        arranged.

        :param node_count: the number of nodes in the site
        "return: The desired maximum number of connections
        r   Tr         r&   2   r/   )rK   
node_countns      r+   intrasite_max_node_edgeszKCC.intrasite_max_node_edges  s_    F 	a1q5kQU3a788AA	 Er66Hrr-   c                    "# |                     |          \  }}}t          j        dd|z  z   d|z  z   d|z  z   d|z  z   d|z  z   d|z  z              |st          j        d|j        z             d	S t          ||j                  }	|	                     j                   ||	_        ||	_	        |
                    |	           g }
 j        j                                        D ]}|j        |j        vr|j        |j                 }|                                r8|                                sM|                                s||u rf|r|                                s}|r7|s5|j        t&          j        k    r |                    t,          j                  s|r                     |          r|
                    |           |rɉ j        j                                        D ]}|j        |j        vr|j        |j                 }|                                s8|                                sM|                                s||u rf|r|                                s}|r                     |          r|
                    |           |
                    |	           |
                    d
            t9          |
          }                     |          }g }|
D ],}t=          |j        |          }|                    |           -d}||dz
  k     r|
|                                         r|
|dz                                            r)||dz                                 |
|         j                   |
|dz                                            r|
|                                         r)||                              |
|dz            j                   |dz   }||dz
  k     |
|dz
                                           r|
d                                         r)|d                              |
|dz
           j                   |
d                                         r|
|dz
                                           r)||dz
                                |
d         j                   tC          dt9          |
          z             tC          d"                    d |
D                                   j#        d	uo j        } j$        s|r)g }tK                      }|D ]W}|&                    |j'                   |j(        D ]3}|                    ||j'        f           |&                    |           4Xd}tS          d|||j*        dtV          |j                 d|j        |tB           j$         j#        d	  	         tK           fd|D                       ""fd|D             }d}tS          d|"|j*        dtV          |j                 d|j        |tB           j$         j#        d	  	         |D ]_} j        j        |j'                 }|j,                                        D ],}|j-        }| j        j        v r|                     |           -`tC          dd"                    d |
D                       z             tC          dd"                    d |D                       z             |D ]#|dk    r#.                                s#fd |D             } t          j/        d!#j'        |t9          |          t9          |           fz             tC          d"d# | D             z             | r#.                                sta          j1        |           }!tC          d$|!j'        z             #                     |!j'                  st          j        d%|!j'        z             | 2                    |!           | r#.                                n7tg          d&#j'        d'|d(t9          #j(                  d)#j4                   tg          d*#z             #j'        |j'        k    r#5                    | j6                    j$        s|r+g }tK                      }|D ]W}|&                    |j'                   |j(        D ]3}|                    ||j'        f           |&                    |           4Xd}tS          d+|||j*        dtV          |j                 d|j        |tB           j$         j#        d	  	         tK           fd,|D                       ""fd-|D             }d}tS          d.|"|j*        dtV          |j                 d|j        |tB           j$         j#        d	  	         d	S d	S )/aT  Create an intrasite graph using given parameters

        This might be called a number of times per site with different
        parameters.

        Based on [MS-ADTS] 6.2.2.2

        :param site_local: site for which we are working
        :param dc_local: local DC that potentially needs a replica
        :param nc_x:  naming context (x) that we are testing if it
                    "should be present" on the local DC
        :param gc_only: Boolean - only consider global catalog servers
        :param detect_stale: Boolean - check whether links seems down
        :return: None
        z"construct_intrasite_graph(): enterz
	gc_only=%dz
	detect_stale=%dz
	needed=%sz
	ro=%sz
	partial=%sz
%szH%s lacks 'should be present' status, aborting construct_intrasite_graph!Nc                 *    t          | j                  S ro   )r   rep_dsa_guid)rl  s    r+   <lambda>z/KCC.construct_intrasite_graph.<locals>.<lambda>  s    HS-=$>$> r-   r]  r   r&   zr_list is length %sr  c              3   L   K   | ]}t          |j        |j        f          V   d S ro   )rZ   r  rep_dsa_dnstrrq   s     r+   rt   z0KCC.construct_intrasite_graph.<locals>.<genexpr>  sG       ) ) Q^Q_=>> ) ) ) ) ) )r-   r  intrasite_pre_ntdscon__T)rH  rI  r"   rI   rJ   rG  c              3   j   K   | ]-}                     |                                          )|V  .d S ro   r<   r   rr   rs   rK   s     r+   rt   z0KCC.construct_intrasite_graph.<locals>.<genexpr>  Z       "B "B)-a)>)>)@)@"B! "B "B "B "B "B "Br-   c                 .    g | ]\  }}|v 	|v ||fS r/   r/   rr   r   r   rw_dot_verticess      r+   rt  z1KCC.construct_intrasite_graph.<locals>.<listcomp>  B     K K Ktq!00Q/5I5I F5I5I5Ir-   )r  directed_double_ring_or_smallintrasite_rw_pre_ntdsconzreps are:  %sz   c              3   $   K   | ]}|j         V  d S ro   )r  rq   s     r+   rt   z0KCC.construct_intrasite_graph.<locals>.<genexpr>  s$      *K*Kq1?*K*K*K*K*K*Kr-   zdsas are:  %sc              3   $   K   | ]}|j         V  d S ro   rq  rq   s     r+   rt   z0KCC.construct_intrasite_graph.<locals>.<genexpr>  s$      *K*K11;*K*K*K*K*K*Kr-      c                 8    g | ]}|ur|j         j        v|S r/   )r   	edge_from)rr   rs   tnodes     r+   rt  z1KCC.construct_intrasite_graph.<locals>.<listcomp>  s=     D D DA ~~ {%/AA  AAAr-   zDlooking for random link for %s. r_len %d, graph len %d candidates %dzcandidates %sc                     g | ]	}|j         
S r/   rq  rq   s     r+   rt  z1KCC.construct_intrasite_graph.<locals>.<listcomp>  s    (I(I(I(I(I(Ir-   ztrying to add candidate %szcould not add %sznot adding links to z: nodes z, links is /z%sintrasite_post_ntdsconc              3   j   K   | ]-}                     |                                          )|V  .d S ro   r  r  s     r+   rt   z0KCC.construct_intrasite_graph.<locals>.<genexpr>'	  r  r-   c                 .    g | ]\  }}|v 	|v ||fS r/   r/   r  s      r+   rt  z1KCC.construct_intrasite_graph.<locals>.<listcomp>)	  r  r-   intrasite_rw_post_ntdscon)7ra  r"   ri  rC  r'  r   identify_by_basednrE   rep_partialrep_roadd_needed_replicarD   rx   ry   r9  r   r(  r'   nc_typer   domainrc  r   rd  r   r   r)  rf  r   r  r   r  add_edge_fromr   joinrJ   rI   r>   r   r   r  r   rN  r   r   r   has_sufficient_edgesr  rg  choiceremover    	max_edgesadd_connections_from_edgesr6   )$rK   
site_localdc_localnc_xgc_onlydetect_staler   r   rk  l_of_xr_listdc_sf_of_xp_of_xr_lenmax_node_edges
graph_listrl  nodeido_dot_filesrQ  dot_verticesv1v2rS  rw_dot_edgesrw_verify_propertiesr  r   r   remote
candidatesotherr  r  s$   `                                 @@r+   construct_intrasite_graphzKCC.construct_intrasite_graph  s   F #44X>>G?+g560<?@ +V34 '+	,
 ,g56 "D=) 	* 	* 	*  	O B M* + + + F 8T]33!!$*---$ 	##F+++0  L*1133 >	" >	"D }D$::: +DM:F ||~~ 
 $$&& 
 zz|| tx//
  tzz|| (  ' dlfm&C&C//0LMM 
   = =d C C  MM&!!!!  .	& .5577 *& *&
 =(>>> /> ((** 
 ((** 
 ::<< 48#3#3
  4::<< 
   D$A$A$$G$G  f%%%% 	f>>???F66u== 
 	$ 	$CS.??Dd#### 519oo !9'')) IVAqD\-D-D-F-F I1q5!//q	0GHHH !a%=++-- I1E1E1G1G I1++F1q5M,GHHHAA 519oo eai ++-- 	I1E1E1G1G 	IqM''uqy(9(GHHH ay##%% 	Iq)9)D)D)F)F 	Iuqy!//q	0GHHH#c&kk1222dii ) )!') ) ) ) ) 	* 	* 	* (4C; !	*, !	*I55L  ) )  ..., ) )B$$b",%7888 $$R(((() !/2I|1;1F1F1F1;DL1I1I1I15"@ '8u"&+(,(9$(* * * * " "B "B "B "B\ "B "B "B B BOK K K Ky K K KL$E 5|*1;1F1F1F1;DL1I1I1I15"@ ';%"&+(,(9$(* * * * ! 	1 	1F,()9:C,3355 1 1 +T\333((0001
 	o

*K*KF*K*K*K K KKLLLo

*K*K
*K*K*K K KKLLL ,	N ,	NE zz%"<"<">">zD D D D D D D
   ">$)OUC
OO$'
OO$5"5 6 6 6
 o(I(Ij(I(I(IIJJJ  -)C)C)E)E -"M*55E6HIII ..u?? N(:U_(LMMM%%e,,, ! -)C)C)E)E - ///555#eo2F2F2F2F//+ , , ,
 TE\"""
 ("444004;LMMM; !	*, !	*I55L  ) )  ..., ) )B$$b",%7888 $$R(((() !/3Y1;1F1F1F1;DL1I1I1I15"@ '8u"&+(,(9$(* * * * " "B "B "B "B\ "B "B "B B BOK K K Ky K K KL$E 6*1;1F1F1F1;DL1I1I1I15"@ ';%"&+(,(9$(* * * * * *3!	* !	*r-   c                    | j         }t          d           | j        }|                                rdS |                                 }|j                                        D ] }|j        rt          j	        d|z             !| j
                                        D ]X\  }}|                     |||d|           |j                                        D ] }|j        rt          j        d|z             !Y|j                                        D ] }|j        rt          j        d|z             !| j
                                        D ]2\  }}|                                r|                     |||d|           3|j                                        D ] }|j        rt          j        d|z             !| j
                                        D ]\  }}|                     |||dd           |j                                        D ] }|j        rt          j        d|z             !| j
                                        D ]2\  }}|                                r|                     |||dd           3|                     |           dS )a  Generate the intrasite KCC connections

        As per MS-ADTS 6.2.2.2.

        If self.readonly is False, the connections are added to self.samdb.

        After this call, all DCs in each site with more than 3 DCs
        should be connected in a bidirectional ring. If a site has 2
        DCs, they will bidirectionally connected. Sites with many DCs
        may have arbitrary extra connections.

        :return: None
        zintrasite(): enterNr   FT)rB   r    rD   is_intrasite_topology_disabledis_detect_stale_disabledr   ry   r   r"   
DEBUG_CYANr4   itemsr  r  ri  	is_configrC  r   )rK   r   r  r  r   partdnr   s          r+   	intrasitezKCC.intrasite7	  s    %&&& 0022 	F";;===*1133 	? 	?G" ? !3g!=>>> !O1133 	C 	CLFD**65$+79 9 9 .5577 C C& C$%7'%ABBBC *1133 	A 	AG" A"#5#?@@@ !O1133 	= 	=LFD~~ =..vudD/;= = = *1133 	? 	?G" ? !3g!=>>> !O1133 	2 	2LFD**65$+02 2 2 2 *1133 	> 	>G" > 2W <=== O1133 	6 	6LFD~~ 6..vudD/46 6 6 	U#####r-   c                    |                                   |                                  |                                  |                                  |                                  |                                  g }| j                                        D ]8}|                    d |j	                                        D                        9|S )a  Compile a comprehensive list of DSA DNs

        These are all the DSAs on all the sites that KCC would be
        dealing with.

        This method is not idempotent and may not work correctly in
        sequence with KCC.run().

        :return: a list of DSA DN strings.
        c                 F    g | ]}|j                             d dd          S )zCN=NTDS Settings,r   r&   )r   replace)rr   r   s     r+   rt  z!KCC.list_dsas.<locals>.<listcomp>	  s=     = = =  ../BBJJ = = =r-   )
r   r   r   r   rf   rl   r5   ry   extendrx   )rK   dsasr{   s      r+   	list_dsaszKCC.list_dsas	  s     	  """   !!!O**,, 	> 	>DKK = =$(N$9$9$;$;= = = > > > >r-   c                     |s| j         ]	 t          |t                      ||          | _         dS # t          j        $ r$}|j        \  }}t          d|d|          d}~ww xY wdS )aS  Load the database using an url, loadparm, and credentials

        If force is False, the samdb won't be reloaded if it already
        exists.

        :param dburl: a database url.
        :param lp: a loadparm object.
        :param creds: a Credentials object.
        :param force: a boolean indicating whether to overwrite.

        N)urlsession_infocredentialslpzUnable to open sam database z : )rE   r
   r	   r   rX   rY   r   )rK   dburlr  credsforcee1numrc   s           r+   
load_samdbzKCC.load_samdb	  s      	-DJ&-"u0>0@0@/4= = =


 < - - -W
ch %ss , - - -- '&s   $1 A$ AA$r/   c                 r   |o| j         }|s	| j        dS g }g }g }g }| j                                        D ]}|                    |j                   |                                r|                    d           n|                    d           |j                                        D ]b}	|	                                r|                    d           n|                    d           |                    |	j	        |j        f           ct          |||| j        |t          || j        d||           dS )zHelper function to plot and verify NTDSConnections

        :param basename: an identifying string to use in filenames and logs.
        :param verify_properties: properties to verify (default empty)
        Nz#cc0000z#0000ccredblueT)	rM  rH  rI  r"   rI   rJ   rG  edge_colorsvertex_colors)rI   rJ   r8   ry   r   r   r   r   r   r   r   rA   r   )
rK   basenamerS  rI   rQ  r  edge_coloursvertex_coloursr   r  s
             r+   plot_all_connectionszKCC.plot_all_connections	  ss    #2t{ 	$+3F	$++-- 	B 	BC...yy{{ 1%%i0000%%i000(//11 B B'')) 0 ''.... ''///  #.#-!@AAAAB 	x\!."35$43D $,%3	5 	5 	5 	5 	5 	5r-   c                    "  j         +t          d|                                |||d           |r j                             d|z             	                                                                                                                                                                            	                                  j
        s j        i } j                                        D ]8"|                    d "j                                        D                        9                     d           g }	 j                                        \  }
}|
                                D ]8\  }}t)          d|z             |	                     j        j        |f           9t/          d	|	d
 j        dt(           j
         j                   g }	 j                                        D ]""j                                        D ]}|                                \  }
}|
                                D ]Y\  }}|j        D ]L}t)          d|z             t5          |j                  }||         }|	                    |j        |f           MZt/          d|	d
 j        dt(           j
         j                   g }	g } j                                        D ]}ddlm} |j                             d          }d ||          !                                dd         z   }tE          j#        |j$        d          D ]=\  }}|	                    |d         |d         f           |                    |           >d}t/          d|	d j        |t(           j
         j        |	  	         |rm j%        j                                        D ]9}tM           fd|j'                                        D                       |_'        :                     d           |r j                                        D ]V""j                                        D ]:}tM           "fd|j'                                        D                       |_'        ;W                     d           |rd }nd} (                    |            )                                  *                    |          } +                    |            ,                                  -                                  .                                  j
        s j                             dd           t_          j0        dtc          |          z             g }	g } j        j        } j                                        \  }
}|                                D ]r\  }} | j        D ]e}t5          |j                  }!|	                    |||!         f           |                    dt5          | j2                  dd         z              fst/          d |	d
 j        dt(           j
         j        |	  	         g }	 j                                        D ]""j                                        D ]r}|                                \  }
}|                                D ]D} | j        D ]:}t5          |j                  }||         }|	                    |j        |f           ;Est/          d!|	d
 j        dt(           j
         j                   n#   xY wdS )"a  Perform a KCC run, possibly updating repsFrom topology

        :param dburl: url of the database to work with.
        :param lp: a loadparm object.
        :param creds: a Credentials object.
        :param forced_local_dsa: pretend to be on the DSA with this dn_str
        :param forget_local_links: calculate as if no connections existed
               (boolean, default False)
        :param forget_intersite_links: calculate with only intrasite connection
               (boolean, default False)
        :param attempt_live_connections: attempt to connect to remote DSAs to
               determine link availability (boolean, default False)
        :return: 1 on error, 0 otherwise
        Nz"samdb is None; let's load it from F)r!  zCN=NTDS Settings,%sc              3   H   K   | ]\  }}t          |j                  |fV  d S ro   rp   )rr   rd   r   s      r+   rt   zKCC.run.<locals>.<genexpr>	  sQ       )D )D-7UC +.cl*;*;U)C )D )D )D )D )D )Dr-   dsa_initialzc_rep %sdsa_repsFrom_initialTr/   rF  zrep %sdsa_repsFrom_initial_allr   )md5r   #r  r   r&   r  dsa_sitelink_initial)rG  rH  rI  r"   rI   rJ   r(  c              3   t   K   | ]2\  }}|                                 s|j        j        j        v,||fV  3d S ro   )r   r   rD   rx   )rr   kr  rK   s      r+   rt   zKCC.run.<locals>.<genexpr>2
  sv       -G -G1010B0B0D0D-G /0l.2l.D/E /E /0V/E /E /E /E-G -Gr-   dsa_forgotten_localc              3   `   K   | ](\  }}j         u |                                "||fV  )d S ro   )rD   r   )rr   r7  r  rK   r{   s      r+   rt   zKCC.run.<locals>.<genexpr><
  s\       1G 1GDAq48DL4H4H121C1C1E1E 5I 34Q4H4H4H4H1G 1Gr-   dsa_forgotten_allc                 v    	 t          j        || j        | j                   n# t           j        $ r Y dS w xY wdS )NFT)r   drsuapi_connectr  r   drsException)rK   dnsnames     r+   r   zKCC.run.<locals>.pingF
  sM    %!1'47DJOOOO$1 % % %$uu%4s    # 66	dsa_finalzthere are %d dsa guidsdsa_repsFrom_finaldsa_repsFrom_final_all)3rE   r    r$  set_ntds_settings_dnr   r   r   r   rf   rl   rI   rJ   r5   ry   rw   rx   r  r-  rB   r   r   r   r   r   rA   r   rZ   r   r7   hashlibr3  rd   encode	hexdigestr   rL  	site_listrD   rw  r   r   r  r  r  rA  r   r  r"   rV  r   nc_guid)#rK   r  r  r   forced_local_dsaforget_local_linksforget_intersite_linksattempt_live_connectionsguid_to_dnstrrQ  current_repsneeded_repsrd   r<  r   rz   rl  r   r(   dsa_dndot_colourslinkr3  tmp_strcolourr   r   rI  r   r  r(  my_dnstrr  guid_strr{   s#   `                                 @r+   runzKCC.run	  s   " :HuuFGGGOOE2uEO::: 	>J++,A,<-= > > >]	!!!$$&&&""$$$##%%%{ 38d/; " O2244 D DD!(( )D )D,0N,@,@,B,B)D )D )D D D D D ))-888	,0K,F,F,H,H)k$0$6$6$8$8 E ELE5*u,---$$dk&;U%CDDDD5y(,D4E*,E$+,0,=? ? ? ?
 	 O2244 J JD#~4466 J J474F4F4H4H1k+7+=+=+?+? J JKFC-0-= J J	 %hn 5 5 5+.y/L+M+M)6x)@ ) 0 0#-1H I I I I	JJJ 99(,D4E*,E$+,0,=? ? ? ?
 	  /6688 3 3D++++++"j//77G 33w<<#9#9#;#;BQB#??F ) 6t~q I I 3 31!((!A$!666#**622223 ,
5y(-%)%6:%*4;,0,=+68 8 8 8 " A<188:: G GC(, -G -G -G -G-0->-D-D-F-F-G -G -G )G )GC%%
 ))*?@@@% ? O2244 G GD#~4466 G G,0 1G 1G 1G 1G 1G141B1H1H1J1J1G 1G 1G -G -G))G ))*=>>>' 
        
 11$777 NN !NN400M ))-888 ##%%% 99;;; ''))){ %?d/;))+*8: : : #$<$'$6$6%7 8 8 8 	 ;0,0K,F,F,H,H)k$/$5$5$7$7 I ILE5%*%7 I I	#&y'D#E#E!(((M(4K)LMMM#**3U]1C1CBQB1G+GHHHHI
 3Y%)%6*,E$+,0,=+6	8 8 8 8 	 O2244 J JD#~4466 J J474F4F4H4H1k%0%7%7%9%9 J JE-2-? J J	+.y/L+M+M)6x)@ ) 0 0#-1H I I I IJJJ 7(,D4E*,E$+,0,=? ? ? ?
	qs   [(\> >]c                     	 t          j        ||||          | _        n1# t           j        $ r}t	          j        |           Y d}~dS d}~ww xY wdS )aI  Import relevant objects and attributes from an LDIF file.

        The point of this function is to allow a programmer/debugger to
        import an LDIF file with non-security relevent information that
        was previously extracted from a DC database.  The LDIF file is used
        to create a temporary abbreviated database.  The KCC algorithm can
        then run against this abbreviated database for debug or test
        verification that the topology generated is computationally the
        same between different OSes and algorithms.

        :param dburl: path to the temporary abbreviated db to create
        :param lp: a loadparm object.
        :param ldif_file: path to the ldif file to import
        :param forced_local_dsa: perform KCC from this DSA's point of view
        :return: zero on success, 1 on error
        Nr&   r   )r   ldif_to_samdbrE   	LdifErrorr!   critical)rK   r  r  	ldif_filerH  r   s         r+   import_ldifzKCC.import_ldif
  sn    "	+9%Y:JL LDJJ!+ 	 	 	OA11111	 qs    AAAc                     	 t          j        | j        ||||           n1# t           j        $ r}t	          j        |           Y d}~dS d}~ww xY wdS )a-  Save KCC relevant details to an ldif file

        The point of this function is to allow a programmer/debugger to
        extract an LDIF file with non-security relevent information from
        a DC database.  The LDIF file can then be used to "import" via
        the import_ldif() function this file into a temporary abbreviated
        database.  The KCC algorithm can then run against this abbreviated
        database for debug or test verification that the topology generated
        is computationally the same between different OSes and algorithms.

        :param dburl: LDAP database URL to extract info from
        :param lp: a loadparm object.
        :param cred: a Credentials object.
        :param ldif_file: output LDIF file name to create
        :return: zero on success, 1 on error
        Nr&   r   )r   samdb_to_ldif_filerE   rY  r!   rZ  )rK   r  r  r   r[  r   s         r+   export_ldifzKCC.export_ldif
  st    "	1$*eR2;= = = =!+ 	 	 	OA11111	 qs     AA		A)FFFNro   )T)F)r/   )NFFF),__name__
__module____qualname____doc__rL   rf   rl   ru   r   r   r   r   r   r   r   r   r   r   r   r   r  r$  r*  rA  rD  r   r[  rU  r   r  r  r  r  r  r  r  r  r  r  r$  r-  rV  r\  r_  r/   r-   r+   r2   r2   N   s         FK"() () () ()T!G !G !GF2 2 2<% % %.	: 	: 	:$ $ $&8F 8F 8Ft, , ,<2K 2K 2K 2Kh  B  8 8 8$- - -2- 2- 2-h74 74 74r/ / /"& & &$M; M; M;^% % %Nz0 z0 z0 z0~K K K K2& & &P  <W W Wr12 12 12fz* z* z*x0 0 0dz+ z+ z+x8 8 8t7 7 7v"> "> "> ">H+ + +ZD* D* D*LN$ N$ N$`  0- - - -,!5 !5 !5 !5F 6:=B%*x x x xt   2    r-   r2   )4rg  r   	functoolsr   r   sambar   r   r   r   r   
samba.authr	   samba.samdbr
   samba.dcerpcr   r   samba.kcc.kcc_utilsr   r   r   r   r   r   r   r   r   r   r   samba.kcc.graphr   	samba.ndrr   samba.kcc.graph_utilsr   	samba.kccr   r   r   r   samba.kcc.debugr   r    r!   r"   samba.commonr#   r,   r0   objectr2   r/   r-   r+   <module>rq     s"  ,                   * * * * * * * * & & & & & & & & & & % % % % % %       & & & & & & & & D D D D D D D D D D D D H H H H H H H H H H H H E E E E E E E E E E 9 9 9 9 9 9       0 0 0 0 0 0 ( ( ( ( ( ( @ @ @ @ @ @ @ @ " " " " " " 3 3 3 3 3 3 3 3 3 3            A A A"  t) t) t) t) t)& t) t) t) t) t)r-   