Add install_topo to test tasks

This allows a cluster of replicas and clients to be installed
in a named topology.
Several named topologies are available (star, line, complete, tree,
tree2) and new ones can be defined as a simple function.
This commit is contained in:
Petr Viktorin 2013-06-27 15:28:13 +02:00
parent ac70c2cc5c
commit 13f4b7e9cf
4 changed files with 254 additions and 8 deletions

View File

@ -35,7 +35,7 @@ log = log_mgr.get_logger(__name__)
class IntegrationTest(object):
num_replicas = 0
num_clients = 0
topology = 'none'
topology = None
@classmethod
def setup_class(cls):
@ -78,14 +78,11 @@ class IntegrationTest(object):
@classmethod
def install(cls):
if cls.topology == 'none':
if cls.topology is None:
return
elif cls.topology == 'star':
tasks.install_master(cls.master)
for replica in cls.replicas:
tasks.install_replica(cls.master, replica)
else:
raise ValueError('Unknown topology %s' % cls.topology)
tasks.install_topo(cls.topology,
cls.master, cls.replicas, cls.clients)
@classmethod
def teardown_class(cls):

View File

@ -162,6 +162,10 @@ class Host(object):
self.log_collectors = []
def __str__(self):
template = ('<{s.__class__.__name__} {s.hostname} ({s.role})>')
return template.format(s=self)
def __repr__(self):
template = ('<{s.__module__}.{s.__class__.__name__} '
'{s.hostname} ({s.role})>')
@ -222,9 +226,11 @@ class Host(object):
:param raiseonerr: If true, an exception will be raised if the command
does not exit with return code 0
"""
assert self.transport
self._command_index += 1
command = RemoteCommand(self, argv, index=self._command_index,
log_stdout=log_stdout)
self._command_index += 1
if cwd is None:
cwd = self.config.test_dir

View File

@ -22,6 +22,8 @@
import os
import textwrap
import re
import collections
import itertools
from ipapython import ipautil
from ipapython.ipa_log_manager import log_mgr
@ -234,3 +236,146 @@ def uninstall_client(host):
host.run_command(['ipa-client-install', '--uninstall', '-U'],
raiseonerr=False)
unapply_fixes(host)
def get_topo(name_or_func):
"""Get a topology function by name
A topology function receives a master and list of replicas, and yields
(parent, child) pairs, where "child" should be installed from "parent"
(or just connected if already installed)
If a callable is given instead of name, it is returned directly
"""
if callable(name_or_func):
return name_or_func
return topologies[name_or_func]
def _topo(name):
"""Decorator that registers a function in topologies under a given name"""
def add_topo(func):
topologies[name] = func
return func
return add_topo
topologies = collections.OrderedDict()
@_topo('star')
def star_topo(master, replicas):
r"""All replicas are connected to the master
Rn R1 R2
\ | /
R7-- M -- R3
/ | \
R6 R5 R4
"""
for replica in replicas:
yield master, replica
@_topo('line')
def line_topo(master, replicas):
r"""Line topology
M
\
R1
\
R2
\
R3
\
...
"""
for replica in replicas:
yield master, replica
master = replica
@_topo('complete')
def complete_topo(master, replicas):
r"""Each host connected to each other host
M--R1
|\/|
|/\|
R2-R3
"""
for replica in replicas:
yield master, replica
for replica1, replica2 in itertools.combinations(replicas, 2):
yield replica1, replica2
@_topo('tree')
def tree_topo(master, replicas):
r"""Binary tree topology
M
/ \
/ \
R1 R2
/ \ / \
R3 R4 R5 R6
/
R7 ...
"""
replicas = list(replicas)
def _masters():
for host in [master] + replicas:
yield host
yield host
for parent, child in zip(_masters(), replicas):
yield parent, child
@_topo('tree2')
def tree2_topo(master, replicas):
r"""First replica connected directly to master, the rest in a line
M
/ \
R1 R2
\
R3
\
R4
\
...
"""
if replicas:
yield master, replicas[0]
for replica in replicas[1:]:
yield master, replica
master = replica
def install_topo(topo, master, replicas, clients,
skip_master=False, setup_replica_cas=True):
"""Install IPA servers and clients in the given topology"""
replicas = list(replicas)
installed = {master}
if not skip_master:
install_master(master)
for parent, child in get_topo(topo)(master, replicas):
if child in installed:
log.info('Connecting replica %s to %s' % (parent, child))
connect_replica(parent, child)
else:
log.info('Installing replica %s from %s' % (parent, child))
install_replica(parent, child, setup_ca=setup_replica_cas)
installed.add(child)
install_clients([master] + replicas, clients)
def install_clients(servers, clients):
"""Install IPA clients, distributing them among the given servers"""
for server, client in itertools.izip(itertools.cycle(servers), clients):
log.info('Installing client %s on %s' % (server, client))
install_client(server, client)

View File

@ -0,0 +1,98 @@
# Authors:
# Petr Viktorin <pviktori@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipatests.test_integration import tasks
def test_topology_star():
topo = tasks.get_topo('star')
assert topo == tasks.star_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
('M', 2),
('M', 3),
('M', 4),
('M', 5),
]
assert list(topo('M', [])) == []
def test_topology_line():
topo = tasks.get_topo('line')
assert topo == tasks.line_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
(1, 2),
(2, 3),
(3, 4),
(4, 5),
]
assert list(topo('M', [])) == []
def test_topology_tree():
topo = tasks.get_topo('tree')
assert topo == tasks.tree_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
('M', 2),
(1, 3),
(1, 4),
(2, 5),
]
assert list(topo('M', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) == [
('M', 1),
('M', 2),
(1, 3),
(1, 4),
(2, 5),
(2, 6),
(3, 7),
(3, 8),
(4, 9),
(4, 10),
]
assert list(topo('M', [])) == []
def test_topology_tree2():
topo = tasks.get_topo('tree2')
assert topo == tasks.tree2_topo
assert list(topo('M', [1, 2, 3, 4, 5])) == [
('M', 1),
('M', 2),
(2, 3),
(3, 4),
(4, 5),
]
assert list(topo('M', [])) == []
def test_topology_complete():
topo = tasks.get_topo('complete')
assert topo == tasks.complete_topo
assert list(topo('M', [1, 2, 3])) == [
('M', 1),
('M', 2),
('M', 3),
(1, 2),
(1, 3),
(2, 3),
]
assert list(topo('M', [])) == []