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:
parent
b0e6a74362
commit
427d05d1ac
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user