def test_compact_db_task(topo): """Test creation of dbcompact task is successful :id: 1b3222ef-a336-4259-be21-6a52f76e1859 :customerscenario: True :setup: Standalone Instance :steps: 1. Create task 2. Check task was successful 3. Check errors log to show task was run 4. Create task just for changelog :expectedresults: 1. Success 2. Success 3. Success 4. Success """ inst = topo.ms["supplier1"] task = DBCompactTask(inst) task.create() task.wait() assert task.get_exit_code() == 0 # Check errors log to make sure task actually compacted db assert inst.searchErrorsLog("Compacting databases") inst.deleteErrorLogs() # Create new task that only compacts changelog task = DBCompactTask(inst) task_properties = {'justChangelog': 'yes'} task.create(properties=task_properties) task.wait() assert task.get_exit_code() == 0 # On bdb, check errors log to make sure task only performed changelog compaction # Note: as mdb contains a single map file (the justChangelog flags has # no impact (and whole db is compacted)) if get_default_db_lib() == "bdb": assert inst.searchErrorsLog("Compacting DB") == False assert inst.searchErrorsLog("Compacting Replication Changelog") inst.deleteErrorLogs(restart=False)
def create_from_cli(self): # Ask questions to generate general, slapd, and backends print('Install Directory Server (interactive mode)') print('===========================================') # Set the defaults general = { 'config_version': 2, 'full_machine_name': socket.getfqdn(), 'strict_host_checking': False, 'selinux': True, 'systemd': ds_paths.with_systemd, 'defaults': '999999999', 'start': True } slapd = { 'self_sign_cert_valid_months': 24, 'group': ds_paths.group, 'root_dn': ds_paths.root_dn, 'initconfig_dir': ds_paths.initconfig_dir, 'self_sign_cert': True, 'root_password': '', 'port': 389, 'instance_name': 'localhost', 'user': ds_paths.user, 'secure_port': 636, 'prefix': ds_paths.prefix, 'bin_dir': ds_paths.bin_dir, 'sbin_dir': ds_paths.sbin_dir, 'sysconf_dir': ds_paths.sysconf_dir, 'data_dir': ds_paths.data_dir, 'local_state_dir': ds_paths.local_state_dir, 'ldapi': ds_paths.ldapi, 'lib_dir': ds_paths.lib_dir, 'run_dir': ds_paths.run_dir, 'tmp_dir': ds_paths.tmp_dir, 'cert_dir': ds_paths.cert_dir, 'config_dir': ds_paths.config_dir, 'inst_dir': ds_paths.inst_dir, 'backup_dir': ds_paths.backup_dir, 'db_dir': ds_paths.db_dir, 'db_home_dir': ds_paths.db_home_dir, 'db_lib': get_default_db_lib(), 'ldif_dir': ds_paths.ldif_dir, 'lock_dir': ds_paths.lock_dir, 'log_dir': ds_paths.log_dir, 'schema_dir': ds_paths.schema_dir } # Let them know about the selinux status if not selinux_present(): val = input( '\nSelinux support will be disabled, continue? [yes]: ') if val.strip().lower().startswith('n'): return # Start asking questions, beginning with the hostname... val = input('\nEnter system\'s hostname [{}]: '.format( general['full_machine_name'])).rstrip() if val != "": general['full_machine_name'] = val # Instance name - adjust defaults once set while 1: slapd['instance_name'] = general['full_machine_name'].split( '.', 1)[0] # Check if default server id is taken if self._server_id_taken(slapd['instance_name'], prefix=slapd['prefix']): slapd['instance_name'] = "" val = input('\nEnter the instance name [{}]: '.format( slapd['instance_name'])).rstrip() if val != "": if len(val) > 80: print( "Server identifier should not be longer than 80 symbols" ) continue if not all(ord(c) < 128 for c in val): print( "Server identifier can not contain non ascii characters" ) continue if ' ' in val: print("Server identifier can not contain a space") continue if val == 'admin': print( "Server identifier \"admin\" is reserved, please choose a different identifier" ) continue # Check that valid characters are used safe = re.compile(r'^[#%:\w@_-]+$').search if not bool(safe(val)): print( "Server identifier has invalid characters, please choose a different value" ) continue # Check if server id is taken if self._server_id_taken(val, prefix=slapd['prefix']): print( "Server identifier \"{}\" is already taken, please choose a new name" .format(val)) continue # instance name is good slapd['instance_name'] = val break elif slapd['instance_name'] == "": continue else: # Check if default server id is taken if self._server_id_taken(slapd['instance_name'], prefix=slapd['prefix']): print( "Server identifier \"{}\" is already taken, please choose a new name" .format(slapd['instance_name'])) continue break # Finally have a good server id, adjust the default paths for key, value in slapd.items(): if isinstance(value, str): slapd[key] = value.format(instance_name=slapd['instance_name']) # Non-Secure Port if not socket_check_open('::1', slapd['port']): port = get_port(slapd['port'], slapd['port']) else: # Port 389 is already taken, pick another port port = get_port(slapd['port'], "") slapd['port'] = port # Self-Signed Cert DB while 1: val = input('\nCreate self-signed certificate database [yes]: ' ).rstrip().lower() if val != "": if val == 'no' or val == "n": slapd['self_sign_cert'] = False break elif val == "yes" or val == "y": # Default value is already yes break else: print('Invalid value "{}", please use "yes" or "no"') continue else: # use default break # Secure Port (only if using self signed cert) if slapd['self_sign_cert']: if not socket_check_open('::1', slapd['secure_port']): port = get_port(slapd['secure_port'], slapd['secure_port'], secure=True) else: # Port 636 is already taken, pick another port port = get_port(slapd['secure_port'], "", secure=True) slapd['secure_port'] = port else: slapd['secure_port'] = False # Root DN while 1: val = input('\nEnter Directory Manager DN [{}]: '.format( slapd['root_dn'])).rstrip() if val != '': # Validate value is a DN if is_a_dn(val, allow_anon=False): slapd['root_dn'] = val break else: print('The value "{}" is not a valid DN'.format(val)) continue else: # use default break # Root DN Password while 1: rootpw1 = getpass.getpass( '\nEnter the Directory Manager password: '******'': print('Password can not be empty') continue if len(rootpw1) < 8: print('Password must be at least 8 characters long') continue rootpw2 = getpass.getpass( 'Confirm the Directory Manager Password: '******'Passwords do not match') continue # Passwords match, set it slapd['root_password'] = rootpw1 break # Backend [{'name': 'userroot', 'suffix': 'dc=example,dc=com'}] backend = {'name': 'userroot', 'suffix': ''} backends = [backend] suffix = '' domain_comps = general['full_machine_name'].split('.') for comp in domain_comps: if suffix == '': suffix = "dc=" + comp else: suffix += ",dc=" + comp while 1: val = input( "\nEnter the database suffix (or enter \"none\" to skip) [{}]: " .format(suffix)).rstrip() if val != '': if val.lower() == "none": # No database, no problem backends = [] break if is_a_dn(val, allow_anon=False): backend['suffix'] = val break else: print("The suffix \"{}\" is not a valid DN".format(val)) continue else: backend['suffix'] = suffix break # Add sample entries or root suffix entry? if len(backends) > 0: while 1: val = input("\nCreate sample entries in the suffix [no]: " ).rstrip().lower() if val != "": if val == "no" or val == "n": break if val == "yes" or val == "y": backend['sample_entries'] = INSTALL_LATEST_CONFIG break # Unknown value print( "Value \"{}\" is invalid, please use \"yes\" or \"no\"" .format(val)) continue else: break if 'sample_entries' not in backend: # Check if they want to create the root node entry instead while 1: val = input("\nCreate just the top suffix entry [no]: " ).rstrip().lower() if val != "": if val == "no" or val == "n": break if val == "yes" or val == "y": backend['create_suffix_entry'] = True break # Unknown value print( "Value \"{}\" is invalid, please use \"yes\" or \"no\"" .format(val)) continue else: break # Start the instance? while 1: val = input( '\nDo you want to start the instance after the installation? [yes]: ' ).rstrip().lower() if val == '' or val == 'yes' or val == 'y': # Default behaviour break elif val == "no" or val == 'n': general['start'] = False break else: print('Invalid value, please use \"yes\" or \"no\"') continue # Are you ready? while 1: val = input('\nAre you ready to install? [no]: ').rstrip().lower() if val == '' or val == "no" or val == 'n': print('Aborting installation...') sys.exit(0) elif val == 'yes' or val == 'y': # lets do it! break else: print('Invalid value, please use \"yes\" or \"no\"') continue self.create_from_args(general, slapd, backends, self.extra) return True
task = DBCompactTask(inst) task_properties = {'justChangelog': 'yes'} task.create(properties=task_properties) task.wait() assert task.get_exit_code() == 0 # On bdb, check errors log to make sure task only performed changelog compaction # Note: as mdb contains a single map file (the justChangelog flags has # no impact (and whole db is compacted)) if get_default_db_lib() == "bdb": assert inst.searchErrorsLog("Compacting DB") == False assert inst.searchErrorsLog("Compacting Replication Changelog") inst.deleteErrorLogs(restart=False) @pytest.mark.skipif(get_default_db_lib() == "mdb", reason="Not supported over mdb") def test_compaction_interval_and_time(topo): """Test dbcompact is successful when nsslapd-db-compactdb-interval and nsslapd-db-compactdb-time is set :id: f361bee9-d7e7-4569-9255-d7b60dd9d92e :customerscenario: True :setup: Supplier Instance :steps: 1. Configure compact interval and time 2. Check compaction occurs as expected :expectedresults: 1. Success 2. Success """
def test_huge_index_key(topo, add_users): """ Test very long indexed attribute values (that should be hashed on mdb) :id: 4bbd0ee2-0108-11ec-a5ce-482ae39447e5 :customerscenario: False :setup: Standalone instance :steps: 1. Add users 2. Change nsslapd-idlistscanlimit to a smaller value to accelerate the reproducer 3. Replace sn with a 600 bytes value 4. equality search for the sn 5. Range search including the sn 6. Replace sn back with small value 7. equality search for the sn 8. Range search including the sn :expectedresults: 1. Should succeed 2. Should succeed 3. Should succeed 4. Should succeed and have exactly 1 result search should be indexed. 5. Should succeed and have exactly 3 results on bdb: search should be indexed. on mdb: search should be unindexed. 6. Should succeed 7. Should succeed and have exactly 1 result search should be indexed. 8. Should succeed and have exactly 3 results search should be indexed. """ inst = topo.standalone ldc = super(DirSrv, inst) # ldap connection to be able to use # the SimpleLDAPObject methods shortsn = 'test_020' test_user = users_list[20] log.debug( f'Check user {test_user} sn: {test_user.get_attr_val_utf8("sn")}') assert (test_user) users = UserAccounts(topo.standalone, DEFAULT_SUFFIX, rdn=None) log.debug(f'Check users {users.list()}') sn600b = shortsn + \ "0001abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0002abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0003abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0004abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0005abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0006abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0007abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0008abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0009abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" + \ "0010abcdefghijklmnopqrstuvwxyz0001abcdefghijklmnopqrstuvwxyz" test_user.replace('sn', sn600b) result = ldc.search_s(base=DEFAULT_SUFFIX, scope=ldap.SCOPE_SUBTREE, filterstr=f'(sn={sn600b})') assert (len(result) == 1) assert (not inst.searchAccessLog("notes=U")) result = ldc.search_s(base=DEFAULT_SUFFIX, scope=ldap.SCOPE_SUBTREE, filterstr=f'(&(sn>=test_019)(sn<=test_021))') #with pytest.raises(ldap.INVALID_SYNTAX): assert (len(result) == 3) if (get_default_db_lib() == "bdb"): assert (not inst.searchAccessLog("notes=U")) else: assert (inst.searchAccessLog("notes=U")) inst.deleteLog(inst.accesslog) test_user.replace('sn', shortsn) result = ldc.search_s(base=DEFAULT_SUFFIX, scope=ldap.SCOPE_SUBTREE, filterstr=f'(sn={shortsn})') assert (len(result) == 1) assert (not inst.searchAccessLog("notes=U")) result = ldc.search_s(base=DEFAULT_SUFFIX, scope=ldap.SCOPE_SUBTREE, filterstr=f'(&(sn>=test_019)(sn<=test_021))') assert (len(result) == 3) assert (not inst.searchAccessLog("notes=U"))
if DEBUGGING: logging.getLogger(__name__).setLevel(logging.DEBUG) else: logging.getLogger(__name__).setLevel(logging.INFO) log = logging.getLogger(__name__) bdb_values = { 'wait30' : 30 } # Note: I still sometime get failure with a 60s timeout so lets use 90s mdb_values = { 'wait30' : 90 } if get_default_db_lib() is 'bdb': values = bdb_values else: values = mdb_values def _generate_ldif(topo, no_no): """ Will generate the ldifs """ ldif_dir = topo.standalone.get_ldif_dir() import_ldif = ldif_dir + '/basic_import.ldif' if os.path.isfile(import_ldif): pass else: dbgen_users(topo.standalone, no_no, import_ldif, DEFAULT_SUFFIX)
REPLICATION_TRANSPORT, DEFAULT_BACKUPDIR, RA_NAME, RA_BINDDN, RA_BINDPW, RA_METHOD, RA_TRANSPORT_PROT, defaultProperties) import json pytestmark = pytest.mark.tier1 DEBUGGING = os.getenv("DEBUGGING", default=False) if DEBUGGING: logging.getLogger(__name__).setLevel(logging.DEBUG) else: logging.getLogger(__name__).setLevel(logging.INFO) log = logging.getLogger(__name__) @pytest.mark.skipif(get_default_db_lib() != "bdb", reason="Test requires bdb files") def test_mail_attr_repl(topo_r): """Check that no crash happens during mail attribute replication :id: 959edc84-05be-4bf9-a541-53afae482052 :customerscenario: True :setup: Replication setup with supplier and consumer instances, test user on supplier :steps: 1. Check that user was replicated to consumer 2. Back up mail database file 3. Remove mail attribute from the user entry 4. Restore mail database 5. Search for the entry with a substring 'mail=user*' 6. Search for the entry once again to make sure that server is alive
def __init__(self, log): super(Slapd2Base, self).__init__(log) self._section = 'slapd' self._options['instance_name'] = 'localhost' self._type['instance_name'] = str self._helptext['instance_name'] = "Sets the name of the instance. You can refer to this value in other parameters of this INF file using the \"{instance_name}\" variable. Note that this name cannot be changed after the installation!" self._options['user'] = ds_paths.user self._type['user'] = str self._helptext['user'] = "******" self._advanced['user'] = True self._options['group'] = ds_paths.group self._type['group'] = str self._helptext['group'] = "Sets the group name the ns-slapd process will use after the service started." self._advanced['group'] = True self._options['root_dn'] = ds_paths.root_dn self._type['root_dn'] = str self._helptext['root_dn'] = "Sets the Distinquished Name (DN) of the administrator account for this instance. It is recommended that you do not change this value from the default \"cn=Directory Manager\"" self._advanced['root_dn'] = True self._options['root_password'] = '******' self._type['root_password'] = str self._helptext['root_password'] = ("Sets the password of the \"cn=Directory Manager\" account (\"root_dn\" parameter)." + "You can either set this parameter to a plain text password dscreate hashes " + "during the installation or to a \"{algorithm}hash\" string generated by the " + "pwdhash utility. The password must be at least 8 characters long. Note " + "that setting a plain text password can be a security risk if unprivileged " + "users can read this INF file!") self._options['prefix'] = ds_paths.prefix self._type['prefix'] = str self._helptext['prefix'] = "Sets the file system prefix for all other directories. You can refer to this value in other fields using the {prefix} variable or the $PREFIX environment variable. Only set this parameter in a development environment." self._advanced['prefix'] = True self._options['port'] = 389 self._type['port'] = int self._helptext['port'] = "Sets the TCP port the instance uses for LDAP connections." self._options['secure_port'] = 636 self._type['secure_port'] = int self._helptext['secure_port'] = "Sets the TCP port the instance uses for TLS-secured LDAP connections (LDAPS)." self._options['self_sign_cert'] = True self._type['self_sign_cert'] = bool self._helptext['self_sign_cert'] = "Sets whether the setup creates a self-signed certificate and enables TLS encryption during the installation. The certificate is not suitable for production, but it enables administrators to use TLS right after the installation. You can replace the self-signed certificate with a certificate issued by a Certificate Authority. If set to False, you can enable TLS later by importing a CA/Certificate and enabling 'dsconf <instance_name> config replace nsslapd-security=on'" self._options['self_sign_cert_valid_months'] = 24 self._type['self_sign_cert_valid_months'] = int self._helptext['self_sign_cert_valid_months'] = "Set the number of months the issued self-signed certificate will be valid." # In the future, make bin and sbin /usr/[s]bin, but we may need autotools assistance from Ds self._options['bin_dir'] = ds_paths.bin_dir self._type['bin_dir'] = str self._helptext['bin_dir'] = "Sets the location where the Directory Server binaries are stored. Only set this parameter in a development environment." self._advanced['bin_dir'] = True self._options['sbin_dir'] = ds_paths.sbin_dir self._type['sbin_dir'] = str self._helptext['sbin_dir'] = "Sets the location where the Directory Server administration binaries are stored. Only set this parameter in a development environment." self._advanced['sbin_dir'] = True self._options['sysconf_dir'] = ds_paths.sysconf_dir self._type['sysconf_dir'] = str self._helptext['sysconf_dir'] = "Sets the location of the system's configuration directory. Only set this parameter in a development environment." self._advanced['sysconf_dir'] = True self._options['initconfig_dir'] = ds_paths.initconfig_dir self._type['initconfig_dir'] = str self._helptext['initconfig_dir'] = "Sets the directory of the operating system's rc configuration directory. Only set this parameter in a development environment." self._advanced['initconfig_dir'] = True # In the future, make bin and sbin /usr/[s]bin, but we may need autotools assistance from Ds self._options['data_dir'] = ds_paths.data_dir self._type['data_dir'] = str self._helptext['data_dir'] = "Sets the location of Directory Server shared static data. Only set this parameter in a development environment." self._advanced['data_dir'] = True self._options['local_state_dir'] = ds_paths.local_state_dir self._type['local_state_dir'] = str self._helptext['local_state_dir'] = "Sets the location of Directory Server variable data. Only set this parameter in a development environment." self._advanced['local_state_dir'] = True self._options['ldapi'] = ds_paths.ldapi self._type['ldapi'] = str self._helptext['ldapi'] = "Sets the location of socket interface of the Directory Server." self._options['lib_dir'] = ds_paths.lib_dir self._type['lib_dir'] = str self._helptext['lib_dir'] = "Sets the location of Directory Server shared libraries. Only set this parameter in a development environment." self._advanced['lib_dir'] = True self._options['cert_dir'] = ds_paths.cert_dir self._type['cert_dir'] = str self._helptext['cert_dir'] = "Sets the directory of the instance's Network Security Services (NSS) database." self._advanced['cert_dir'] = True self._options['config_dir'] = ds_paths.config_dir self._type['config_dir'] = str self._helptext['config_dir'] = "Sets the configuration directory of the instance." self._advanced['config_dir'] = True self._options['inst_dir'] = ds_paths.inst_dir self._type['inst_dir'] = str self._helptext['inst_dir'] = "Sets the directory of instance-specific scripts." self._advanced['inst_dir'] = True self._options['backup_dir'] = ds_paths.backup_dir self._type['backup_dir'] = str self._helptext['backup_dir'] = "Set the backup directory of the instance." self._advanced['backup_dir'] = True self._options['db_dir'] = ds_paths.db_dir self._type['db_dir'] = str self._helptext['db_dir'] = "Sets the database directory of the instance." self._advanced['db_dir'] = True self._options['db_home_dir'] = ds_paths.db_home_dir self._type['db_home_dir'] = str self._helptext['db_home_dir'] = "Sets the memory-mapped database files location of the instance." self._advanced['db_home_dir'] = True self._options['db_lib'] = get_default_db_lib() self._type['db_lib'] = str self._helptext['db_lib'] = "Select the database implementation library (bdb or mdb)." self._advanced['db_lib'] = True self._options['ldif_dir'] = ds_paths.ldif_dir self._type['ldif_dir'] = str self._helptext['ldif_dir'] = "Sets the LDIF export and import directory of the instance." self._advanced['ldif_dir'] = True self._options['lock_dir'] = ds_paths.lock_dir self._type['lock_dir'] = str self._helptext['lock_dir'] = "Sets the lock directory of the instance." self._advanced['lock_dir'] = True self._options['log_dir'] = ds_paths.log_dir self._type['log_dir'] = str self._helptext['log_dir'] = "Sets the log directory of the instance." self._advanced['log_dir'] = True self._options['run_dir'] = ds_paths.run_dir self._type['run_dir'] = str self._helptext['run_dir'] = "Sets PID directory of the instance." self._advanced['run_dir'] = True self._options['schema_dir'] = ds_paths.schema_dir self._type['schema_dir'] = str self._helptext['schema_dir'] = "Sets schema directory of the instance." self._advanced['schema_dir'] = True self._options['tmp_dir'] = ds_paths.tmp_dir self._type['tmp_dir'] = str self._helptext['tmp_dir'] = "Sets the temporary directory of the instance." self._advanced['tmp_dir'] = True
def initInstance(self): if (self._instance): return self._instance uidpath = self.getFilePath("uids") nb_uids = 0 try: with open(uidpath, 'r') as f: while f.readline(): nb_uids += 1 except FileNotFoundError: pass nb_users = self._options['nbUsers'] need_rebuild = True if (nb_uids == nb_users): # Lets try to reuse existing instance try: self._instance = DirSrv(verbose=True) self._instance.local_simple_allocate(serverid="standalone1", password=PW_DM) self._instance.open() if (self._instance.exists()): if (self._instance.get_db_lib() == get_default_db_lib()): need_rebuild = False else: print( f"db is {self._instance.get_db_lib()} instead of {get_default_db_lib()} ==> instance must be rebuild" ) else: print(f"missing instance ==> instance must be rebuild") except Exception: pass else: print( f"Instance has {nb_uids} users instead of {nb_users} ==> instance must be rebuild" ) if (need_rebuild): print("Rebuilding standalone1 instance") # Should rebuild the instance from scratch topology = create_topology({ReplicaRole.STANDALONE: 1}) self._instance = topology.standalone # Adjust db size if needed (i.e about 670 K users) defaultDBsize = 1073741824 entrySize = 1600 # Real size is around 1525 if (self._instance.get_db_lib() == "mdb" and nb_users * entrySize > defaultDBsize): mdb_config = LMDB_LDBMConfig(self._instance) mdb_config.replace("nsslapd-mdb-max-size", str(nb_users * entrySize)) self._instance.restart() # Then populate the users useraccounts = UserAccounts(self._instance, self._options['suffix']) with open(uidpath, 'w') as f: uidgen = IdGeneratorWithNumbers(nb_users) cnGen = IdGeneratorWithNames(100) snGen = IdGeneratorWithNames(100) for uid in uidgen: cn = cnGen.random() sn = snGen.random() rdn = f"uid={uid}" osuid = uidgen.getIdx() + 1000 osgid = int(osuid % 100) + 1000 properties = { 'uid': uid, 'cn': cn, 'sn': sn, 'uidNumber': str(osuid), 'gidNumber': str(osgid), 'homeDirectory': f'/home/{uid}' } super(UserAccounts, useraccounts).create(rdn, properties) f.write(f'{uid}\n') return self._instance