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
From f5bfc5256aecd78001904d77a3741d30c51c6207 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      |  39 +++++++++-
 install/tools/ipa-server-install       |  14 +++-
 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        |  14 ++++
 ipaserver/install/ldapupdate.py        |   5 ++
 13 files changed, 269 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..95b321666c33f9b15a71905bf49d96f616d39705 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,37 @@ def main():
                 print "    %% ipa-replica-manage del %s --force" % config.host_name
                 exit(3)
 
+            # Detect the current domain level
+            try:
+                domainlevel_dn = DN(
+                    ('cn', 'Domain Level'),
+                    ('cn', 'ipa'),
+                    ('cn', 'etc'),
+                    ipautil.realm_to_suffix(config.realm_name)
+                )
+
+                entry = conn.get_entry(domainlevel_dn, ['ipaDomainLevel'])
+                current = int(entry['ipaDomainLevel'][0])
+
+            except errors.NotFound:
+                current = 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..0cff09ec1186bf37cb03f36b64297b2c3a941d50 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("--domainlevel", 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")
@@ -1261,6 +1272,7 @@ def main():
     # is created
     service.print_msg("Applying LDAP updates")
     ds.apply_updates()
+    ds.set_domainlevel(options.domainlevel)
 
     # Restart ds and krb after configurations have been changed
     service.print_msg("Restarting the directory server")
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..c131494651f6550f23e33d5fe9d3e60e925b67fc 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")
 
@@ -1002,3 +1003,16 @@ class DsInstance(service.Service):
         root_logger.debug('Unable to find certificate subject base in '
                           'certmap.conf')
         return None
+
+    def set_domainlevel(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