1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-08 21:18:16 +03:00

KCC: shift samba.kcc intersite functions to samba.kcc.graph

So samba.kcc.graph is the place to look for everything intersite.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Douglas Bagnall 2015-06-10 16:42:54 +12:00 committed by Andrew Bartlett
parent b0e6a74362
commit 427d05d1ac
2 changed files with 454 additions and 435 deletions

View File

@ -61,6 +61,7 @@ from samba.kcc.kcc_utils import convert_schedule_to_repltimes
from samba.kcc.graph_utils import verify_and_dot
from samba import ldif_utils
from samba.kcc.graph import setup_graph, get_spanning_tree_edges
from samba.kcc.graph import Vertex
from samba.kcc.debug import DEBUG, DEBUG_FN, logger
@ -1186,52 +1187,16 @@ class KCC(object):
:param part: a Partition object
:returns: an InterSiteGraph object
"""
guid_to_vertex = {}
# Create graph
g = IntersiteGraph()
# Add vertices
for site_guid, site in self.site_table.items():
vertex = Vertex(site, part)
vertex.guid = site_guid
vertex.ndrpacked_guid = ndr_pack(site.site_guid)
g.vertices.add(vertex)
# If 'Bridge all site links' is enabled and Win2k3 bridges required
# is not set
# NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
# No documentation for this however, ntdsapi.h appears to have:
# NTDSSETTINGS_OPT_W2K3_BRIDGES_REQUIRED = 0x00001000
bridges_required = self.my_site.site_options & 0x00001002 == 0
if not guid_to_vertex.get(site_guid):
guid_to_vertex[site_guid] = []
g = setup_graph(part, self.site_table, self.transport_table,
self.sitelink_table, bridges_required)
guid_to_vertex[site_guid].append(vertex)
connected_vertices = set()
for transport_guid, transport in self.transport_table.items():
# Currently only ever "IP"
if transport.name != 'IP':
DEBUG_FN("setup_graph is ignoring transport %s" %
transport.name)
continue
for site_link_dn, site_link in self.sitelink_table.items():
new_edge = create_edge(transport_guid, site_link,
guid_to_vertex)
connected_vertices.update(new_edge.vertices)
g.edges.add(new_edge)
# If 'Bridge all site links' is enabled and Win2k3 bridges required
# is not set
# NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
# No documentation for this however, ntdsapi.h appears to have:
# NTDSSETTINGS_OPT_W2K3_BRIDGES_REQUIRED = 0x00001000
if (((self.my_site.site_options & 0x00000002) == 0
and (self.my_site.site_options & 0x00001000) == 0)):
g.edge_set.add(create_auto_edge_set(g, transport_guid))
else:
# TODO get all site link bridges
for site_link_bridge in []:
g.edge_set.add(create_edge_set(g, transport_guid,
site_link_bridge))
g.connected_vertices = connected_vertices
#be less verbose in dot file output unless --debug
do_dot_files = opts.dot_files and opts.debug
dot_edges = []
for edge in g.edges:
for a, b in itertools.combinations(edge.vertices, 2):
@ -2753,394 +2718,3 @@ class KCC(object):
print e
return 1
return 0
def get_spanning_tree_edges(graph, my_site, label=None):
# Phase 1: Run Dijkstra's to get a list of internal edges, which are
# just the shortest-paths connecting colored vertices
internal_edges = set()
for e_set in graph.edge_set:
edgeType = None
for v in graph.vertices:
v.edges = []
# All con_type in an edge set is the same
for e in e_set.edges:
edgeType = e.con_type
for v in e.vertices:
v.edges.append(e)
if opts.verify or opts.dot_files:
graph_edges = [(a.site.site_dnstr, b.site.site_dnstr)
for a, b in
itertools.chain(
*(itertools.combinations(edge.vertices, 2)
for edge in e_set.edges))]
graph_nodes = [v.site.site_dnstr for v in graph.vertices]
if opts.dot_files and opts.debug:
write_dot_file('edgeset_%s' % (edgeType,), graph_edges,
vertices=graph_nodes, label=label)
if opts.verify:
verify_graph('spanning tree edge set %s' % edgeType,
graph_edges, vertices=graph_nodes,
properties=('complete', 'connected'),
debug=DEBUG)
# Run dijkstra's algorithm with just the red vertices as seeds
# Seed from the full replicas
dijkstra(graph, edgeType, False)
# Process edge set
process_edge_set(graph, e_set, internal_edges)
# Run dijkstra's algorithm with red and black vertices as the seeds
# Seed from both full and partial replicas
dijkstra(graph, edgeType, True)
# Process edge set
process_edge_set(graph, e_set, internal_edges)
# All vertices have root/component as itself
setup_vertices(graph)
process_edge_set(graph, None, internal_edges)
if opts.verify or opts.dot_files:
graph_edges = [(e.v1.site.site_dnstr, e.v2.site.site_dnstr)
for e in internal_edges]
graph_nodes = [v.site.site_dnstr for v in graph.vertices]
verify_properties = ('multi_edge_forest',)
verify_and_dot('prekruskal', graph_edges, graph_nodes, label=label,
properties=verify_properties, debug=DEBUG,
verify=opts.verify,
dot_files=opts.dot_files)
# Phase 2: Run Kruskal's on the internal edges
output_edges, components = kruskal(graph, internal_edges)
# This recalculates the cost for the path connecting the
# closest red vertex. Ignoring types is fine because NO
# suboptimal edge should exist in the graph
dijkstra(graph, "EDGE_TYPE_ALL", False) # TODO rename
# Phase 3: Process the output
for v in graph.vertices:
if v.is_red():
v.dist_to_red = 0
else:
v.dist_to_red = v.repl_info.cost
if opts.verify or opts.dot_files:
graph_edges = [(e.v1.site.site_dnstr, e.v2.site.site_dnstr)
for e in internal_edges]
graph_nodes = [v.site.site_dnstr for v in graph.vertices]
verify_properties = ('multi_edge_forest',)
verify_and_dot('postkruskal', graph_edges, graph_nodes,
label=label, properties=verify_properties,
debug=DEBUG, verify=opts.verify,
dot_files=opts.dot_files)
# Ensure only one-way connections for partial-replicas,
# and make sure they point the right way.
edge_list = []
for edge in output_edges:
# We know these edges only have two endpoints because we made
# them.
v, w = edge.vertices
if v.site is my_site or w.site is my_site:
if (((v.is_black() or w.is_black()) and
v.dist_to_red != MAX_DWORD)):
edge.directed = True
if w.dist_to_red < v.dist_to_red:
edge.vertices[:] = w, v
edge_list.append(edge)
if opts.verify or opts.dot_files:
graph_edges = [[x.site.site_dnstr for x in e.vertices]
for e in edge_list]
#add the reverse edge if not directed.
graph_edges.extend([x.site.site_dnstr
for x in reversed(e.vertices)]
for e in edge_list if not e.directed)
graph_nodes = [x.site.site_dnstr for x in graph.vertices]
verify_properties = ()
verify_and_dot('post-one-way-partial', graph_edges, graph_nodes,
label=label, properties=verify_properties,
debug=DEBUG, verify=opts.verify,
directed=True,
dot_files=opts.dot_files)
# count the components
return edge_list, components
def create_edge(con_type, site_link, guid_to_vertex):
e = MultiEdge()
e.site_link = site_link
e.vertices = []
for site_guid in site_link.site_list:
if str(site_guid) in guid_to_vertex:
e.vertices.extend(guid_to_vertex.get(str(site_guid)))
e.repl_info.cost = site_link.cost
e.repl_info.options = site_link.options
e.repl_info.interval = site_link.interval
e.repl_info.schedule = convert_schedule_to_repltimes(site_link.schedule)
e.con_type = con_type
e.directed = False
return e
def create_auto_edge_set(graph, transport):
e_set = MultiEdgeSet()
# use a NULL guid, not associated with a SiteLinkBridge object
e_set.guid = misc.GUID()
for site_link in graph.edges:
if site_link.con_type == transport:
e_set.edges.append(site_link)
return e_set
def create_edge_set(graph, transport, site_link_bridge):
# TODO not implemented - need to store all site link bridges
e_set = MultiEdgeSet()
# e_set.guid = site_link_bridge
return e_set
def setup_vertices(graph):
for v in graph.vertices:
if v.is_white():
v.repl_info.cost = MAX_DWORD
v.root = None
v.component_id = None
else:
v.repl_info.cost = 0
v.root = v
v.component_id = v
v.repl_info.interval = 0
v.repl_info.options = 0xFFFFFFFF
v.repl_info.schedule = None # TODO highly suspicious
v.demoted = False
def dijkstra(graph, edge_type, include_black):
queue = []
setup_dijkstra(graph, edge_type, include_black, queue)
while len(queue) > 0:
cost, guid, vertex = heapq.heappop(queue)
for edge in vertex.edges:
for v in edge.vertices:
if v is not vertex:
# add new path from vertex to v
try_new_path(graph, queue, vertex, edge, v)
def setup_dijkstra(graph, edge_type, include_black, queue):
setup_vertices(graph)
for vertex in graph.vertices:
if vertex.is_white():
continue
if (((vertex.is_black() and not include_black)
or edge_type not in vertex.accept_black
or edge_type not in vertex.accept_red_red)):
vertex.repl_info.cost = MAX_DWORD
vertex.root = None # NULL GUID
vertex.demoted = True # Demoted appears not to be used
else:
heapq.heappush(queue, (vertex.repl_info.cost, vertex.guid, vertex))
def try_new_path(graph, queue, vfrom, edge, vto):
newRI = ReplInfo()
# What this function checks is that there is a valid time frame for
# which replication can actually occur, despite being adequately
# connected
intersect = combine_repl_info(vfrom.repl_info, edge.repl_info, newRI)
# If the new path costs more than the current, then ignore the edge
if newRI.cost > vto.repl_info.cost:
return
if newRI.cost < vto.repl_info.cost and not intersect:
return
new_duration = total_schedule(newRI.schedule)
old_duration = total_schedule(vto.repl_info.schedule)
# Cheaper or longer schedule
if newRI.cost < vto.repl_info.cost or new_duration > old_duration:
vto.root = vfrom.root
vto.component_id = vfrom.component_id
vto.repl_info = newRI
heapq.heappush(queue, (vto.repl_info.cost, vto.guid, vto))
def check_demote_vertex(vertex, edge_type):
if vertex.is_white():
return
# Accepts neither red-red nor black edges, demote
if ((edge_type not in vertex.accept_black and
edge_type not in vertex.accept_red_red)):
vertex.repl_info.cost = MAX_DWORD
vertex.root = None
vertex.demoted = True # Demoted appears not to be used
def undemote_vertex(vertex):
if vertex.is_white():
return
vertex.repl_info.cost = 0
vertex.root = vertex
vertex.demoted = False
def process_edge_set(graph, e_set, internal_edges):
if e_set is None:
for edge in graph.edges:
for vertex in edge.vertices:
check_demote_vertex(vertex, edge.con_type)
process_edge(graph, edge, internal_edges)
for vertex in edge.vertices:
undemote_vertex(vertex)
else:
for edge in e_set.edges:
process_edge(graph, edge, internal_edges)
def process_edge(graph, examine, internal_edges):
# Find the set of all vertices touches the edge to examine
vertices = []
for v in examine.vertices:
# Append a 4-tuple of color, repl cost, guid and vertex
vertices.append((v.color, v.repl_info.cost, v.ndrpacked_guid, v))
# Sort by color, lower
DEBUG("vertices is %s" % vertices)
vertices.sort()
color, cost, guid, bestv = vertices[0]
# Add to internal edges an edge from every colored vertex to bestV
for v in examine.vertices:
if v.component_id is None or v.root is None:
continue
# Only add edge if valid inter-tree edge - needs a root and
# different components
if ((bestv.component_id is not None and
bestv.root is not None and
v.component_id is not None and
v.root is not None and
bestv.component_id != v.component_id)):
add_int_edge(graph, internal_edges, examine, bestv, v)
# Add internal edge, endpoints are roots of the vertices to pass in
# and are always colored
def add_int_edge(graph, internal_edges, examine, v1, v2):
root1 = v1.root
root2 = v2.root
red_red = False
if root1.is_red() and root2.is_red():
red_red = True
if red_red:
if ((examine.con_type not in root1.accept_red_red
or examine.con_type not in root2.accept_red_red)):
return
elif (examine.con_type not in root1.accept_black
or examine.con_type not in root2.accept_black):
return
ri = ReplInfo()
ri2 = ReplInfo()
# Create the transitive replInfo for the two trees and this edge
if not combine_repl_info(v1.repl_info, v2.repl_info, ri):
return
# ri is now initialized
if not combine_repl_info(ri, examine.repl_info, ri2):
return
newIntEdge = InternalEdge(root1, root2, red_red, ri2, examine.con_type,
examine.site_link)
# Order by vertex guid
#XXX guid comparison using ndr_pack
if newIntEdge.v1.ndrpacked_guid > newIntEdge.v2.ndrpacked_guid:
newIntEdge.v1 = root2
newIntEdge.v2 = root1
internal_edges.add(newIntEdge)
def kruskal(graph, edges):
for v in graph.vertices:
v.edges = []
components = set([x for x in graph.vertices if not x.is_white()])
edges = list(edges)
# Sorted based on internal comparison function of internal edge
edges.sort()
#XXX expected_num_tree_edges is never used
expected_num_tree_edges = 0 # TODO this value makes little sense
count_edges = 0
output_edges = []
index = 0
while index < len(edges): # TODO and num_components > 1
e = edges[index]
parent1 = find_component(e.v1)
parent2 = find_component(e.v2)
if parent1 is not parent2:
count_edges += 1
add_out_edge(graph, output_edges, e)
parent1.component_id = parent2
components.discard(parent1)
index += 1
return output_edges, len(components)
def find_component(vertex):
if vertex.component_id is vertex:
return vertex
current = vertex
while current.component_id is not current:
current = current.component_id
root = current
current = vertex
while current.component_id is not root:
n = current.component_id
current.component_id = root
current = n
return root
def add_out_edge(graph, output_edges, e):
v1 = e.v1
v2 = e.v2
# This multi-edge is a 'real' edge with no GUID
ee = MultiEdge()
ee.directed = False
ee.site_link = e.site_link
ee.vertices.append(v1)
ee.vertices.append(v2)
ee.con_type = e.e_type
ee.repl_info = e.repl_info
output_edges.append(ee)
v1.edges.append(ee)
v2.edges.append(ee)

View File

@ -33,6 +33,451 @@ from samba.kcc.kcc_utils import ReplInfo, combine_repl_info, total_schedule
from samba.kcc.kcc_utils import convert_schedule_to_repltimes
def get_spanning_tree_edges(graph, my_site, label=None, verify=False,
dot_files=False):
# Phase 1: Run Dijkstra's to get a list of internal edges, which are
# just the shortest-paths connecting colored vertices
internal_edges = set()
for e_set in graph.edge_set:
edgeType = None
for v in graph.vertices:
v.edges = []
# All con_type in an edge set is the same
for e in e_set.edges:
edgeType = e.con_type
for v in e.vertices:
v.edges.append(e)
if verify or dot_files:
graph_edges = [(a.site.site_dnstr, b.site.site_dnstr)
for a, b in
itertools.chain(
*(itertools.combinations(edge.vertices, 2)
for edge in e_set.edges))]
graph_nodes = [v.site.site_dnstr for v in graph.vertices]
if dot_files:
write_dot_file('edgeset_%s' % (edgeType,), graph_edges,
vertices=graph_nodes, label=label)
if verify:
verify_graph('spanning tree edge set %s' % edgeType,
graph_edges, vertices=graph_nodes,
properties=('complete', 'connected'),
debug=DEBUG)
# Run dijkstra's algorithm with just the red vertices as seeds
# Seed from the full replicas
dijkstra(graph, edgeType, False)
# Process edge set
process_edge_set(graph, e_set, internal_edges)
# Run dijkstra's algorithm with red and black vertices as the seeds
# Seed from both full and partial replicas
dijkstra(graph, edgeType, True)
# Process edge set
process_edge_set(graph, e_set, internal_edges)
# All vertices have root/component as itself
setup_vertices(graph)
process_edge_set(graph, None, internal_edges)
if verify or dot_files:
graph_edges = [(e.v1.site.site_dnstr, e.v2.site.site_dnstr)
for e in internal_edges]
graph_nodes = [v.site.site_dnstr for v in graph.vertices]
verify_properties = ('multi_edge_forest',)
verify_and_dot('prekruskal', graph_edges, graph_nodes, label=label,
properties=verify_properties, debug=DEBUG,
verify=verify,
dot_files=dot_files)
# Phase 2: Run Kruskal's on the internal edges
output_edges, components = kruskal(graph, internal_edges)
# This recalculates the cost for the path connecting the
# closest red vertex. Ignoring types is fine because NO
# suboptimal edge should exist in the graph
dijkstra(graph, "EDGE_TYPE_ALL", False) # TODO rename
# Phase 3: Process the output
for v in graph.vertices:
if v.is_red():
v.dist_to_red = 0
else:
v.dist_to_red = v.repl_info.cost
if verify or dot_files:
graph_edges = [(e.v1.site.site_dnstr, e.v2.site.site_dnstr)
for e in internal_edges]
graph_nodes = [v.site.site_dnstr for v in graph.vertices]
verify_properties = ('multi_edge_forest',)
verify_and_dot('postkruskal', graph_edges, graph_nodes,
label=label, properties=verify_properties,
debug=DEBUG, verify=verify,
dot_files=dot_files)
# Ensure only one-way connections for partial-replicas,
# and make sure they point the right way.
edge_list = []
for edge in output_edges:
# We know these edges only have two endpoints because we made
# them.
v, w = edge.vertices
if v.site is my_site or w.site is my_site:
if (((v.is_black() or w.is_black()) and
v.dist_to_red != MAX_DWORD)):
edge.directed = True
if w.dist_to_red < v.dist_to_red:
edge.vertices[:] = w, v
edge_list.append(edge)
if verify or dot_files:
graph_edges = [[x.site.site_dnstr for x in e.vertices]
for e in edge_list]
#add the reverse edge if not directed.
graph_edges.extend([x.site.site_dnstr
for x in reversed(e.vertices)]
for e in edge_list if not e.directed)
graph_nodes = [x.site.site_dnstr for x in graph.vertices]
verify_properties = ()
verify_and_dot('post-one-way-partial', graph_edges, graph_nodes,
label=label, properties=verify_properties,
debug=DEBUG, verify=verify,
directed=True,
dot_files=dot_files)
# count the components
return edge_list, components
def create_edge(con_type, site_link, guid_to_vertex):
e = MultiEdge()
e.site_link = site_link
e.vertices = []
for site_guid in site_link.site_list:
if str(site_guid) in guid_to_vertex:
e.vertices.extend(guid_to_vertex.get(str(site_guid)))
e.repl_info.cost = site_link.cost
e.repl_info.options = site_link.options
e.repl_info.interval = site_link.interval
e.repl_info.schedule = convert_schedule_to_repltimes(site_link.schedule)
e.con_type = con_type
e.directed = False
return e
def create_auto_edge_set(graph, transport):
e_set = MultiEdgeSet()
# use a NULL guid, not associated with a SiteLinkBridge object
e_set.guid = misc.GUID()
for site_link in graph.edges:
if site_link.con_type == transport:
e_set.edges.append(site_link)
return e_set
def create_edge_set(graph, transport, site_link_bridge):
# TODO not implemented - need to store all site link bridges
e_set = MultiEdgeSet()
# e_set.guid = site_link_bridge
return e_set
def setup_vertices(graph):
for v in graph.vertices:
if v.is_white():
v.repl_info.cost = MAX_DWORD
v.root = None
v.component_id = None
else:
v.repl_info.cost = 0
v.root = v
v.component_id = v
v.repl_info.interval = 0
v.repl_info.options = 0xFFFFFFFF
v.repl_info.schedule = None # TODO highly suspicious
v.demoted = False
def dijkstra(graph, edge_type, include_black):
queue = []
setup_dijkstra(graph, edge_type, include_black, queue)
while len(queue) > 0:
cost, guid, vertex = heapq.heappop(queue)
for edge in vertex.edges:
for v in edge.vertices:
if v is not vertex:
# add new path from vertex to v
try_new_path(graph, queue, vertex, edge, v)
def setup_dijkstra(graph, edge_type, include_black, queue):
setup_vertices(graph)
for vertex in graph.vertices:
if vertex.is_white():
continue
if (((vertex.is_black() and not include_black)
or edge_type not in vertex.accept_black
or edge_type not in vertex.accept_red_red)):
vertex.repl_info.cost = MAX_DWORD
vertex.root = None # NULL GUID
vertex.demoted = True # Demoted appears not to be used
else:
heapq.heappush(queue, (vertex.repl_info.cost, vertex.guid, vertex))
def try_new_path(graph, queue, vfrom, edge, vto):
newRI = ReplInfo()
# What this function checks is that there is a valid time frame for
# which replication can actually occur, despite being adequately
# connected
intersect = combine_repl_info(vfrom.repl_info, edge.repl_info, newRI)
# If the new path costs more than the current, then ignore the edge
if newRI.cost > vto.repl_info.cost:
return
if newRI.cost < vto.repl_info.cost and not intersect:
return
new_duration = total_schedule(newRI.schedule)
old_duration = total_schedule(vto.repl_info.schedule)
# Cheaper or longer schedule
if newRI.cost < vto.repl_info.cost or new_duration > old_duration:
vto.root = vfrom.root
vto.component_id = vfrom.component_id
vto.repl_info = newRI
heapq.heappush(queue, (vto.repl_info.cost, vto.guid, vto))
def check_demote_vertex(vertex, edge_type):
if vertex.is_white():
return
# Accepts neither red-red nor black edges, demote
if ((edge_type not in vertex.accept_black and
edge_type not in vertex.accept_red_red)):
vertex.repl_info.cost = MAX_DWORD
vertex.root = None
vertex.demoted = True # Demoted appears not to be used
def undemote_vertex(vertex):
if vertex.is_white():
return
vertex.repl_info.cost = 0
vertex.root = vertex
vertex.demoted = False
def process_edge_set(graph, e_set, internal_edges):
if e_set is None:
for edge in graph.edges:
for vertex in edge.vertices:
check_demote_vertex(vertex, edge.con_type)
process_edge(graph, edge, internal_edges)
for vertex in edge.vertices:
undemote_vertex(vertex)
else:
for edge in e_set.edges:
process_edge(graph, edge, internal_edges)
def process_edge(graph, examine, internal_edges):
# Find the set of all vertices touches the edge to examine
vertices = []
for v in examine.vertices:
# Append a 4-tuple of color, repl cost, guid and vertex
vertices.append((v.color, v.repl_info.cost, v.ndrpacked_guid, v))
# Sort by color, lower
DEBUG("vertices is %s" % vertices)
vertices.sort()
color, cost, guid, bestv = vertices[0]
# Add to internal edges an edge from every colored vertex to bestV
for v in examine.vertices:
if v.component_id is None or v.root is None:
continue
# Only add edge if valid inter-tree edge - needs a root and
# different components
if ((bestv.component_id is not None and
bestv.root is not None and
v.component_id is not None and
v.root is not None and
bestv.component_id != v.component_id)):
add_int_edge(graph, internal_edges, examine, bestv, v)
# Add internal edge, endpoints are roots of the vertices to pass in
# and are always colored
def add_int_edge(graph, internal_edges, examine, v1, v2):
root1 = v1.root
root2 = v2.root
red_red = False
if root1.is_red() and root2.is_red():
red_red = True
if red_red:
if ((examine.con_type not in root1.accept_red_red
or examine.con_type not in root2.accept_red_red)):
return
elif (examine.con_type not in root1.accept_black
or examine.con_type not in root2.accept_black):
return
ri = ReplInfo()
ri2 = ReplInfo()
# Create the transitive replInfo for the two trees and this edge
if not combine_repl_info(v1.repl_info, v2.repl_info, ri):
return
# ri is now initialized
if not combine_repl_info(ri, examine.repl_info, ri2):
return
newIntEdge = InternalEdge(root1, root2, red_red, ri2, examine.con_type,
examine.site_link)
# Order by vertex guid
#XXX guid comparison using ndr_pack
if newIntEdge.v1.ndrpacked_guid > newIntEdge.v2.ndrpacked_guid:
newIntEdge.v1 = root2
newIntEdge.v2 = root1
internal_edges.add(newIntEdge)
def kruskal(graph, edges):
for v in graph.vertices:
v.edges = []
components = set([x for x in graph.vertices if not x.is_white()])
edges = list(edges)
# Sorted based on internal comparison function of internal edge
edges.sort()
#XXX expected_num_tree_edges is never used
expected_num_tree_edges = 0 # TODO this value makes little sense
count_edges = 0
output_edges = []
index = 0
while index < len(edges): # TODO and num_components > 1
e = edges[index]
parent1 = find_component(e.v1)
parent2 = find_component(e.v2)
if parent1 is not parent2:
count_edges += 1
add_out_edge(graph, output_edges, e)
parent1.component_id = parent2
components.discard(parent1)
index += 1
return output_edges, len(components)
def find_component(vertex):
if vertex.component_id is vertex:
return vertex
current = vertex
while current.component_id is not current:
current = current.component_id
root = current
current = vertex
while current.component_id is not root:
n = current.component_id
current.component_id = root
current = n
return root
def add_out_edge(graph, output_edges, e):
v1 = e.v1
v2 = e.v2
# This multi-edge is a 'real' edge with no GUID
ee = MultiEdge()
ee.directed = False
ee.site_link = e.site_link
ee.vertices.append(v1)
ee.vertices.append(v2)
ee.con_type = e.e_type
ee.repl_info = e.repl_info
output_edges.append(ee)
v1.edges.append(ee)
v2.edges.append(ee)
def setup_graph(part, site_table, transport_table, sitelink_table,
bridges_required):
"""Set up a GRAPH, populated with a VERTEX for each site
object, a MULTIEDGE for each siteLink object, and a
MUTLIEDGESET for each siteLinkBridge object (or implied
siteLinkBridge).
::returns: a new graph
"""
guid_to_vertex = {}
# Create graph
g = IntersiteGraph()
# Add vertices
for site_guid, site in site_table.items():
vertex = Vertex(site, part)
vertex.guid = site_guid
vertex.ndrpacked_guid = ndr_pack(site.site_guid)
g.vertices.add(vertex)
guid_vertices = guid_to_vertex.setdefault(site_guid, [])
guid_vertices.append(vertex)
connected_vertices = set()
for transport_guid, transport in transport_table.items():
# Currently only ever "IP"
if transport.name != 'IP':
DEBUG_FN("setup_graph is ignoring transport %s" %
transport.name)
continue
for site_link_dn, site_link in sitelink_table.items():
new_edge = create_edge(transport_guid, site_link,
guid_to_vertex)
connected_vertices.update(new_edge.vertices)
g.edges.add(new_edge)
# If 'Bridge all site links' is enabled and Win2k3 bridges required
# is not set
# NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
# No documentation for this however, ntdsapi.h appears to have:
# NTDSSETTINGS_OPT_W2K3_BRIDGES_REQUIRED = 0x00001000
if bridges_required:
g.edge_set.add(create_auto_edge_set(g, transport_guid))
else:
# TODO get all site link bridges
for site_link_bridge in []:
g.edge_set.add(create_edge_set(g, transport_guid,
site_link_bridge))
g.connected_vertices = connected_vertices
return g
class VertexColor(object):
(red, black, white, unknown) = range(0, 4)