summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipatests/test_integration/base.py11
-rw-r--r--ipatests/test_integration/host.py8
-rw-r--r--ipatests/test_integration/tasks.py145
-rw-r--r--ipatests/test_integration/test_topologies.py98
4 files changed, 254 insertions, 8 deletions
diff --git a/ipatests/test_integration/base.py b/ipatests/test_integration/base.py
index 86acbcce1..43360a83a 100644
--- a/ipatests/test_integration/base.py
+++ b/ipatests/test_integration/base.py
@@ -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):
diff --git a/ipatests/test_integration/host.py b/ipatests/test_integration/host.py
index e3cc4c924..38e6a34e0 100644
--- a/ipatests/test_integration/host.py
+++ b/ipatests/test_integration/host.py
@@ -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
diff --git a/ipatests/test_integration/tasks.py b/ipatests/test_integration/tasks.py
index ebdd0b813..609df8130 100644
--- a/ipatests/test_integration/tasks.py
+++ b/ipatests/test_integration/tasks.py
@@ -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)
diff --git a/ipatests/test_integration/test_topologies.py b/ipatests/test_integration/test_topologies.py
new file mode 100644
index 000000000..86ca1904f
--- /dev/null
+++ b/ipatests/test_integration/test_topologies.py
@@ -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', [])) == []