Hi,

Sending the updated version of the patchset. It includes many fixes, plus incorporations of the comments by the QE. Tests are now broken down into multiple test cases.


This requires my patch 119.

Tomas
From 0a8645eaa1d8826a9544df288b12bde71a83dcb4 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Mon, 14 Oct 2013 16:37:55 +0200
Subject: [PATCH 106/106] ipatests: Add AD integration test case

---
 ipatests/test_integration/test_trust.py | 182 ++++++++++++++++++++++++++++++++
 1 file changed, 182 insertions(+)
 create mode 100644 ipatests/test_integration/test_trust.py

diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
new file mode 100644
index 0000000000000000000000000000000000000000..202db2a2f6fb83a1e3cdf05b9c5c9c2617862736
--- /dev/null
+++ b/ipatests/test_integration/test_trust.py
@@ -0,0 +1,182 @@
+# Authors:
+#   Tomas Babej <tba...@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/>.
+
+import re
+
+from ipatests.test_integration.base import IntegrationTest
+from ipatests.test_integration import tasks
+from ipatests.test_integration import util
+
+
+class ADTrustBase(IntegrationTest):
+    """Provides common checks for the AD trust integration testing."""
+
+    topology = 'line'
+    num_ad_domains = 1
+
+    @classmethod
+    def setup_class(cls):
+        super(ADTrustBase, cls).setup_class()
+        cls.ad = cls.ad_domains[0].ads[0]
+
+    def test_install_adtrust(self):
+        """Test adtrust support installation"""
+
+        tasks.install_adtrust(self.master)
+
+    def test_check_sid_generation(self):
+        """Test SID generation"""
+
+        command = ['ipa', 'user-show', 'admin', '--all', '--raw']
+
+        # TODO: remove duplicate definition and import from common module
+        _sid_identifier_authority = '(0x[0-9a-f]{1,12}|[0-9]{1,10})'
+        sid_regex = 'S-1-5-21-%(idauth)s-%(idauth)s-%(idauth)s'\
+                    % dict(idauth=_sid_identifier_authority)
+        stdout_re = re.escape('  ipaNTSecurityIdentifier: ') + sid_regex
+
+        assert util.run_repeatedly(self.master, command,
+                                   test=lambda x: re.search(stdout_re, x))
+
+    def test_configure_dns_and_time(self):
+        tasks.configure_dns_for_trust(self.master, self.ad)
+        tasks.sync_time(self.master, self.ad)
+
+
+class TestBasicADTrust(ADTrustBase):
+    """Basic Integration test for Active Directory"""
+
+    def test_estabilish_trust(self):
+        """Tests estabilishing trust with Active Directory"""
+
+        tasks.estabilish_trust_with_ad(self.master, self.ad,
+            extra_args=['--range-type', 'ipa-ad-trust'])
+
+    def test_range_properties_in_nonposix_trust(self):
+        """Check the properties of the created range"""
+
+        range_name = self.ad.domain.name.upper() + '_id_range'
+        result = self.master.run_command(['ipa', 'idrange-show', range_name,
+                                          '--all', '--raw'])
+        assert "  ipaRangeType: ipa-ad-trust" in result.stdout_text
+        assert "  ipaIDRangeSize: 200000" in result.stdout_text
+
+    def test_user_gid_uid_resolution_in_nonposix_trust(self):
+        """Check that user has SID-generated UID"""
+
+        testuser = 'testuser@%s' % self.ad.domain.realm
+        result = self.master.run_command(['getent', 'passwd', testuser])
+
+        # This regex checks that Test User does not have UID 10042 nor belongs
+        # to the group with GID 10047
+        testuser_regex = "^testuser@%s:\*:(?!10042)(\d+):(?!10047)(\d+):"\
+                         "Test User:/home/testuser:/bin/sh$"\
+                         % re.escape(self.ad.domain.name)
+
+        assert re.search(testuser_regex, result.stdout_text)
+
+    def test_remove_nonposix_trust(self):
+        tasks.remove_trust_with_ad(self.master, self.ad)
+        tasks.clear_sssd_cache(self.master)
+
+
+class TestPosixADTrust(ADTrustBase):
+    """Integration test for Active Directory with POSIX support"""
+
+    def test_estabilish_trust_with_posix_attributes(self):
+        # Not specifying the --range-type directly, it should be detected
+        tasks.estabilish_trust_with_ad(self.master, self.ad)
+
+    def test_range_properties_in_posix_trust(self):
+        # Check the properties of the created range
+
+        range_name = self.ad.domain.name.upper() + '_id_range'
+
+        result = self.master.run_command(['ipa', 'idrange-show', range_name,
+                                          '--all', '--raw'])
+
+        # Check the range type and size
+        assert "  ipaRangeType: ipa-ad-trust-posix" in result.stdout_text
+        assert "  ipaIDRangeSize: 200000" in result.stdout_text
+
+    def test_user_uid_gid_resolution_in_posix_trust(self):
+        # Check that user has AD-defined UID
+
+        testuser = 'testuser@%s' % self.ad.domain.realm
+        result = self.master.run_command(['getent', 'passwd', testuser])
+
+        testuser_stdout = "testuser@%s:*:10042:10047:"\
+                          "Test User:/home/testuser:/bin/sh"\
+                          % self.ad.domain.name
+
+        assert testuser_stdout in result.stdout_text
+
+    def test_user_without_posix_attributes_not_visible(self):
+        # Check that user has AD-defined UID
+
+        nonposixuser = 'nonposixuser@%s' % self.ad.domain.realm
+        result = self.master.run_command(['getent', 'passwd', nonposixuser],
+                                         raiseonerr=False)
+
+        # Getent exits with 2 for non-existent user
+        assert result.returncode == 2
+
+    def test_remove_trust_with_posix_attributes(self):
+        tasks.remove_trust_with_ad(self.master, self.ad)
+        tasks.clear_sssd_cache(self.master)
+
+
+class TestEnforcedPosixADTrust(TestPosixADTrust):
+    """
+    This test is intented to copycat PosixADTrust, since enforcing the POSIX
+    trust type should not make a difference.
+    """
+
+    def test_estabilish_trust_with_posix_attributes(self):
+        tasks.estabilish_trust_with_ad(self.master, self.ad,
+            extra_args=['--range-type', 'ipa-ad-trust-posix'])
+
+
+class TestInvalidRangeTypes(ADTrustBase):
+    """
+    Tests invalid values being put into trust-add command.
+    """
+
+    def test_invalid_range_types(self):
+
+        invalid_range_types = ['ipa-local',
+                               'ipa-ad-winsync',
+                               'ipa-ipa-trust',
+                               'random-invalid',
+                               're@ll%ybad12!']
+
+        for range_type in invalid_range_types:
+            tasks.kinit_admin(self.master)
+
+            result = self.master.run_command(
+                               ['ipa', 'trust-add',
+                               '--type', 'ad', self.ad.domain.name,
+                               '--admin', 'Administrator',
+                               '--range-type', range_type,
+                               '--password'],
+                               raiseonerr=False,
+                               stdin_text=self.master.config.ad_admin_password)
+
+            # The trust-add command is supposed to fail
+            assert result.returncode == 1
-- 
1.8.3.1

From 595f92c48aa6d17f965704e6a85bd16190fef74c Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 4 Sep 2013 16:29:06 +0200
Subject: [PATCH 105/106] ipatests: Add AD-integration related tasks

---
 ipatests/man/ipa-test-task.1       |  32 +++++++
 ipatests/test_integration/tasks.py | 177 +++++++++++++++++++++++++++++++++++++
 2 files changed, 209 insertions(+)

diff --git a/ipatests/man/ipa-test-task.1 b/ipatests/man/ipa-test-task.1
index b625b285067c8fccbda540cbadebacc75925d553..fc56dc70edc36d831a254c3e0f57e238ba386f29 100644
--- a/ipatests/man/ipa-test-task.1
+++ b/ipatests/man/ipa-test-task.1
@@ -116,6 +116,38 @@ Servers used for client installation are selected in a round-robin fashion.
 \fBipa\-test\-task list-topos\fR
 List the topologies available for the install-topo subcommand.
 
+.TP
+\fBipa\-test\-task install\-adtrust MASTER\fR
+Run ipa-adtrust-install on the IPA and generatee SIDs for the entries in IPA.
+
+.TP
+\fBipa\-test\-task configure\-dns-for-trust MASTER AD\fR
+Based on the relationship of the domains configures the IPA DNS for trust.
+AD DNS needs to be setup manually.
+
+.TP
+\fBipa\-test\-task estabilish\-trust-with-ad MASTER AD\fR
+Estabilishes trust with Active Directory. Trust type is detected depending on
+the presence of SfU (Services for Unix) support on the AD.
+
+.TP
+\fBipa\-test\-task remove\-trust-with-ad MASTER AD\fR
+Removes trust with Active Directory. Also removes the associated ID range.
+
+.TP
+\fBipa\-test\-task configure\-auth-to-local-rule MASTER AD\fR
+Configures auth_to_local rule in /etc/krb5.conf
+
+.TP
+\fBipa\-test\-task clear\-sssd-cache HOST\fR
+Clears SSSD cache by removing the cache files. Restarts SSSD.
+
+.TP
+\fBipa\-test\-task clear\-sssd-cache HOST\fR
+Syncs the time with the remote server. Please note that this function leaves
+ntpd stopped.
+
+
 .SH "EXIT STATUS"
 0 if the command was successful
 
diff --git a/ipatests/test_integration/tasks.py b/ipatests/test_integration/tasks.py
index 7ea0ce4f16fc65e5b63b294e5f17145700a2ac4d..f41c330bbb2f2e3cb493c399d1e7fc73003f3404 100644
--- a/ipatests/test_integration/tasks.py
+++ b/ipatests/test_integration/tasks.py
@@ -32,6 +32,7 @@ from ldif import LDIFWriter
 from ipapython import ipautil
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import log_mgr
+from ipatests.test_integration import util
 from ipatests.test_integration.config import env_to_script
 
 log = log_mgr.get_logger(__name__)
@@ -197,6 +198,7 @@ def install_replica(master, replica, setup_ca=True):
 
     kinit_admin(replica)
 
+
 def install_client(master, client):
     client.collect_log('/var/log/ipaclient-install.log')
 
@@ -212,6 +214,181 @@ def install_client(master, client):
     kinit_admin(client)
 
 
+def install_adtrust(host):
+    """
+    Runs ipa-adtrust-install on the client and generates SIDs for the entries.
+    """
+
+    # ipa-adtrust-install appends to ipaserver-install.log
+    host.collect_log('/var/log/ipaserver-install.log')
+
+    inst = host.domain.realm.replace('.', '-')
+    host.collect_log('/var/log/dirsrv/slapd-%s/errors' % inst)
+    host.collect_log('/var/log/dirsrv/slapd-%s/access' % inst)
+
+    kinit_admin(host)
+    host.run_command(['ipa-adtrust-install', '-U',
+                      '--netbios-name', host.netbios,
+                      '-a', host.config.admin_password,
+                      '--add-sids'])
+
+    # Restart named because it lost connection to dirsrv
+    # (Directory server restarts during the ipa-adtrust-install)
+    host.run_command(['systemctl', 'restart', 'named'])
+
+    # Check that named is running and has loaded the information from LDAP
+    dig_command = ['dig', 'SRV', '+short', '@localhost',
+               '_ldap._tcp.%s' % host.domain.name]
+    dig_output = '0 100 389 %s.' % host.hostname
+    dig_test = lambda x: re.search(re.escape(dig_output), x)
+
+    assert util.run_repeatedly(host, dig_command, test=dig_test)
+
+
+def configure_dns_for_trust(master, ad):
+    """
+    This configures DNS on IPA master according to the relationship of the
+    IPA's and AD's domains.
+    """
+
+    def is_subdomain(subdomain, domain):
+        subdomain_unpacked = subdomain.split('.')
+        domain_unpacked = domain.split('.')
+
+        subdomain_unpacked.reverse()
+        domain_unpacked.reverse()
+
+        subdomain = False
+
+        if len(subdomain_unpacked) > len(domain_unpacked):
+            subdomain = True
+
+            for i in range(len(domain_unpacked)):
+                subdomain = (subdomain and
+                             subdomain_unpacked[i] == domain_unpacked[i])
+
+        return subdomain
+
+    kinit_admin(master)
+
+    if is_subdomain(master.domain.name, ad.domain.name):
+        master.run_command(['ipa', 'dnszone-add', ad.domain.name,
+                            '--name-server', ad.hostname,
+                            '--admin-email', 'hostmaster@%s' % ad.domain.name,
+                            '--forwarder', ad.ip,
+                            '--forward-policy', 'only',
+                            '--ip-address', ad.ip,
+                            '--force'])
+    elif is_subdomain(ad.domain.name, master.domain.name):
+        master.run_command(['ipa', 'dnsrecord-add', master.domain.name,
+                            '%s.%s' % (ad.shortname, ad.netbios),
+                            '--a-ip-address', ad.ip])
+
+        master.run_command(['ipa', 'dnsrecord-add', master.domain.name,
+                            ad.netbios,
+                            '--ns-hostname',
+                            '%s.%s' % (ad.shortname, ad.netbios)])
+
+        master.run_command(['ipa', 'dnszone-mod', master.domain.name,
+                            '--allow-transfer', ad.ip])
+    else:
+        master.run_command(['ipa', 'dnszone-add', ad.domain.name,
+                            '--name-server', ad.hostname,
+                            '--admin-email', 'hostmaster@%s' % ad.domain.name,
+                            '--forwarder', ad.ip,
+                            '--forward-policy', 'only',
+                            '--ip-address', ad.ip,
+                            '--force'])
+
+
+def estabilish_trust_with_ad(master, ad, extra_args=[]):
+    """
+    Estabilishes trust with Active Directory. Trust type is detected depending
+    on the presence of SfU (Services for Unix) support on the AD.
+
+    Use extra arguments to pass extra arguments to the trust-add command, such
+    as --range-type="ipa-ad-trust" to enfroce a particular range type.
+    """
+
+    # Force KDC to reload MS-PAC info by trying to get TGT for HTTP
+    master.run_command(['kinit', '-kt', '/etc/httpd/conf/ipa.keytab',
+                        'HTTP/%s' % master.hostname])
+    master.run_command(['systemctl', 'restart', 'krb5kdc.service'])
+
+    kinit_admin(master)
+    master.run_command(['klist'])
+    master.run_command(['smbcontrol', 'all', 'debug', '100'])
+    assert util.run_repeatedly(master,
+                               ['ipa', 'trust-add',
+                               '--type', 'ad', ad.domain.name,
+                               '--admin', 'Administrator',
+                               '--password'] + extra_args,
+                               stdin_text=master.config.ad_admin_password)
+    master.run_command(['smbcontrol', 'all', 'debug', '1'])
+    clear_sssd_cache(master)
+
+
+def remove_trust_with_ad(master, ad):
+    """
+    Removes trust with Active Directory. Also removes the associated ID range.
+    """
+
+    kinit_admin(master)
+
+    # Remove the trust
+    master.run_command(['ipa', 'trust-del', ad.domain.name])
+
+    # Remove the range
+    range_name = ad.domain.name.upper() + '_id_range'
+    master.run_command(['ipa', 'idrange-del', range_name])
+
+
+def configure_auth_to_local_rule(master, ad):
+    """
+    Configures auth_to_local rule in /etc/krb5.conf
+    """
+
+    section_identifier = " %s = {" % master.domain.realm
+    line1 = ("  auth_to_local = RULE:[1:$1@$0](^.*@%s$)s/@%s/@%s/"
+             % (ad.domain.realm, ad.domain.realm, ad.domain.name))
+    line2 = "  auth_to_local = DEFAULT"
+
+    krb5_conf_content = master.get_file_contents('/etc/krb5.conf')
+    krb5_lines = [line.rstrip() for line in krb5_conf_content.split('\n')]
+    realm_section_index = krb5_lines.index(section_identifier)
+
+    krb5_lines.insert(realm_section_index + 1, line1)
+    krb5_lines.insert(realm_section_index + 2, line2)
+
+    krb5_conf_new_content = '\n'.join(krb5_lines)
+    master.put_file_contents('/etc/krb5.conf', krb5_conf_new_content)
+
+    master.run_command(['systemctl', 'restart', 'sssd'])
+
+
+def clear_sssd_cache(host):
+    """
+    Clears SSSD cache by removing the cache files. Restarts SSSD.
+    """
+
+    host.run_command(['systemctl', 'stop', 'sssd'])
+    host.run_command(['rm', '-rfv', '/var/lib/sss/db/cache_%s.ldb'
+                                    % host.domain.name])
+    host.run_command(['rm', '-rfv', '/var/lib/sss/mc/group'])
+    host.run_command(['rm', '-rfv', '/var/lib/sss/mc/passwd'])
+    host.run_command(['systemctl', 'start', 'sssd'])
+
+
+def sync_time(host, server):
+    """
+    Syncs the time with the remote server. Please note that this function
+    leaves ntpd stopped.
+    """
+
+    host.run_command(['sudo', 'systemctl', 'stop', 'ntpd'])
+    host.run_command(['sudo', 'ntpdate', server.hostname])
+
+
 def connect_replica(master, replica):
     kinit_admin(replica)
     replica.run_command(['ipa-replica-manage', 'connect', master.hostname])
-- 
1.8.3.1

From e92c16402f76c4f5dc49db2e3514de627a1199ec Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 4 Sep 2013 16:26:23 +0200
Subject: [PATCH 104/106] ipatests: Add WinHost class

---
 ipatests/test_integration/host.py | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/ipatests/test_integration/host.py b/ipatests/test_integration/host.py
index a5cced84cde6afea6d4d3ecc98a7cc93eb32d6b5..2938556a40dcea7b41157fa39128ec836e872112 100644
--- a/ipatests/test_integration/host.py
+++ b/ipatests/test_integration/host.py
@@ -42,6 +42,8 @@ class BaseHost(object):
         self.hostname = shortname + '.' + self.domain.name
         self.external_hostname = hostname
 
+        self.netbios = self.domain.name.split('.')[0].upper()
+
         self.logger_name = '%s.%s.%s' % (
             self.__module__, type(self).__name__, shortname)
         self.log = log_mgr.get_logger(self.logger_name)
@@ -94,6 +96,12 @@ class BaseHost(object):
     def from_env(cls, env, domain, hostname, role, index):
         ip = env.get('BEAKER%s%s_IP_env%s' %
                         (role.upper(), index, domain.index), None)
+
+        if role == 'ad':
+            cls = WinHost
+        else:
+            cls = Host
+
         self = cls(domain, hostname, role, index, ip)
         return self
 
@@ -217,3 +225,14 @@ class Host(BaseHost):
 
         command.wait(raiseonerr=raiseonerr)
         return command
+
+
+class WinHost(Host):
+    """
+    Representation of a remote Windows host.
+
+    This serves as a sketch class once we move from manual preparation of
+    Active Directory to the automated setup.
+    """
+
+    pass
-- 
1.8.3.1

From 11635f4613b3be2bdc4533800c37020ea09e9759 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Thu, 26 Sep 2013 13:18:59 +0200
Subject: [PATCH 103/106] ipatests: Create util module for ipatests

---
 ipatests/test_integration/util.py | 55 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 ipatests/test_integration/util.py

diff --git a/ipatests/test_integration/util.py b/ipatests/test_integration/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..14318f0dc207be8f3cfdf7c55826e44903322717
--- /dev/null
+++ b/ipatests/test_integration/util.py
@@ -0,0 +1,55 @@
+# Authors:
+#   Tomas Babej <tba...@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/>.
+
+import time
+
+
+def run_repeatedly(host, command, assert_zero_rc=True, test=None,
+                timeout=30, **kwargs):
+    """
+    Runs command on host repeatedly until it's finished successfully (returns
+    0 exit code and its stdout passes the test function).
+
+    Returns True if the command was executed succesfully, False otherwise.
+
+    This method accepts additional kwargs and passes these arguments
+    to the actual run_command method.
+    """
+
+    time_waited = 0
+
+    # Check that the test is a function
+    if test:
+        assert callable(test)
+
+    while(time_waited <= timeout):
+        result = host.run_command(command, raiseonerr=False, **kwargs)
+
+        return_code_ok = not assert_zero_rc or (result.returncode == 0)
+        test_ok = not test or test(result.stdout_text)
+
+        if return_code_ok and test_ok:
+            # Command successful
+            return True
+        else:
+            # Command not successful
+            time.sleep(2)
+            time_waited += 2
+
+    return False
-- 
1.8.3.1

From 477a1f3d614771dbd9759588f657a5c402fb0b35 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 4 Sep 2013 15:02:21 +0200
Subject: [PATCH 102/106] ipatests: Extend IntegrationTest with multiple AD
 domain support

---
 ipatests/test_integration/base.py | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/ipatests/test_integration/base.py b/ipatests/test_integration/base.py
index 43360a83a4d2df656051beada7b69556066c2801..1bed7d55b0e89f88507165807e01ca7e185f2c80 100644
--- a/ipatests/test_integration/base.py
+++ b/ipatests/test_integration/base.py
@@ -35,10 +35,19 @@ log = log_mgr.get_logger(__name__)
 class IntegrationTest(object):
     num_replicas = 0
     num_clients = 0
+    num_ad_domains = 0
     topology = None
 
     @classmethod
     def setup_class(cls):
+
+        def get_resources(resource_container, resource_str, num_needed):
+            if len(resource_container) < num_needed:
+                raise nose.SkipTest(
+                    'Not enough %s available (have %s, need %s)' %
+                    (resource_str, len(resource_container), num_needed))
+            return resource_container[:num_needed]
+
         config = get_global_config()
         if not config.domains:
             raise nose.SkipTest('Integration testing not configured')
@@ -46,17 +55,15 @@ class IntegrationTest(object):
         cls.logs_to_collect = {}
 
         domain = config.domains[0]
+
         cls.master = domain.master
-        if len(domain.replicas) < cls.num_replicas:
-            raise nose.SkipTest(
-                'Not enough replicas available (have %s, need %s)' %
-                (len(domain.replicas), cls.num_replicas))
-        if len(domain.clients) < cls.num_clients:
-            raise nose.SkipTest(
-                'Not enough clients available (have %s, need %s)' %
-                (len(domain.clients), cls.num_clients))
-        cls.replicas = domain.replicas[:cls.num_replicas]
-        cls.clients = domain.clients[:cls.num_clients]
+        cls.replicas = get_resources(domain.replicas, 'replicas',
+                                     cls.num_replicas)
+        cls.clients = get_resources(domain.clients, 'clients',
+                                    cls.num_clients)
+        cls.ad_domains = get_resources(config.ad_domains, 'AD domains',
+                                       cls.num_ad_domains)
+
         for host in cls.get_all_hosts():
             host.add_log_collector(cls.collect_log)
             cls.prepare_host(host)
@@ -95,6 +102,7 @@ class IntegrationTest(object):
             del cls.master
             del cls.replicas
             del cls.clients
+            del cls.ad_domains
 
     @classmethod
     def uninstall(cls):
-- 
1.8.3.1

From 416f33f569f59c835c9de0524c54304b0bba055d Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 4 Sep 2013 14:24:41 +0200
Subject: [PATCH 101/106] ipatests: Extend domain object with 'ad' role support
 and WinHosts

---
 ipatests/test_integration/config.py | 36 +++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 19 deletions(-)

diff --git a/ipatests/test_integration/config.py b/ipatests/test_integration/config.py
index ac0131e435d33b6730093cbc64a1b47910d3971e..01fb9bd3aefc99fae327cb34abe678cf0780792f 100644
--- a/ipatests/test_integration/config.py
+++ b/ipatests/test_integration/config.py
@@ -27,7 +27,7 @@ import random
 from ipapython import ipautil
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import log_mgr
-from ipatests.test_integration.host import Host
+from ipatests.test_integration.host import BaseHost
 
 
 class Config(object):
@@ -248,7 +248,7 @@ def env_normalize(env):
 
 
 class Domain(object):
-    """Configuration for an IPA domain"""
+    """Configuration for an IPA / AD domain"""
     def __init__(self, config, name, index):
         self.log = log_mgr.get_logger(self)
 
@@ -263,27 +263,21 @@ class Domain(object):
         self.basedn = DN(*(('dc', p) for p in name.split('.')))
 
     @classmethod
-    def from_env(cls, env, config, index):
-        try:
-            default_domain = env['DOMAIN']
-        except KeyError:
-            hostname, dot, default_domain = env['MASTER_env1'].partition('.')
-        parts = default_domain.split('.')
+    def from_env(cls, env, config, index, ad_domain=False):
 
-        if index == 1:
-            name = default_domain
-        else:
-            # For $DOMAIN = dom.example.com, additional domains are
-            # dom1.example.com, dom2.example.com, etc.
-            parts[0] += str(index)
-            name = '.'.join(parts)
+        master_role = 'AD' if ad_domain else 'MASTER'
+        master_env = '%s_env%s' % (master_role, index)
 
-        self = cls(config, name, index)
+        hostname, dot, domain_name = env[master_env].partition('.')
 
-        for role in 'master', 'replica', 'client', 'other':
+        self = cls(config, domain_name, index)
+
+        for role in 'master', 'replica', 'client', 'ad', 'other':
             value = env.get('%s%s' % (role.upper(), self._env), '')
             for index, hostname in enumerate(value.split(), start=1):
-                host = Host.from_env(env, self, hostname, role, index)
+
+                host = BaseHost.from_env(env, self, hostname, role, index)
+
                 self.hosts.append(host)
 
         if not self.hosts:
@@ -318,9 +312,13 @@ class Domain(object):
         return [h for h in self.hosts if h.role == 'client']
 
     @property
+    def ads(self):
+        return [h for h in self.hosts if h.role == 'ad']
+
+    @property
     def other_hosts(self):
         return [h for h in self.hosts
-                if h.role not in ('master', 'client', 'replica')]
+                if h.role not in ('master', 'client', 'replica', 'ad')]
 
     def host_by_name(self, name):
         for host in self.hosts:
-- 
1.8.3.1

From 491da21e5e3551736e2e1363a0391816d90f5f69 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 4 Sep 2013 14:12:28 +0200
Subject: [PATCH 100/106] ipatests: Add Active Directory support to
 configuration

---
 ipatests/man/ipa-test-config.1      | 13 +++++++++++++
 ipatests/test_integration/config.py | 21 +++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/ipatests/man/ipa-test-config.1 b/ipatests/man/ipa-test-config.1
index 4b998adb4f65265f4b0cfc49cac2979740562db5..786380791eb52a25aa6d36a5d1793afcd948d3eb 100644
--- a/ipatests/man/ipa-test-config.1
+++ b/ipatests/man/ipa-test-config.1
@@ -84,6 +84,11 @@ Host configuration:
 \fB$MASTER_env2\fR, \fB$REPLICA_env2\fR, \fB$CLIENT_env2\fR, \fB$MASTER_env3\fR, ...
     can be used for additional domains when needed
 .TP
+\fB$AD_env1\fR, \fB$AD_env2\fR, \fB$AD_env3\fR, \fB$AD_env4\fR, ...
+    can be used to define Active Directory domains. Please note that these
+    domains are separate from the IPA domains in that sense that AD_env1 does
+    not have to correspond only to the MASTER_env1.
+.TP
 \fB$BEAKER\fR<role><num>\fB_IP_env\fR<e>, e.g. \fB$BEAKERREPLICA1_IP_env1\fR
     the IP address of the given host
     Default: resolved via gethostbyname (or DNS if $IPv6SETUP is set)
@@ -139,6 +144,14 @@ Test customization:
     Admin user password
     Default: Secret123
 .TP
+\fB$ADADMINID\fR
+    Active Directory Administrator username
+    Default: Administrator
+.TP
+\fB$ADADMINPW\fR
+    Active Directory Administrator password
+    Default: Secret123
+.TP
 \fB$ROOTDN\fR
     Directory manager DN
     Default: cn=Directory Manager
diff --git a/ipatests/test_integration/config.py b/ipatests/test_integration/config.py
index d43812c514bf1c9d23740e6f75cc3574235e86d3..ac0131e435d33b6730093cbc64a1b47910d3971e 100644
--- a/ipatests/test_integration/config.py
+++ b/ipatests/test_integration/config.py
@@ -1,5 +1,6 @@
 # Authors:
 #   Petr Viktorin <pvikt...@redhat.com>
+#   Tomas Babej <tba...@redhat.com>
 #
 # Copyright (C) 2013  Red Hat
 # see file 'COPYING' for use and warranty information
@@ -50,11 +51,14 @@ class Config(object):
         self.nis_domain = kwargs.get('nis_domain') or 'ipatest'
         self.ntp_server = kwargs.get('ntp_server') or (
             '%s.pool.ntp.org' % random.randint(0, 3))
+        self.ad_admin_name = kwargs.get('ad_admin_name') or 'Administrator'
+        self.ad_admin_password = kwargs.get('ad_admin_password') or 'Secret123'
 
         if not self.root_password and not self.root_ssh_key_filename:
             self.root_ssh_key_filename = '~/.ssh/id_rsa'
 
         self.domains = []
+        self.ad_domains = []
 
     @classmethod
     def from_env(cls, env):
@@ -76,6 +80,8 @@ class Config(object):
         ADMINPW: Administrator password
         ROOTDN: Directory Manager DN
         ROOTDNPWD: Directory Manager password
+        ADADMINID: Active Directory Administrator username
+        ADADMINPW: Active Directory Administrator password
         DNSFORWARD: DNS forwarder
         NISDOMAIN
         NTPSERVER
@@ -83,6 +89,7 @@ class Config(object):
         MASTER_env1: FQDN of the master
         REPLICA_env1: space-separated FQDNs of the replicas
         CLIENT_env1: space-separated FQDNs of the clients
+        AD_env1: space-separated FQDNs of the Active Directories
         OTHER_env1: space-separated FQDNs of other hosts
         (same for _env2, _env3, etc)
         BEAKERREPLICA1_IP_env1: IP address of replica 1 in env 1
@@ -104,13 +111,23 @@ class Config(object):
                    dns_forwarder=env.get('DNSFORWARD'),
                    nis_domain=env.get('NISDOMAIN'),
                    ntp_server=env.get('NTPSERVER'),
+                   ad_admin_name=env.get('ADADMINID'),
+                   ad_admin_password=env.get('ADADMINPW'),
                    )
 
+        # Either IPA master or AD can define a domain
+
         domain_index = 1
         while env.get('MASTER_env%s' % domain_index):
             self.domains.append(Domain.from_env(env, self, domain_index))
             domain_index += 1
 
+        domain_index = 1
+        while env.get('AD_env%s' % domain_index):
+            self.ad_domains.append(Domain.from_env(env, self, domain_index,
+                                                   ad_domain=True))
+            domain_index += 1
+
         return self
 
     def to_env(self, simple=True):
@@ -133,6 +150,9 @@ class Config(object):
         env['ROOTDN'] = str(self.dirman_dn)
         env['ROOTDNPWD'] = self.dirman_password
 
+        env['ADADMINID'] = self.ad_admin_name
+        env['ADADMINPW'] = self.ad_admin_password
+
         env['DNSFORWARD'] = self.dns_forwarder
         env['NISDOMAIN'] = self.nis_domain
         env['NTPSERVER'] = self.ntp_server
@@ -145,6 +165,7 @@ class Config(object):
             for role, hosts in [('MASTER', domain.masters),
                                 ('REPLICA', domain.replicas),
                                 ('CLIENT', domain.clients),
+                                ('AD', domain.ads),
                                 ('OTHER', domain.other_hosts)]:
                 hostnames = ' '.join(h.hostname for h in hosts)
                 env['%s%s' % (role, domain._env)] = hostnames
-- 
1.8.3.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to