On 05/22/2015 12:36 PM, Petr Vobornik wrote:
On 05/22/2015 07:08 AM, Jan Cholasta wrote:
Dne 21.5.2015 v 18:18 Tomas Babej napsal(a):


On 05/19/2015 04:07 PM, Tomas Babej wrote:


On 05/19/2015 03:59 PM, Martin Kosek wrote:
On 05/19/2015 03:56 PM, Tomas Babej wrote:

On 05/19/2015 03:51 PM, Martin Kosek wrote:
On 05/19/2015 03:49 PM, Ludwig Krispenz wrote:
On 05/19/2015 03:36 PM, Martin Kosek wrote:
On 05/19/2015 03:22 PM, Tomas Babej wrote:
...
3) Domain level is just a single integer and it should be
treated as such,
there's no need for an LDAPObject plugin and other unnecessary
complexities.
The implemetation could be as simple as (from top of my head,
untested):
That's right, I also considered this approach, but as far as I
know you do
not
get the permission handling for the global DomainLevel entry
otherwise.

Ludwig, I changed the path for the global entry to cn=DomainLevel.
I know this particular DN was added to the design by Simo, but
why do we want
to use CamelCase with LDAP object?

Wouldn't "cn=Domain Level,cn=ipa,cn=etc,SUFFIX" be a better place
for it? This
is the last time we can change it, so I am asking now. Then, we
will be stuck
with this DN forever.
I don't mind using ""cn=Domain Level" ,

but where does the entry live, here you say

cn=Domain Level,cn=ipa,cn=etc,SUFFIX"

and in the design page it is:

cn=DomainLevel,cn=etc,SUFFIX

The current version of the topology plugin is looking for

cn=DomainLevel,cn=ipa,cn=etc,SUFFIX"
but I want to change it to do a search on
objectclass=ipaDomainLevelConfig
I see - we all need to unify the location apparently. I updated the
design page
to use "cn=Domain Level,cn=ipa,cn=etc,SUFFIX". Tomas, please send
the updated
patch set, it should be an extremely simple change :-)
I prefer the ipa parent and the space in the name, so I'm glad we
could agree
on this without much bikeshedding.

Updated patch attaced.

Tomas


I still see

+# Create default Domain Level entry if it does not exist
+dn: cn=DomainLevel,cn=ipa,cn=etc,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: objectClass: ipaDomainLevelConfig
+default: ipaDomainLevel: 0

...

Right, the space eluded me there, thanks for the catch.

Tomas

A new iteration of the patch, including the server-side checks for the
installers.

Tomas

1) https://www.redhat.com/archives/freeipa-devel/2015-May/msg00228.html
- I still don't agree that the plugin should be based on LDAPObject.

On the other hand, with LDAPObject base, Web UI for this feature is much more simpler because it can rely on existing conventions.

IMHO we can swap the approach in a later patch, if we decide it's necessary. It does not block or relate to other features much.



2) Use api domainlevel-show call to get the current domain level in
ipa-replica-install instead of duplicating the code.
I chose the former approach since the domainlevel_show command doesn't need to be available, but yeah, this can be properly detected and worked around too. Fixed.


3) Set the domain level in DSInstance.create_instance instead of a
separate call in ipa-server-install. It should be done about the same
time as the master entry is added.

4) I think the option should be named --domain-level (with a dash), for
consistency.



All other issues fixed.

Updated patch atttached.

Tomas

From f4894ec2b002d0ddac5dab71233aa78f297993cf Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Thu, 14 May 2015 10:49:55 +0200
Subject: [PATCH] Add Domain Level feature

https://fedorahosted.org/freeipa/ticket/5018
---
 ACI.txt                                |   2 +
 API.txt                                |  22 ++++++
 install/share/72domainlevels.ldif      |   6 ++
 install/share/Makefile.am              |   1 +
 install/tools/ipa-replica-install      |  34 +++++++-
 install/tools/ipa-server-install       |  13 +++-
 install/updates/72-domainlevels.update |  14 ++++
 install/updates/Makefile.am            |   1 +
 ipalib/constants.py                    |   3 +
 ipalib/errors.py                       |  16 ++++
 ipalib/plugins/domainlevel.py          | 137 +++++++++++++++++++++++++++++++++
 ipaserver/install/dsinstance.py        |  15 ++++
 ipaserver/install/ldapupdate.py        |   5 ++
 13 files changed, 264 insertions(+), 5 deletions(-)
 create mode 100644 install/share/72domainlevels.ldif
 create mode 100644 install/updates/72-domainlevels.update
 create mode 100644 ipalib/plugins/domainlevel.py

diff --git a/ACI.txt b/ACI.txt
index bf539892910f14ebc3fbee88a72d2b57c0d1327b..d38d1c6f5e7f3095704e2c205a52316b4f39f469 100644
--- a/ACI.txt
+++ b/ACI.txt
@@ -50,6 +50,8 @@ dn: dc=ipa,dc=example
 aci: (target = "ldap:///idnsname=*,cn=dns,dc=ipa,dc=example";)(version 3.0;acl "permission:System: Remove DNS Entries";allow (delete) groupdn = "ldap:///cn=System: Remove DNS Entries,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: dc=ipa,dc=example
 aci: (targetattr = "a6record || aaaarecord || afsdbrecord || arecord || certrecord || cn || cnamerecord || dlvrecord || dnamerecord || dnsclass || dnsttl || dsrecord || hinforecord || idnsallowdynupdate || idnsallowquery || idnsallowsyncptr || idnsallowtransfer || idnsforwarders || idnsforwardpolicy || idnsname || idnssecinlinesigning || idnssoaexpire || idnssoaminimum || idnssoamname || idnssoarefresh || idnssoaretry || idnssoarname || idnssoaserial || idnsupdatepolicy || idnszoneactive || keyrecord || kxrecord || locrecord || managedby || mdrecord || minforecord || mxrecord || naptrrecord || nsec3paramrecord || nsecrecord || nsrecord || nxtrecord || ptrrecord || rrsigrecord || sigrecord || srvrecord || sshfprecord || tlsarecord || txtrecord")(target = "ldap:///idnsname=*,cn=dns,dc=ipa,dc=example";)(version 3.0;acl "permission:System: Update DNS Entries";allow (write) groupdn = "ldap:///cn=System: Update DNS Entries,cn=permissions,cn=pbac,dc=ipa,dc=example";)
+dn: dc=ipa,dc=example
+aci: (targetattr = "createtimestamp || entryusn || ipadomainlevel || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipadomainlevelconfig)")(version 3.0;acl "permission:System: Read Domain Level";allow (compare,read,search) userdn = "ldap:///all";;)
 dn: cn=groups,cn=accounts,dc=ipa,dc=example
 aci: (targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Add Groups";allow (add) groupdn = "ldap:///cn=System: Add Groups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=groups,cn=accounts,dc=ipa,dc=example
diff --git a/API.txt b/API.txt
index 0808f3c64595495c8a9e60da5cbd689d5cdc6224..7ee7618a51d8f1ea7a64cddd1e6550616c1cca29 100644
--- a/API.txt
+++ b/API.txt
@@ -1283,6 +1283,28 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: domainlevel_set
+args: 0,8,3
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Int('ipadomainlevel', attribute=True, autofill=False, cli_name='level', minvalue=0, multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
+command: domainlevel_show
+args: 0,4,3
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: PrimaryKey('value', None, None)
 command: env
 args: 1,3,4
 arg: Str('variables*')
diff --git a/install/share/72domainlevels.ldif b/install/share/72domainlevels.ldif
new file mode 100644
index 0000000000000000000000000000000000000000..8473eebb47e1a77fa476f4a7eb1bc04b98fa0813
--- /dev/null
+++ b/install/share/72domainlevels.ldif
@@ -0,0 +1,6 @@
+dn: cn=schema
+attributeTypes: (2.16.840.1.113730.3.8.19.2.1 NAME 'ipaDomainLevel' DESC 'Domain Level value' EQUALITY numericStringMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE X-ORIGIN 'IPA v4')
+attributeTypes: (2.16.840.1.113730.3.8.19.2.2 NAME 'ipaMinDomainLevel' DESC 'Minimal supported Domain Level value' EQUALITY numericStringMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE X-ORIGIN 'IPA v4')
+attributeTypes: (2.16.840.1.113730.3.8.19.2.3 NAME 'ipaMaxDomainLevel' DESC 'Maximal supported Domain Level value' EQUALITY numericStringMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE X-ORIGIN 'IPA v4')
+objectClasses:  (2.16.840.1.113730.3.8.19.1.1  NAME 'ipaDomainLevelConfig' SUP ipaConfigObject AUXILIARY DESC 'Domain Level Configuration' MUST (ipaDomainLevel) X-ORIGIN 'IPA v4')
+objectClasses:  (2.16.840.1.113730.3.8.19.1.2  NAME 'ipaSupportedDomainLevelConfig' SUP ipaConfigObject AUXILIARY DESC 'Supported Domain Level Configuration' MUST (ipaMinDomainLevel $ ipaMaxDomainLevel) X-ORIGIN 'IPA v4')
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index ca6128e2911ab5c0a773dd553f8e67eab944f120..ac4538ba9547243c28d27a196f943b5bc2bdcd93 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -21,6 +21,7 @@ app_DATA =				\
 	65ipasudo.ldif			\
 	70ipaotp.ldif			\
 	71idviews.ldif			\
+	72domainlevels.ldif			\
 	anonymous-vlv.ldif		\
 	bootstrap-template.ldif		\
 	caJarSigningCert.cfg.template	\
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index f68cc8cf4722264ecea2f1f50de3aa245be24ef9..a415c293ea6fc2b8c195e6123bbca9686fa0e5e1 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -43,7 +43,7 @@ from ipaserver.install import cainstance
 from ipaserver.install import krainstance
 from ipaserver.install import dns as dns_installer
 from ipalib import api, create_api, errors, util, certstore, x509
-from ipalib.constants import CACERT
+from ipalib import constants
 from ipapython import version
 from ipapython.config import IPAOptionParser
 from ipapython import sysrestore
@@ -224,12 +224,12 @@ def install_ca_cert(ldap, base_dn, realm, cafile):
         try:
             certs = certstore.get_ca_certs(ldap, base_dn, realm, False)
         except errors.NotFound:
-            shutil.copy(cafile, CACERT)
+            shutil.copy(cafile, constants.CACERT)
         else:
             certs = [c[0] for c in certs if c[2] is not False]
-            x509.write_certificate_list(certs, CACERT)
+            x509.write_certificate_list(certs, constants.CACERT)
 
-        os.chmod(CACERT, 0444)
+        os.chmod(constants.CACERT, 0444)
     except Exception, e:
         print "error copying files: " + str(e)
         sys.exit(1)
@@ -569,6 +569,32 @@ def main():
                 print "    %% ipa-replica-manage del %s --force" % config.host_name
                 exit(3)
 
+            # Detect the current domain level
+            try:
+                result = remote_api.Command['domainlevel_show']['result']
+            except KeyError:
+                # If we're joining an older master, domainlevel_show is not
+                # available
+                current = 0
+            else:
+                current = int(result['ipadomainlevel'][0])
+
+            # Detect if current level is out of supported range
+            # for this IPA version
+            under_lower_bound = current < constants.MIN_DOMAIN_LEVEL
+            above_upper_bound = current > constants.MAX_DOMAIN_LEVEL
+
+            if under_lower_bound or above_upper_bound:
+                message = ("This version of FreeIPA does not support "
+                           "the Domain Level which is currently set for "
+                           "this domain. The Domain Level needs to be "
+                           "raised before installing a replica with "
+                           "this version is allowed to be installed "
+                           "within this domain.")
+                root_logger.error(message)
+                print(message)
+                exit(3)
+
             # Check pre-existing host entry
             try:
                 entry = conn.find_entries(u'fqdn=%s' % config.host_name, ['fqdn'], DN(api.env.container_host, api.env.basedn))
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index cb6e1abe2016c0f8cefc35b1d685373f05b3ef89..dfee19d9d5fc927a17e913eb73e7148dcd0bbba9 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -70,7 +70,7 @@ from ipapython import sysrestore
 from ipapython.ipautil import *
 from ipapython import ipautil
 from ipapython import dogtag
-from ipalib import api, errors, util, x509
+from ipalib import api, errors, util, x509, constants
 from ipapython.config import IPAOptionParser
 from ipalib.util import validate_domain_name
 from ipalib.constants import CACERT
@@ -176,6 +176,8 @@ def parse_options():
                            help="create home directories for users "
                                 "on their first login")
     basic_group.add_option("--hostname", dest="host_name", help="fully qualified name of server")
+    basic_group.add_option("--domain-level", dest="domainlevel", help="IPA domain level",
+                           default=constants.MAX_DOMAIN_LEVEL, type=int)
     basic_group.add_option("--ip-address", dest="ip_addresses",
                       type="ip", ip_local=True, action="append", default=[],
                       help="Master Server IP Address. This option can be used multiple times",
@@ -327,6 +329,15 @@ def parse_options():
         except ValueError, e:
             parser.error("invalid domain: " + unicode(e))
 
+    # Check that Domain Level is within the allowed range
+    if not options.uninstall:
+        if options.domainlevel < constants.MIN_DOMAIN_LEVEL:
+            parser.error("Domain Level cannot be lower than {0}"
+                         .format(constants.MIN_DOMAIN_LEVEL))
+        elif options.domainlevel > constants.MAX_DOMAIN_LEVEL:
+            parser.error("Domain Level cannot be higher than {0}"
+                         .format(constants.MAX_DOMAIN_LEVEL))
+
     if not options.setup_dns:
         if options.forwarders:
             parser.error("You cannot specify a --forwarder option without the --setup-dns option")
diff --git a/install/updates/72-domainlevels.update b/install/updates/72-domainlevels.update
new file mode 100644
index 0000000000000000000000000000000000000000..2e83c7be9b200121081470a80a3a9303d685a789
--- /dev/null
+++ b/install/updates/72-domainlevels.update
@@ -0,0 +1,14 @@
+# Create default Domain Level entry if it does not exist
+dn: cn=Domain Level,cn=ipa,cn=etc,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: objectClass: ipaDomainLevelConfig
+default: ipaDomainLevel: 0
+
+# Create entry proclaiming Domain Level support of this master
+# This will update the supported Domain Levels during upgrade
+dn: cn=$FQDN,cn=masters,cn=ipa,cn=etc,$SUFFIX
+add: objectClass: ipaConfigObject
+add: objectClass: ipaSupportedDomainLevelConfig
+only: ipaMinDomainLevel: $MIN_DOMAIN_LEVEL
+only: ipaMaxDomainLevel: $MAX_DOMAIN_LEVEL
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index 0d63d9ea8d85f1add5f036e7a39f89543586d33b..b52dc2570040336031b7fe01c1cec50156bd90c3 100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -48,6 +48,7 @@ app_DATA =				\
 	61-trusts-s4u2proxy.update	\
 	62-ranges.update		\
 	71-idviews.update		\
+	72-domainlevels.update		\
 	90-post_upgrade_plugins.update	\
 	$(NULL)
 
diff --git a/ipalib/constants.py b/ipalib/constants.py
index f1e14702ffdf5a3bd23a62b1fdd2ee3cd95d84f8..04e29d25b1b41dbb67c23d02b4a534ec1735e44c 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -223,3 +223,6 @@ LDAP_GENERALIZED_TIME_FORMAT = "%Y%m%d%H%M%SZ"
 
 IPA_ANCHOR_PREFIX = ':IPA:'
 SID_ANCHOR_PREFIX = ':SID:'
+
+MIN_DOMAIN_LEVEL = 0
+MAX_DOMAIN_LEVEL = 1
diff --git a/ipalib/errors.py b/ipalib/errors.py
index 89b1ef2e0dc1d7346a69fb813bd71990746c620c..63ec22269467b769d276c443f6b3dbed38cd766e 100644
--- a/ipalib/errors.py
+++ b/ipalib/errors.py
@@ -1344,6 +1344,22 @@ class EmptyResult(NotFound):
 
     errno = 4031
 
+class InvalidDomainLevelError(ExecutionError):
+    """
+    **4032** Raised when a operation could not be completed due to a invalid
+             domain level.
+
+    For example:
+
+    >>> raise InvalidDomainLevelError(reason='feature requires domain level 4')
+    Traceback (most recent call last):
+      ...
+    InvalidDomainLevelError: feature requires domain level 4
+
+    """
+
+    errno = 4032
+
 class BuiltinError(ExecutionError):
     """
     **4100** Base class for builtin execution errors (*4100 - 4199*).
diff --git a/ipalib/plugins/domainlevel.py b/ipalib/plugins/domainlevel.py
new file mode 100644
index 0000000000000000000000000000000000000000..dbc2a5916d39d9b857e40bba45d703db8a076ede
--- /dev/null
+++ b/ipalib/plugins/domainlevel.py
@@ -0,0 +1,137 @@
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+from collections import namedtuple
+
+from ipalib import _
+from ipalib import api
+from ipalib import Command
+from ipalib import errors
+from ipalib import output
+from ipalib.parameters import Int
+from ipalib.plugable import Registry
+from ipalib.plugins.baseldap import LDAPObject, LDAPUpdate, LDAPRetrieve
+
+from ipapython.dn import DN
+
+
+__doc__ = _("""
+Raise the IPA Domain Level.
+""")
+
+register = Registry()
+
+DomainLevelRange = namedtuple('DomainLevelRange', ['min', 'max'])
+
+
+@register()
+class domainlevel(LDAPObject):
+    """
+    IPA Domain Level object
+    """
+
+    object_name = _('domain level')
+    default_attributes = [
+        'ipadomainlevel',
+    ]
+
+    permission_filter_objectclasses = ['ipadomainlevelconfig']
+
+    managed_permissions = {
+        'System: Read Domain Level': {
+            'replaces_global_anonymous_aci': True,
+            'ipapermbindruletype': 'all',
+            'ipapermright': {'read', 'search', 'compare'},
+            'ipapermdefaultattr': {
+                'ipadomainlevel', 'objectclass',
+            },
+        },
+    }
+
+    label = _('Domain Level')
+    label_singular = _('Domain Level')
+
+    takes_params = (
+        Int('ipadomainlevel',
+            cli_name='level',
+            label=_('Domain Level'),
+            minvalue=0,
+        ),
+    )
+
+    def get_dn(self, *keys, **kwargs):
+        return DN(('cn', 'Domain Level'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+
+    def get_domainlevel_range(self, master_entry):
+        try:
+            return DomainLevelRange(
+                int(master_entry['ipaMinDomainLevel'][0]),
+                int(master_entry['ipaMaxDomainLevel'][0])
+            )
+        except KeyError:
+            return DomainLevelRange(0, 0)
+
+    def get_master_entries(self):
+        """
+        Returns list of LDAPEntries representing IPA masters.
+        """
+        ldap = self.backend
+
+        container_masters = DN(
+            ('cn', 'masters'),
+            ('cn', 'ipa'),
+            ('cn', 'etc'),
+            api.env.basedn
+        )
+
+        masters, _ = ldap.find_entries(
+            filter="(cn=*)",
+            base_dn=container_masters,
+            scope=ldap.SCOPE_ONELEVEL,
+            paged_search=True,  # we need to make sure to get all of them
+        )
+
+        return masters
+
+
+@register()
+class domainlevel_set(LDAPUpdate):
+    __doc__ = _('Modify configuration options.')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        """
+        Checks all the IPA masters for supported domain level ranges.
+
+        If the desired domain level is within the supported range of all
+        masters, it will be raised.
+
+        Domain level cannot be lowered.
+        """
+
+        assert isinstance(dn, DN)
+
+        current_value = int(ldap.get_entry(dn)['ipadomainlevel'][0])
+        desired_value = int(entry_attrs['ipadomainlevel'])
+
+        # Domain level cannot be lowered
+        if int(desired_value) < int(current_value):
+            message = _("Domain Level cannot be lowered.")
+            raise errors.InvalidDomainLevelError(message)
+
+        # Check if every master supports the desired level
+        for master in self.obj.get_master_entries():
+            supported = self.obj.get_domainlevel_range(master)
+
+            if supported.min > desired_value or supported.max < desired_value:
+                message = _("Domain Level cannot be raised to {0}, server {1} "
+                            "does not support it."
+                            .format(desired_value, master['cn'][0]))
+                raise errors.InvalidDomainLevelError(message)
+
+        return dn
+
+
+@register()
+class domainlevel_show(LDAPRetrieve):
+    __doc__ = _('Show the current domain level.')
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index f1d24e49d1b184efde1c8d18ff37d0e329037ccc..14c91250c2f5f6f701b9610dd7945851c300e0d0 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -61,6 +61,7 @@ IPA_SCHEMA_FILES = ("60kerberos.ldif",
                     "65ipasudo.ldif",
                     "70ipaotp.ldif",
                     "71idviews.ldif",
+                    "72domainlevels.ldif",
                     "15rfc2307bis.ldif",
                     "15rfc4876.ldif")
 
@@ -252,6 +253,7 @@ class DsInstance(service.Service):
     def __common_post_setup(self):
         self.step("initializing group membership", self.init_memberof)
         self.step("adding master entry", self.__add_master_entry)
+        self.step("initializing domain level", self.__set_domain_level)
         self.step("configuring Posix uid/gid generation",
                   self.__config_uidgid_gen)
         self.step("adding replication acis", self.__add_replication_acis)
@@ -1002,3 +1004,16 @@ class DsInstance(service.Service):
         root_logger.debug('Unable to find certificate subject base in '
                           'certmap.conf')
         return None
+
+    def __set_domain_level(self, domainlevel):
+        self.ldap_connect()
+
+        domainlevel_dn = DN(
+            ('cn', 'Domain Level'),
+            ('cn', 'ipa'),
+            ('cn', 'etc'),
+            self.suffix
+        )
+
+        mods = [(ldap.MOD_REPLACE, 'ipaDomainLevel', str(domainlevel))]
+        self.admin_conn.modify_s(domainlevel_dn, mods)
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 2f5bcc748eb546b4dad7e1aeeb7a2882a40d8d35..4aa463152b9ec05fb5e1de9e1a5e386f6fc46e6f 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -39,6 +39,7 @@ from ipaserver.install import installutils
 from ipapython import ipautil, ipaldap
 from ipalib import errors
 from ipalib import api, create_api
+from ipalib import constants
 from ipaplatform.paths import paths
 from ipaplatform import services
 from ipapython.dn import DN
@@ -305,6 +306,10 @@ class LDAPUpdate:
             self.sub_dict["TIME"] = int(time.time())
         if not self.sub_dict.get("DOMAIN") and domain is not None:
             self.sub_dict["DOMAIN"] = domain
+        if not self.sub_dict.get("MIN_DOMAIN_LEVEL"):
+            self.sub_dict["MIN_DOMAIN_LEVEL"] = str(constants.MIN_DOMAIN_LEVEL)
+        if not self.sub_dict.get("MAX_DOMAIN_LEVEL"):
+            self.sub_dict["MAX_DOMAIN_LEVEL"] = str(constants.MAX_DOMAIN_LEVEL)
         self.api = create_api(mode=None)
         self.api.bootstrap(in_server=True, context='updates')
         self.api.finalize()
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to