mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipaserver module for working with managed topology
This module should aggregate common functionality utilized in the commands managing domain-level 1 topology. https://fedorahosted.org/freeipa/ticket/5588 Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
committed by
Martin Basti
parent
45bb2ad045
commit
d8ae2b4055
195
ipaserver/topology.py
Normal file
195
ipaserver/topology.py
Normal file
@@ -0,0 +1,195 @@
|
||||
#
|
||||
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
"""
|
||||
set of functions and classes useful for management of domain level 1 topology
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ipalib import _
|
||||
from ipapython.graph import Graph
|
||||
|
||||
CURR_TOPOLOGY_DISCONNECTED = _("""
|
||||
Replication topology in suffix '%(suffix)s' is disconnected:
|
||||
%(errors)s""")
|
||||
|
||||
REMOVAL_DISCONNECTS_TOPOLOGY = _("""
|
||||
Removal of '%(hostname)s' leads to disconnected topology in suffix '%(suffix)s':
|
||||
%(errors)s""")
|
||||
|
||||
|
||||
def create_topology_graph(masters, segments):
|
||||
"""
|
||||
Create an oriented graph from topology defined by masters and segments.
|
||||
|
||||
:param masters
|
||||
:param segments
|
||||
:returns: Graph
|
||||
"""
|
||||
graph = Graph()
|
||||
|
||||
for m in masters:
|
||||
graph.add_vertex(m['cn'][0])
|
||||
|
||||
for s in segments:
|
||||
direction = s['iparepltoposegmentdirection'][0]
|
||||
left = s['iparepltoposegmentleftnode'][0]
|
||||
right = s['iparepltoposegmentrightnode'][0]
|
||||
try:
|
||||
if direction == u'both':
|
||||
graph.add_edge(left, right)
|
||||
graph.add_edge(right, left)
|
||||
elif direction == u'left-right':
|
||||
graph.add_edge(left, right)
|
||||
elif direction == u'right-left':
|
||||
graph.add_edge(right, left)
|
||||
except ValueError: # ignore segments with deleted master
|
||||
pass
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
def get_topology_connection_errors(graph):
|
||||
"""
|
||||
Traverse graph from each master and find out which masters are not
|
||||
reachable.
|
||||
|
||||
:param graph: topology graph where vertices are masters
|
||||
:returns: list of errors, error is: (master, visited, not_visited)
|
||||
"""
|
||||
connect_errors = []
|
||||
master_cns = list(graph.vertices)
|
||||
master_cns.sort()
|
||||
for m in master_cns:
|
||||
visited = graph.bfs(m)
|
||||
not_visited = graph.vertices - visited
|
||||
if not_visited:
|
||||
connect_errors.append((m, list(visited), list(not_visited)))
|
||||
return connect_errors
|
||||
|
||||
|
||||
def _map_masters_to_suffixes(masters):
|
||||
masters_to_suffix = {}
|
||||
|
||||
for master in masters:
|
||||
try:
|
||||
managed_suffixes = master.get(
|
||||
'iparepltopomanagedsuffix_topologysuffix')
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for suffix_name in managed_suffixes:
|
||||
try:
|
||||
masters_to_suffix[suffix_name].append(master)
|
||||
except KeyError:
|
||||
masters_to_suffix[suffix_name] = [master]
|
||||
|
||||
return masters_to_suffix
|
||||
|
||||
|
||||
def _create_topology_graphs(api_instance):
|
||||
"""
|
||||
Construct a topology graph for each topology suffix
|
||||
:param api_instance: instance of IPA API
|
||||
"""
|
||||
masters = api_instance.Command.server_find(
|
||||
u'', sizelimit=0, no_members=False)['result']
|
||||
|
||||
suffix_to_masters = _map_masters_to_suffixes(masters)
|
||||
|
||||
topology_graphs = {}
|
||||
|
||||
for suffix_name in suffix_to_masters:
|
||||
segments = api_instance.Command.topologysegment_find(
|
||||
suffix_name, sizelimit=0).get('result')
|
||||
|
||||
topology_graphs[suffix_name] = create_topology_graph(
|
||||
suffix_to_masters[suffix_name], segments)
|
||||
|
||||
return topology_graphs
|
||||
|
||||
|
||||
def _format_topology_errors(topo_errors):
|
||||
msg_lines = []
|
||||
for error in topo_errors:
|
||||
msg_lines.append(
|
||||
_("Topology does not allow server %(server)s to replicate with "
|
||||
"servers:")
|
||||
% {'server': error[0]}
|
||||
)
|
||||
for srv in error[2]:
|
||||
msg_lines.append(" %s" % srv)
|
||||
|
||||
return "\n".join(msg_lines)
|
||||
|
||||
|
||||
class TopologyConnectivity(object):
|
||||
"""
|
||||
a simple class abstracting the replication connectivity in managed topology
|
||||
"""
|
||||
|
||||
def __init__(self, api_instance):
|
||||
self.api = api_instance
|
||||
|
||||
self.graphs = _create_topology_graphs(self.api)
|
||||
|
||||
@property
|
||||
def errors(self):
|
||||
errors_by_suffix = {}
|
||||
for suffix in self.graphs:
|
||||
errors_by_suffix[suffix] = get_topology_connection_errors(
|
||||
self.graphs[suffix]
|
||||
)
|
||||
|
||||
return errors_by_suffix
|
||||
|
||||
def errors_after_master_removal(self, master_cn):
|
||||
graphs_before = deepcopy(self.graphs)
|
||||
|
||||
for s in self.graphs:
|
||||
try:
|
||||
self.graphs[s].remove_vertex(master_cn)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
errors_after_removal = self.errors
|
||||
|
||||
self.graphs = graphs_before
|
||||
|
||||
return errors_after_removal
|
||||
|
||||
def check_current_state(self):
|
||||
err_msg = ""
|
||||
for suffix in self.errors:
|
||||
errors = self.errors[suffix]
|
||||
if errors:
|
||||
err_msg = "\n".join([
|
||||
err_msg,
|
||||
CURR_TOPOLOGY_DISCONNECTED % dict(
|
||||
suffix=suffix,
|
||||
errors=_format_topology_errors(errors)
|
||||
)])
|
||||
|
||||
if err_msg:
|
||||
raise ValueError(err_msg)
|
||||
|
||||
def check_state_after_removal(self, master_cn):
|
||||
err_msg = ""
|
||||
errors_after_removal = self.errors_after_master_removal(master_cn)
|
||||
|
||||
for suffix in errors_after_removal:
|
||||
errors = errors_after_removal[suffix]
|
||||
if errors:
|
||||
err_msg = "\n".join([
|
||||
err_msg,
|
||||
REMOVAL_DISCONNECTS_TOPOLOGY % dict(
|
||||
hostname=master_cn,
|
||||
suffix=suffix,
|
||||
errors=_format_topology_errors(errors)
|
||||
)
|
||||
])
|
||||
|
||||
if err_msg:
|
||||
raise ValueError(err_msg)
|
||||
Reference in New Issue
Block a user