#50287 Added python3 support and code fixes. [WIP]
Closed 3 years ago by spichugi. Opened 5 years ago by aadhikari.
aadhikari/389-ds-base upstream-fixes-new  into  master

@@ -15,8 +15,13 @@ 

  from lib389._constants import *

  from lib389.properties import *

  from lib389.topologies import topology_m2

+ from lib389.utils import *

+ from lib389.replica import BootstrapReplicationManager

+ from lib389.plugins import *

+ 

+ pytestmark = [pytest.mark.tier2,

+               pytest.mark.skipif(ds_is_newer('1.4.0'), reason="Upgrade scripts are supported only on versions < 1.4.x")]

  

- pytestmark = pytest.mark.tier2

  

  logging.getLogger(__name__).setLevel(logging.DEBUG)

  log = logging.getLogger(__name__)
@@ -44,76 +49,43 @@ 

      #

      # Add an extra attribute to the DES plugin args

      #

-     try:

-         topology_m2.ms["master1"].modify_s(DES_PLUGIN,

-                                            [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])

-     except ldap.LDAPError as e:

-         log.fatal('Failed to enable DES plugin, error: ' +

-                   e.message['desc'])

-         assert False

- 

-     try:

-         topology_m2.ms["master1"].modify_s(DES_PLUGIN,

-                                            [(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])

-     except ldap.LDAPError as e:

-         log.fatal('Failed to reset DES plugin, error: ' +

-                   e.message['desc'])

-         assert False

- 

-     try:

-         topology_m2.ms["master1"].modify_s(MMR_PLUGIN,

-                                            [(ldap.MOD_DELETE,

-                                              'nsslapd-plugin-depends-on-named',

-                                              'AES')])

- 

-     except ldap.NO_SUCH_ATTRIBUTE:

-         pass

-     except ldap.LDAPError as e:

-         log.fatal('Failed to reset MMR plugin, error: ' +

-                   e.message['desc'])

-         assert False

+     plugin_des = Plugin(topology_m2.ms["master1"], DES_PLUGIN)

+     plugin_des.set('nsslapd-pluginEnabled', 'on')

+     plugin_des.set('nsslapd-pluginarg2', 'description')

  

+     plugin_mmr = Plugin(topology_m2.ms["master1"], MMR_PLUGIN)

+     plugin_mmr.remove('nsslapd-plugin-depends-on-named', 'AES')

      #

      # Delete the AES plugin

      #

-     try:

-         topology_m2.ms["master1"].delete_s(AES_PLUGIN)

-     except ldap.NO_SUCH_OBJECT:

-         pass

-     except ldap.LDAPError as e:

-         log.fatal('Failed to delete AES plugin, error: ' +

-                   e.message['desc'])

-         assert False

- 

+     topology_m2.ms["master1"].delete_s(AES_PLUGIN)

      # restart the server so we must use DES plugin

      topology_m2.ms["master1"].restart(timeout=10)

  

-     #

-     # Get the agmt dn, and set the password

-     #

-     try:

-         entry = topology_m2.ms["master1"].search_s('cn=config', ldap.SCOPE_SUBTREE,

-                                                    'objectclass=nsDS5ReplicationAgreement')

-         if entry:

-             agmt_dn = entry[0].dn

-             log.info('Found agmt dn (%s)' % agmt_dn)

-         else:

-             log.fatal('No replication agreements!')

-             assert False

-     except ldap.LDAPError as e:

-         log.fatal('Failed to search for replica credentials: ' +

-                   e.message['desc'])

-         assert False

+     manager = BootstrapReplicationManager(topology_m2.ms["master2"])

  

-     try:

-         properties = {RA_BINDPW: "password"}

-         topology_m2.ms["master1"].agreement.setProperties(None, agmt_dn, None,

-                                                           properties)

-         log.info('Successfully modified replication agreement')

-     except ValueError:

-         log.error('Failed to update replica agreement: ' + AGMT_DN)

-         assert False

+     manager.create(properties={

+                 'cn': 'replication manager',

+                 'userPassword': 'password'

+     })

+ 

+     DN = topology_m2.ms["master2"].replica._get_mt_entry(DEFAULT_SUFFIX)

+ 

+     topology_m2.ms["master2"].modify_s(DN, [(ldap.MOD_REPLACE,

+                                              'nsDS5ReplicaBindDN', ensure_bytes(defaultProperties[REPLICATION_BIND_DN]))])

+     #

+     # Create repl agreement from the newly promoted master to master1

  

+     properties = {RA_NAME: 'meTo_{}:{}'.format(topology_m2.ms["master2"].host,

+                                                str(topology_m2.ms["master2"].port)),

+                   RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],

+                   RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],

+                   RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],

+                   RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}

+     topology_m2.ms["master1"].agreement.create(suffix=SUFFIX,

+                                                host=topology_m2.ms["master2"].host,

+                                                port=topology_m2.ms["master2"].port,

+                                                properties=properties)

      #

      # Check replication works with the new DES password

      #
@@ -139,7 +111,7 @@ 

          else:

              log.info('Replication test passed')

      except ldap.LDAPError as e:

-         log.fatal('Failed to add test user: ' + e.message['desc'])

+         log.fatal('Failed to add test user: ' + e.args[0]['desc'])

          assert False

  

      #
@@ -148,13 +120,15 @@ 

      try:

          topology_m2.ms["master1"].backend.create("o=empty", {BACKEND_NAME: "empty"})

      except ldap.LDAPError as e:

-         log.fatal('Failed to create extra/empty backend: ' + e.message['desc'])

+         log.fatal('Failed to create extra/empty backend: ' + e.args[0]['desc'])

          assert False

  

      #

      # Run the upgrade...

      #

-     topology_m2.ms["master1"].upgrade('online')

+     topology_m2.ms["master1"].stop()

+     topology_m2.ms["master2"].stop()

+     topology_m2.ms["master1"].upgrade('offline')

      topology_m2.ms["master1"].restart()

      topology_m2.ms["master2"].restart()

  
@@ -166,7 +140,7 @@ 

                                                     'nsDS5ReplicaCredentials=*')

          if entry:

              val = entry[0].getValue('nsDS5ReplicaCredentials')

-             if val.startswith('{AES-'):

+             if val.startswith(b'{AES-'):

                  log.info('The DES credentials have been converted to AES')

              else:

                  log.fatal('Failed to convert credentials from DES to AES!')
@@ -176,7 +150,7 @@ 

              assert False

      except ldap.LDAPError as e:

          log.fatal('Failed to search for replica credentials: ' +

-                   e.message['desc'])

+                   e.args[0]['desc'])

          assert False

  

      #
@@ -196,7 +170,7 @@ 

          else:

              log.info('The AES plugin was correctly setup')

      except ldap.LDAPError as e:

-         log.fatal('Failed to find AES plugin: ' + e.message['desc'])

+         log.fatal('Failed to find AES plugin: ' + e.args[0]['desc'])

          assert False

  

      #
@@ -211,7 +185,7 @@ 

          else:

              log.info('The MMR plugin was correctly updated')

      except ldap.LDAPError as e:

-         log.fatal('Failed to find AES plugin: ' + e.message['desc'])

+         log.fatal('Failed to find AES plugin: ' + e.args[0]['desc'])

          assert False

  

      #
@@ -226,7 +200,7 @@ 

          else:

              log.info('The DES plugin was correctly updated')

      except ldap.LDAPError as e:

-         log.fatal('Failed to find AES plugin: ' + e.message['desc'])

+         log.fatal('Failed to find AES plugin: ' + e.args[0]['desc'])

          assert False

  

      #
@@ -253,7 +227,7 @@ 

          else:

              log.info('Replication test passed')

      except ldap.LDAPError as e:

-         log.fatal('Failed to add test user: ' + e.message['desc'])

+         log.fatal('Failed to add test user: ' + e.args[0]['desc'])

          assert False

  

      # Check the entry
@@ -269,7 +243,7 @@ 

              assert False

      except ldap.LDAPError as e:

          log.fatal('Failed to search for entries: ' +

-                   e.message['desc'])

+                   e.args[0]['desc'])

          assert False

  

      #
@@ -282,7 +256,7 @@ 

                                                  'suffix': DEFAULT_SUFFIX,

                                                  'cn': 'convert'})))

      except ldap.LDAPError as e:

-         log.fatal('Failed to add task entry: ' + e.message['desc'])

+         log.fatal('Failed to add task entry: ' + e.args[0]['desc'])

          assert False

  

      # Wait for task
@@ -300,7 +274,7 @@ 

          if entry:

              val = entry[0].getValue('description')

              print(str(entry[0]))

-             if val.startswith('{AES-'):

+             if val.startswith(b'{AES-'):

                  log.info('Task: DES credentials have been converted to AES')

              else:

                  log.fatal('Task: Failed to convert credentials from DES to ' +
@@ -311,7 +285,7 @@ 

              assert False

      except ldap.LDAPError as e:

          log.fatal('Failed to search for entries: ' +

-                   e.message['desc'])

+                   e.args[0]['desc'])

          assert False

  

  

@@ -66,11 +66,12 @@ 

                                                  (ldap.MOD_REPLACE, 'nsslapd-ssl-check-hostname', b'off'),

                                                  (ldap.MOD_REPLACE, 'nsslapd-secureport', ensure_bytes(LDAPSPORT))])

  

-     topology_st.standalone.add_s(Entry((RSA_DN, {'objectclass': "top nsEncryptionModule".split(),

-                                                  'cn': RSA,

-                                                  'nsSSLPersonalitySSL': SERVERCERT,

-                                                  'nsSSLToken': 'internal (software)',

-                                                  'nsSSLActivation': 'on'})))

+     if ds_is_older('1.4.0'):

+         topology_st.standalone.add_s(Entry((RSA_DN, {'objectclass': "top nsEncryptionModule".split(),

+                                                      'cn': RSA,

+                                                      'nsSSLPersonalitySSL': SERVERCERT,

+                                                      'nsSSLToken': 'internal (software)',

+                                                      'nsSSLActivation': 'on'})))

  

  

  def connectWithOpenssl(topology_st, cipher, expect):
@@ -87,7 +88,7 @@ 

      myurl = 'localhost:%s' % LDAPSPORT

      cmdline = ['/usr/bin/openssl', 's_client', '-connect', myurl, '-cipher', cipher]

  

-     strcmdline = '/usr/bin/openssl s_client -connect localhost:%s -cipher %s' % (LDAPSPORT, cipher)

+     strcmdline = " ".join(cmdline)

Subprocess shouldn't need a "string", because it takes an array, so actually you would want "cmdline = strcmdline.split[" "]" and then give that to subprocess, and remove the "shell=True" option as it's insecure. nss_ssl.py has great examples of secure cli subprocess usage.

      log.info("Running cmdline: %s", strcmdline)

  

      try:

file modified
+51 -48
@@ -52,7 +52,8 @@ 

      getdefaultsuffix,

      ensure_bytes,

      ensure_str,

-     socket_check_open,)

+     socket_check_open,

+     ds_is_older,)

  from lib389.passwd import password_hash, password_generate

  

  
@@ -862,7 +863,7 @@ 

                              # We just want to make sure it's in there somewhere

                              if expectedHost in words:

                                  return True

-             except AssertionError:

+             except AssertionError:  

                  raise AssertionError(

                      "Error: %s should contain '%s' host for %s" %

                      ('/etc/hosts', expectedHost, ipPattern))
@@ -903,53 +904,55 @@ 

  

              /prefix/lib[64]/dirsrv/slapd-INSTANCE/

          '''

- 

-         libdir = os.path.join(_ds_paths.lib_dir, 'dirsrv')

- 

-         # Gather all the instances so we can adjust the permissions, otherwise

-         servers = []

-         path = os.path.join(_ds_paths.sysconf_dir, 'dirsrv')

-         for files in os.listdir(path):

-             if files.startswith('slapd-') and not files.endswith('.removed'):

-                 servers.append(os.path.join(libdir, files))

- 

-         if len(servers) == 0:

-             # This should not happen

-             log.fatal('runUpgrade: no servers found!')

-             assert False

- 

-         '''

-         The setup script calls things like /lib/dirsrv/slapd-instance/db2bak,

-         etc, and when we run the setup perl script it gets permission denied

-         as the default permissions are 750.  Adjust the permissions to 755.

-         '''

-         for instance in servers:

-             for files in os.listdir(instance):

-                 os.chmod(os.path.join(instance, files), 755)

- 

-         # Run the "upgrade"

-         try:

-             prog = os.path.join(_ds_paths.sbin_dir, PATH_SETUP_DS)

-             process = subprocess.Popen([prog, '--update'], shell=False,

+         if ds_is_older('1.4.0'):

+             libdir = os.path.join(_ds_paths.lib_dir, 'dirsrv')

+ 

+             # Gather all the instances so we can adjust the permissions, otherwise

+             servers = []

+             path = os.path.join(_ds_paths.sysconf_dir, 'dirsrv')

+             for files in os.listdir(path):

+                 if files.startswith('slapd-') and not files.endswith('.removed'):

+                     servers.append(os.path.join(libdir, files))

+ 

+             if len(servers) == 0:

+                 # This should not happen

+                 log.fatal('runUpgrade: no servers found!')

+                 assert False

+ 

+             '''

+             The setup script calls things like /lib/dirsrv/slapd-instance/db2bak,

+             etc, and when we run the setup perl script it gets permission denied

+             as the default permissions are 750.  Adjust the permissions to 755.

+             '''

+             for instance in servers:

+                 for files in os.listdir(instance):

+                     os.chmod(os.path.join(instance, files), 755)

+ 

+             # Run the "upgrade"

+             try:

+                 prog = os.path.join(_ds_paths.sbin_dir, PATH_SETUP_DS)

+                 process = subprocess.Popen([prog, '--update'], shell=False,

                                         stdin=subprocess.PIPE)

-             # Answer the interactive questions, as "--update" currently does

-             # not work with INF files

-             process.stdin.write('yes\n')

-             if(online):

-                 process.stdin.write('online\n')

-                 for x in servers:

-                     process.stdin.write(DN_DM + '\n')

-                     process.stdin.write(PW_DM + '\n')

-             else:

-                 process.stdin.write('offline\n')

-             process.stdin.close()

-             process.wait()

-             if process.returncode != 0:

-                 log.fatal('runUpgrade failed!  Error: %s ' % process.returncode)

-                 assert(False)

-         except:

-             log.fatal('runUpgrade failed!')

-             raise

+                 # Answer the interactive questions, as "--update" currently does

+                 # not work with INF files

+                 process.stdin.write(b'yes\n')

+                 if(online):

+                     process.stdin.write(b'online\n')

+                     for x in servers:

+                         process.stdin.write(ensure_bytes(DN_DM + '\n'))

+                         process.stdin.write(ensure_bytes(PW_DM + '\n'))

+                 else:

+                     process.stdin.write(b'offline\n')

+                 process.stdin.close()

+                 process.wait()

+                 if process.returncode != 0:

+                     log.fatal('runUpgrade failed!  Error: %s ' % process.returncode)

+                     assert(False)

+             except:

+                 log.fatal('runUpgrade failed!')

+                 raise

+         else:

+             pass

  

      @staticmethod

      def searchFile(filename, pattern):

Description: Added py3 support by explicitly changing strings to bytes.
Codes are fixed, which are stated in each commit.

https://pagure.io/389-ds-base/issue/49588

Reviewed by: ??

Don't duplicate this, use strcmdline = " ".join(cmdline) instead.

Probably means you have a candidate for rewriting this modify to use the Plugin(DSldapObjects) api ....

Isn't there a "replication manager" dsldapobject you can use instead? IIRC I made one for the replication code a few years back.

In general, if you are going to port from py2 -> py3, that doesn't mean slap bytes on it, it means rewrite parts that need dsldapobjects instead.

I also would actually propose removing that crypto/tls cipher count test. It breaks every verison, it proves nothing, and generally is annoying to maintain in any capacity. I'd rather just remove it. @spichugi may have a comment on that though ....

Isn't there a "replication manager" dsldapobject you can use instead? IIRC I made one for the replication code a few years back.

It is, but in the test, replication is actually working but giving an invalid credential error for replication manager. I could not access the service accounts, so I have created another manager.

In general, if you are going to port from py2 -> py3, that doesn't mean slap bytes on it, it means rewrite parts that need dsldapobjects instead.
I also would actually propose removing that crypto/tls cipher count test. It breaks every verison, it proves nothing, and generally is annoying to maintain in any capacity. I'd rather just remove it.
+1
@spichugi may have a comment on that though...

I agree, py2 -> py3 is not just adding bytes, but there are a lot of failures which needs to be fixed first so that build quality could be analyzed first. there are a lot of tests which are failing and we are unable to have a proper say on the builds. We actually wanna avoid it as much we can, anyways I will still see we can quickly remove this :)

The associated commit message is entirely wrong.

EDIT: I meant the other one (3c7d46224 for dirsrvtests/tests/tickets/ticket48194_test.py).

Please explain what and why this fixes. What's the actual error? This really needs a comprehensive commit message.

Why this hardcoded value? Where does it come from? Cannot it be pulled in from anywhere?

AFAICT this file's change effectively removes a test for a feature from cab38f9 introduced just a few weeks ago. What is the story here?

This was introduced 3 weeks ago - why is this wrong? (not saying it is not, just need an explanation)

rebased onto 4737790b289136ab3d3e65dfa04626f09b28815d

5 years ago

1 new commit added

  • Revert "Description: Fixed the test by changing passwordInHistory value to 1,"
5 years ago

5 new commits added

  • Description: Added py3 support by explicitly changing strings to bytes.
  • Description: Fixed the test by removing "cn:RSA" entry from the test,
  • Description: Added py3 support by explicitly changing strings to bytes.
  • Description: Fixed the test by change replication manager's value.
  • Description: Fixed the test by changing passwordInHistory value to 1,
5 years ago

5 new commits added

  • Description: Description: Fixed the test by changing passwordInHistory value to 1,
  • Description: Fixed the test by removing "cn:RSA" entry from the test,
  • Description: Added py3 support by explicitly changing strings to bytes.
  • Description: Fixed the test by change replication manager's value.
  • Description: Fixed the test by changing passwordInHistory value to 1,
5 years ago

5 new commits added

  • Description: Fixed the test by removing "cn:RSA" entry from the second test,
  • Description: Fixed the test by removing "cn:RSA" entry from the test,
  • Description: Added py3 support by explicitly changing strings to bytes.
  • Description: Fixed the test by change replication manager's value.
  • Description: Fixed the test by changing passwordInHistory value to 1,
5 years ago

rebased onto 024aa5b302ffa054f65dc54520040f27517514fb

5 years ago

This was introduced 3 weeks ago - why is this wrong? (not saying it is not, just need an explanation)

Was having some merge conflict issue, resolved and fixed. also, meaningful commit message is been added.

This was introduced 3 weeks ago - why is this wrong? (not saying it is not, just need an explanation)

Was having some merge conflict issue, resolved and fixed. also, meaningful commit message is been added.

@mhonek But I see them passing in the latest builds will remove it from the commit.

rebased onto 8b7328b3d9947db82e8cbdba63cb1dd839c4a832

5 years ago

Please explain what and why this fixes. What's the actual error? This really needs a comprehensive commit message.

I will paste a complete output of the error, one was the cn=RSA entry which I removed. Will update with the error if the changes in the code are not done.

I think the openssl test is pretty bad anyway, like ... we are testing something that is really dynamic and fragile, and a bit, not our problem?

@aadhikari Out of curiosity, what is the build you're testing?

In general, tests should definitely pass on master. If they don't pass on older builds then relevant fixes are welcome of course, but only those that don't break the tests on master. E.g. with pwp_history_test.py here, (it should have been done so before, but we can do it now, too) we can properly make the test conditional using ds_is_older or ds_is_newer so that it passes on versions without the new feature and those with it, too.

@aadhikari Out of curiosity, what is the build you're testing?
In general, tests should definitely pass on master. If they don't pass on older builds then relevant fixes are welcome of course, but only those that don't break the tests on master. E.g. with pwp_history_test.py here, (it should have been done so before, but we can do it now, too) we can properly make the test conditional using ds_is_older or ds_is_newer so that it passes on versions without the new feature and those with it, too.

@mhonek I was testing on 389-ds-base: 1.4.0.21-1.fc29 and 389-ds-base: 1.4.1.1-20190320gitf16615485.fc29.

Also what I observed is the timing of the sleep. The same code may not Rework for the first time but can work for the second time.

Just for the clarification:
Result for build: 1.4.0.21-1.fc29 - https://paste.fedoraproject.org/paste/Xg3NZ61ilVgI0aT7Eej~iw
Result for build: 389-ds-base: 1.4.1.1-20190320gitf16615485.fc29 - https://paste.fedoraproject.org/paste/AJ~65WXImPX5ZDWZVeIqLw
Result form latest build: https://paste.fedoraproject.org/paste/KXRROyTs66LCS62hiHNBeg

So, form the result what I can get is we need to change the sleep to a line where the set of operation which is causing failures should be done then sleep should be used. Secondly, I am guessing, 0 as a minimum range value for passwordInHistory was acceptable before, we changed that in between and now it is back to the default range?

@aadhikari Not sure why not for you but the test passes for me even in a loop. I've tested with 1.4.1.1-20190320git33fbced25.fc28. I don't see a point in testing the 1.4.1.1-20190320gitf16615485.fc29 which is fairly old.

Looking at https://paste.fedoraproject.org/paste/KXRROyTs66LCS62hiHNBeg at the following (notice missing password1 there):

pwp_history_test.py        132 ERROR    password history: [b'20190320101020Zpassword', b'20190320101020Zpassword2', b'20190320101020Zpassword3']

it seems like user.set('userpassword', 'password1') does not set the new password correctly, which is why the test fails for you. I cannot reproduce, so please try to investigate why it happens for you.

@aadhikari Not sure why not for you but the test passes for me even in a loop. I've tested with 1.4.1.1-20190320git33fbced25.fc28. I don't see a point in testing the 1.4.1.1-20190320gitf16615485.fc29 which is fairly old.
Looking at https://paste.fedoraproject.org/paste/KXRROyTs66LCS62hiHNBeg at the following (notice missing password1 there):
pwp_history_test.py 132 ERROR password history: [b'20190320101020Zpassword', b'20190320101020Zpassword2', b'20190320101020Zpassword3']

it seems like user.set('userpassword', 'password1') does not set the new password correctly, which is why the test fails for you. I cannot reproduce, so please try to investigate why it happens for you.

The issue is not reproducible every time, will investigate.

Highly likely if you are seeing an odd behaviour, it's a race condidition between replication and the bind or similar, so I'd check you are using ReplicationManager's "wait_for_repl" instead of sleep. That's my first instinct here ....

Is there any update? Do you have any blocker that we can help you with?

rebased onto 37c3e8a558aa638a298dc083eb5913e78d5bb947

4 years ago

rebased onto eb71a079621f53948561f5a8b6b6c0964e114717

4 years ago

rebased onto 35ee0be0e8062978f01648bdd6b0e28e28f8f06a

4 years ago

rebased onto b7ba0f2562c9b2ead6d9247e5a4dd40df7a9d323

4 years ago

3 new commits added

  • Description: Fixed the test by removing "cn:RSA" entry from the second test,
  • Description: Explicitly changed strings to bytes in upgrade-script(tools.py)
  • Description: Fixed the test by removing "cn:RSA" entry from the test,
4 years ago

3 new commits added

  • Description: Fixed the failure by adding server version check,
  • Description: Explicitly changed strings to bytes in upgrade-script(tools.py)
  • Description: Fixed the test by removing "cn:RSA" entry from the test,
4 years ago

rebased onto 3dc678e95b291adf2b38194c46eb7bac3e9aca28

4 years ago

Isn't there a "replication manager" dsldapobject you can use instead? IIRC I made one for the replication code a few years back.

@firstyear
I tried using topology_m2.ms["master2"].replica.create_repl_manager() by passing/NOT passing password and dn, got this error: ERR - slapi_ldap_bind - Error: could not bind id [cn=replication manager,cn=config] authentication mechanism [SIMPLE]: error 49 (Invalid credentials)

@firstyear
Rest all the changes have been made, also @spichugi please let us know what we should be doing with ticket47838_test.py, the ciphers are very dynamic as mentioned by @firstyear
Thanks!

Isn't there a "replication manager" dsldapobject you can use instead? IIRC I made one for the replication code a few years back.

@firstyear
I tried using topology_m2.ms["master2"].replica.create_repl_manager() by passing/NOT passing password and dn, got this error: ERR - slapi_ldap_bind - Error: could not bind id [cn=replication manager,cn=config] authentication mechanism [SIMPLE]: error 49 (Invalid credentials)

Look at:

https://pagure.io/389-ds-base/blob/master/f/src/lib389/lib389/cli_conf/replication.py#_178

This is how you use the "BootstrapReplicationManager" to create the replication manager dn's

@firstyear
Rest all the changes have been made, also @spichugi please let us know what we should be doing with ticket47838_test.py, the ciphers are very dynamic as mentioned by @firstyear
Thanks!

rm ./dirsrvtests/tests/tickets/ticket47838_test.py

I never want to see it again, it's a silly test. I think it adds no value and no help.

@spichugi may have a different view though :)

@firstyear
Rest all the changes have been made, also @spichugi please let us know what we should be doing with ticket47838_test.py, the ciphers are very dynamic as mentioned by @firstyear
Thanks!

rm ./dirsrvtests/tests/tickets/ticket47838_test.py
I never want to see it again, it's a silly test. I think it adds no value and no help.
@spichugi may have a different view though :)

I agree with you. I think it makes sense to just get rid of this.
But I think the decision is up to @vashirov to make

I'm OK with removing ticket47838_test.py.

rebased onto d01f1a1e176211df9df786c18f0e5fc08d6cfe54

4 years ago

3 new commits added

  • Description: Removed the test as crypto/tls cipher count is very dynamic and breaks in every verison.
  • Description: Explicitly changed strings to bytes in upgrade-script(tools.py)
  • Description: Fixed the failure by adding server version check,
4 years ago

@firstyear Changes regarding the replication manager's creation is fixed, please review the rest of the code.

What does this option do? Could be good to comment this ....

Have a look at the function definition in replica.py: def __init__(self, instance, dn='cn=replication manager,cn=config'):. You don't need "defaultProperties[...]". Remember, lots of parts of lib389 are made to "do the right thing, in the simplest way"!

I think you only need "userPassword" attribute here, no cn needs to be provided?

Subprocess shouldn't need a "string", because it takes an array, so actually you would want "cmdline = strcmdline.split[" "]" and then give that to subprocess, and remove the "shell=True" option as it's insecure. nss_ssl.py has great examples of secure cli subprocess usage.

setup-ds.pl is deprecated, and may cause this test to break. What does the update do that you require? Consider running your test with --disable-perl on ./configure, and youll see where perl breaks things. If anything we shouldn't use def runUpgrade as a result ....

Subprocess shouldn't need a "string", because it takes an array, so actually you would want "cmdline = strcmdline.split[" "]" and then give that to subprocess, and remove the "shell=True" option as it's insecure. nss_ssl.py has great examples of secure cli subprocess usage.

the strcmdline is just for printing what we are executing if you see the whole code will give a better idea, BTW this change is done by your suggestion only.

Don't duplicate this, use strcmdline = " ".join(cmdline) instead.

What does this option do? Could be good to comment this ....

This is already being mentioned in the previous comments(At the start of the test), if that what you are asking?

setup-ds.pl is deprecated, and may cause this test to break. What does the update do that you require? Consider running your test with --disable-perl on ./configure, and youll see where perl breaks things. If anything we shouldn't use def runUpgrade as a result ....

@firstyear I think, setup-ds.pl is deprecated but it is still consumed by RHEL 7 and many other OS and these will still be present for a very long time. Rather than using only the latest way, I think ds_older check can be used here but we still need to fix runUpgrade. Tell me a better way of doing this because we cannot ignore older versions. BTW is there anything similar to runUpgrade in lib389 for the the dscreate? because that can be used here with ds_check.
Thanks!

Have a look at the function definition in replica.py: def init(self, instance, dn='cn=replication manager,cn=config'):. You don't need "defaultProperties[...]". Remember, lots of parts of lib389 are made to "do the right thing, in the simplest way"!

Oops, fixing these changes right away!

I think you only need "userPassword" attribute here, no cn needs to be provided?

I think cn is needed, getting this: ldap.UNWILLING_TO_PERFORM: Attribute cn must not be None

rebased onto 96a37764e0267187f1ef10ae1e35b8fa61bc1ef8

4 years ago

setup-ds.pl is deprecated, and may cause this test to break. What does the update do that you require? Consider running your test with --disable-perl on ./configure, and youll see where perl breaks things. If anything we shouldn't use def runUpgrade as a result ....

Should we have a version check in runUpgrade then to check if it's 1.3.x or lower?

@firstyear I think, setup-ds.pl is deprecated but it is still consumed by RHEL 7 and many other OS and these will still be present for a very long time. Rather than using only the latest way, I think ds_older check can be used here but we still need to fix runUpgrade. Tell me a better way of doing this because we cannot ignore older versions. BTW is there anything similar to runUpgrade in lib389 for the the dscreate? because that can be used here with ds_check.
Thanks!

1.4.x, upgrades should be done "inside the server" at startup, else we fail to work with docker. Today, I don't think we have a fully agreed mechanism for this, but there is inplace an internal upgrade system already in ns-slapd so simply restarting is sufficient. But we may need to discuss and clarify this as a team to be completely sure.

As for the cn being needed, yep, ignore my comment then :) I always thought that interface was simpler to use, so may I have forgotten something.

And I think you answered my other questions. Thanks!

setup-ds.pl is deprecated, and may cause this test to break. What does the update do that you require? Consider running your test with --disable-perl on ./configure, and youll see where perl breaks things. If anything we shouldn't use def runUpgrade as a result ....

Should we have a version check in runUpgrade then to check if it's 1.3.x or lower?

Correct a check in the upgrade like if it is 1.3.x then do the things else just pass? Do you think that is fine? But in that case also we need to fix the byte issue inside runUpgrade correct?

@firstyear I think, setup-ds.pl is deprecated but it is still consumed by RHEL 7 and many other OS and these will still be present for a very long time. Rather than using only the latest way, I think ds_older check can be used here but we still need to fix runUpgrade. Tell me a better way of doing this because we cannot ignore older versions. BTW is there anything similar to runUpgrade in lib389 for the the dscreate? because that can be used here with ds_check.
Thanks!

1.4.x, upgrades should be done "inside the server" at startup, else we fail to work with docker. Today, I don't think we have a fully agreed mechanism for this, but there is inplace an internal upgrade system already in ns-slapd so simply restarting is sufficient. But we may need to discuss and clarify this as a team to be completely sure.
As for the cn being needed, yep, ignore my comment then :) I always thought that interface was simpler to use, so may I have forgotten something.
And I think you answered my other questions. Thanks!

Correct a check in the upgrade like if it is 1.3.x then do the things else just pass? Do you think that is fine? But in that case also we need to fix the byte issue inside runUpgrade correct?

Yes I think that would be fine.

rebased onto 570a2d12281884b023b4a43f404fff9d436174ed

4 years ago

@firstyear ds_is_older('1.4.0') is added so that all older version of DS can use the script, not sure in which version exactly it will be removed, as I see 1.4 version have a legacy tool package which can be used as if now, but it will be removed in future. So should we do this in this way or make it work specifically in 1.3.x or lower?

No - you may not call any perl tool from lib389 for 1.4.x. There is a "--disable-perl" flag in the ./configure, and no pl tools will be generated or emited. You must assume all 1.4.x installs run with this setting, and the pl tools as "legacy" are just for installing/removing/admin tools.

So it must only call any pl tools with 1.3.x or lower.

No - you may not call any perl tool from lib389 for 1.4.x. There is a "--disable-perl" flag in the ./configure, and no pl tools will be generated or emited. You must assume all 1.4.x installs run with this setting, and the pl tools as "legacy" are just for installing/removing/admin tools.
So it must only call any pl tools with 1.3.x or lower.

@firstyear I did ds_is_older('1.4.0') that means it cover our requirement correct?

@firstyear for 1.4.x it won't run the script.

Great! that's what I wanted to hear.

@firstyear So everything is good in the patch?

BTW, new findings, I ran the tests on a machine having 1.4 just to be sure, so it didn't run the update script as expected but the test failed. Seems like the internal upgrade system in ns-slapd is not working, I have restarted the server as mentioned by you. Any idea?

@aadhikari The internal upgrade does work, if you are finding something missing, then it means we need to port/add the upgrade requirements to the server core. So you'll need to report what the upgrade is meant to do, and how it's meant to work into an issue so we can implement it in the main server code.

@firstyear Here is the trace log and the whole run of the test, it failed to find AES plugin: No such object

[root@fedora29-aadhikar1 389-ds-base]# py.test-3 -v dirsrvtests/tests/tickets/ticket47462_test.py 
============================================================================ test session starts ============================================================================
platform linux -- Python 3.7.3, pytest-3.6.4, py-1.5.4, pluggy-0.6.0 -- /usr/bin/python3
cachedir: dirsrvtests/.pytest_cache
metadata: {'Python': '3.7.3', 'Platform': 'Linux-4.18.16-300.fc29.x86_64-x86_64-with-fedora-29-Twenty_Nine', 'Packages': {'pytest': '3.6.4', 'py': '1.5.4', 'pluggy': '0.6.0'}, 'Plugins': {'metadata': '1.8.0', 'html': '1.20.0'}}
389-ds-base: 1.4.0.23-1.fc29
nss: 3.44.0-2.fc29
nspr: 4.21.0-1.fc29
openldap: 2.4.46-10.fc29
cyrus-sasl: not installed
FIPS: disabled
rootdir: /root/389-ds-base/dirsrvtests, inifile: pytest.ini
plugins: metadata-1.8.0, html-1.20.0
collected 1 item                                                                                                                                                            

dirsrvtests/tests/tickets/ticket47462_test.py::test_ticket47462 FAILED                                                                                                [100%]

================================================================================= FAILURES ==================================================================================
_____________________________________________________________________________ test_ticket47462 ______________________________________________________________________________

topology_m2 = <lib389.topologies.TopologyMain object at 0x7fec22c603c8>

    def test_ticket47462(topology_m2):
        """
            Test that AES properly replaces DES during an update/restart, and that
            replication also works correctly.
        """

        #
        # First set config as if it's an older version.  Set DES to use
        # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
        # and set a DES password for the replication agreement.
        #
        # Add an extra attribute to the DES plugin args
        #
        plugin_des = Plugin(topology_m2.ms["master1"], DES_PLUGIN)
        plugin_des.set('nsslapd-pluginEnabled', 'on')
        plugin_des.set('nsslapd-pluginarg2', 'description')

        plugin_mmr = Plugin(topology_m2.ms["master1"], MMR_PLUGIN)
        plugin_mmr.remove('nsslapd-plugin-depends-on-named', 'AES')
        #
        # Delete the AES plugin
        #
        topology_m2.ms["master1"].delete_s(AES_PLUGIN)
        # restart the server so we must use DES plugin
        topology_m2.ms["master1"].restart(timeout=10)

        manager = BootstrapReplicationManager(topology_m2.ms["master2"])

        manager.create(properties={
                    'cn': 'replication manager',
                    'userPassword': 'password'
        })

        DN = topology_m2.ms["master2"].replica._get_mt_entry(DEFAULT_SUFFIX)

        topology_m2.ms["master2"].modify_s(DN, [(ldap.MOD_REPLACE,
                                                 'nsDS5ReplicaBindDN', ensure_bytes(defaultProperties[REPLICATION_BIND_DN]))])
        #
        # Create repl agreement from the newly promoted master to master1

        properties = {RA_NAME: 'meTo_{}:{}'.format(topology_m2.ms["master2"].host,
                                                   str(topology_m2.ms["master2"].port)),
                      RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
                      RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
                      RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
                      RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
        topology_m2.ms["master1"].agreement.create(suffix=SUFFIX,
                                                   host=topology_m2.ms["master2"].host,
                                                   port=topology_m2.ms["master2"].port,
                                                   properties=properties)
        #
        # Check replication works with the new DES password
        #
        try:
            topology_m2.ms["master1"].add_s(Entry((USER1_DN,
                                                   {'objectclass': "top person".split(),
                                                    'sn': 'sn',
                                                    'description': 'DES value to convert',
                                                    'cn': 'test_user'})))
            loop = 0
            ent = None
            while loop <= 10:
                try:
                    ent = topology_m2.ms["master2"].getEntry(USER1_DN, ldap.SCOPE_BASE,
                                                             "(objectclass=*)")
                    break
                except ldap.NO_SUCH_OBJECT:
                    time.sleep(1)
                    loop += 1
            if not ent:
                log.fatal('Replication test failed fo user1!')
                assert False
            else:
                log.info('Replication test passed')
        except ldap.LDAPError as e:
            log.fatal('Failed to add test user: ' + e.args[0]['desc'])
            assert False

        #
        # Add a backend (that has no entries)
        #
        try:
            topology_m2.ms["master1"].backend.create("o=empty", {BACKEND_NAME: "empty"})
        except ldap.LDAPError as e:
            log.fatal('Failed to create extra/empty backend: ' + e.args[0]['desc'])
            assert False

        #
        # Run the upgrade...
        #
        topology_m2.ms["master1"].stop()
        topology_m2.ms["master2"].stop()
        #import pdb;pdb.set_trace()
        topology_m2.ms["master1"].upgrade('offline')
        topology_m2.ms["master1"].restart()
        topology_m2.ms["master2"].restart()

        #
        # Check that the restart converted existing DES credentials
        #
        try:
            entry = topology_m2.ms["master1"].search_s('cn=config', ldap.SCOPE_SUBTREE,
                                                       'nsDS5ReplicaCredentials=*')
            if entry:
                val = entry[0].getValue('nsDS5ReplicaCredentials')
                if val.startswith(b'{AES-'):
                    log.info('The DES credentials have been converted to AES')
                else:
                    log.fatal('Failed to convert credentials from DES to AES!')
                    assert False
            else:
                log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
                assert False
        except ldap.LDAPError as e:
            log.fatal('Failed to search for replica credentials: ' +
                      e.args[0]['desc'])
            assert False

        #
        # Check that the AES plugin exists, and has all the attributes listed in
        # DES plugin.  The attributes might not be in the expected order so check
        # all the attributes.
        #
        try:
            entry = topology_m2.ms["master1"].search_s(AES_PLUGIN, ldap.SCOPE_BASE,
>                                                      'objectclass=*')

dirsrvtests/tests/tickets/ticket47462_test.py:162: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = ('cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config', 0, 'objectclass=*'), kwargs = {}

    def inner(*args, **kwargs):
        if name == 'result':
            objtype, data = f(*args, **kwargs)
            # data is either a 2-tuple or a list of 2-tuples
            # print data
            if data:
                if isinstance(data, tuple):
                    return objtype, Entry(data)
                elif isinstance(data, list):
                    # AD sends back these search references
                    # if objtype == ldap.RES_SEARCH_RESULT and \
                    #    isinstance(data[-1],tuple) and \
                    #    not data[-1][0]:
                    #     print "Received search reference: "
                    #     pprint.pprint(data[-1][1])
                    #     data.pop() # remove the last non-entry element

                    return objtype, [Entry(x) for x in data]
                else:
                    raise TypeError("unknown data type %s returned by result" %
                                    type(data))
            else:
                return objtype, data
        elif name.startswith('add'):
            # the first arg is self
            # the second and third arg are the dn and the data to send
            # We need to convert the Entry into the format used by
            # python-ldap
            ent = args[0]
            if isinstance(ent, Entry):
                return f(ent.dn, ent.toTupleList(), *args[2:])
            else:
                return f(*args, **kwargs)
        else:
>           return f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, base = 'cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config', scope = 0, filterstr = 'objectclass=*', attrlist = None
attrsonly = 0

    def search_s(self,base,scope,filterstr=None,attrlist=None,attrsonly=0):
>     return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout=self.timeout)

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:852: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = ('cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config', 0, 'objectclass=*', None, 0, None, ...), kwargs = {'timeout': -1}

    def inner(*args, **kwargs):
        if name == 'result':
            objtype, data = f(*args, **kwargs)
            # data is either a 2-tuple or a list of 2-tuples
            # print data
            if data:
                if isinstance(data, tuple):
                    return objtype, Entry(data)
                elif isinstance(data, list):
                    # AD sends back these search references
                    # if objtype == ldap.RES_SEARCH_RESULT and \
                    #    isinstance(data[-1],tuple) and \
                    #    not data[-1][0]:
                    #     print "Received search reference: "
                    #     pprint.pprint(data[-1][1])
                    #     data.pop() # remove the last non-entry element

                    return objtype, [Entry(x) for x in data]
                else:
                    raise TypeError("unknown data type %s returned by result" %
                                    type(data))
            else:
                return objtype, data
        elif name.startswith('add'):
            # the first arg is self
            # the second and third arg are the dn and the data to send
            # We need to convert the Entry into the format used by
            # python-ldap
            ent = args[0]
            if isinstance(ent, Entry):
                return f(ent.dn, ent.toTupleList(), *args[2:])
            else:
                return f(*args, **kwargs)
        else:
>           return f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, base = 'cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config', scope = 0, filterstr = 'objectclass=*', attrlist = None
attrsonly = 0, serverctrls = None, clientctrls = None, timeout = -1, sizelimit = 0

    def search_ext_s(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
      msgid = self.search_ext(base,scope,filterstr,attrlist,attrsonly,serverctrls,clientctrls,timeout,sizelimit)
>     return self.result(msgid,all=1,timeout=timeout)[1]

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:846: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (3,), kwargs = {'all': 1, 'timeout': -1}

    def inner(*args, **kwargs):
        if name == 'result':
>           objtype, data = f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:135: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, msgid = 3, all = 1, timeout = -1

    def result(self,msgid=ldap.RES_ANY,all=1,timeout=None):
      """
        result([msgid=RES_ANY [,all=1 [,timeout=None]]]) -> (result_type, result_data)

            This method is used to wait for and return the result of an
            operation previously initiated by one of the LDAP asynchronous
            operation routines (e.g. search(), modify(), etc.) They all
            returned an invocation identifier (a message id) upon successful
            initiation of their operation. This id is guaranteed to be
            unique across an LDAP session, and can be used to request the
            result of a specific operation via the msgid parameter of the
            result() method.

            If the result of a specific operation is required, msgid should
            be set to the invocation message id returned when the operation
            was initiated; otherwise RES_ANY should be supplied.

            The all parameter only has meaning for search() responses
            and is used to select whether a single entry of the search
            response should be returned, or to wait for all the results
            of the search before returning.

            A search response is made up of zero or more search entries
            followed by a search result. If all is 0, search entries will
            be returned one at a time as they come in, via separate calls
            to result(). If all is 1, the search response will be returned
            in its entirety, i.e. after all entries and the final search
            result have been received.

            For all set to 0, result tuples
            trickle in (with the same message id), and with the result type
            RES_SEARCH_ENTRY, until the final result which has a result
            type of RES_SEARCH_RESULT and a (usually) empty data field.
            When all is set to 1, only one result is returned, with a
            result type of RES_SEARCH_RESULT, and all the result tuples
            listed in the data field.

            The method returns a tuple of the form (result_type,
            result_data).  The result_type is one of the constants RES_*.

            See search() for a description of the search result's
            result_data, otherwise the result_data is normally meaningless.

            The result() method will block for timeout seconds, or
            indefinitely if timeout is negative.  A timeout of 0 will effect
            a poll. The timeout can be expressed as a floating-point value.
            If timeout is None the default in self.timeout is used.

            If a timeout occurs, a TIMEOUT exception is raised, unless
            polling (timeout = 0), in which case (None, None) is returned.
        """
>     resp_type, resp_data, resp_msgid = self.result2(msgid,all,timeout)

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:738: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (3, 1, -1), kwargs = {}

    def inner(*args, **kwargs):
        if name == 'result':
            objtype, data = f(*args, **kwargs)
            # data is either a 2-tuple or a list of 2-tuples
            # print data
            if data:
                if isinstance(data, tuple):
                    return objtype, Entry(data)
                elif isinstance(data, list):
                    # AD sends back these search references
                    # if objtype == ldap.RES_SEARCH_RESULT and \
                    #    isinstance(data[-1],tuple) and \
                    #    not data[-1][0]:
                    #     print "Received search reference: "
                    #     pprint.pprint(data[-1][1])
                    #     data.pop() # remove the last non-entry element

                    return objtype, [Entry(x) for x in data]
                else:
                    raise TypeError("unknown data type %s returned by result" %
                                    type(data))
            else:
                return objtype, data
        elif name.startswith('add'):
            # the first arg is self
            # the second and third arg are the dn and the data to send
            # We need to convert the Entry into the format used by
            # python-ldap
            ent = args[0]
            if isinstance(ent, Entry):
                return f(ent.dn, ent.toTupleList(), *args[2:])
            else:
                return f(*args, **kwargs)
        else:
>           return f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, msgid = 3, all = 1, timeout = -1

    def result2(self,msgid=ldap.RES_ANY,all=1,timeout=None):
>     resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all,timeout)

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:742: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (3, 1, -1), kwargs = {}

    def inner(*args, **kwargs):
        if name == 'result':
            objtype, data = f(*args, **kwargs)
            # data is either a 2-tuple or a list of 2-tuples
            # print data
            if data:
                if isinstance(data, tuple):
                    return objtype, Entry(data)
                elif isinstance(data, list):
                    # AD sends back these search references
                    # if objtype == ldap.RES_SEARCH_RESULT and \
                    #    isinstance(data[-1],tuple) and \
                    #    not data[-1][0]:
                    #     print "Received search reference: "
                    #     pprint.pprint(data[-1][1])
                    #     data.pop() # remove the last non-entry element

                    return objtype, [Entry(x) for x in data]
                else:
                    raise TypeError("unknown data type %s returned by result" %
                                    type(data))
            else:
                return objtype, data
        elif name.startswith('add'):
            # the first arg is self
            # the second and third arg are the dn and the data to send
            # We need to convert the Entry into the format used by
            # python-ldap
            ent = args[0]
            if isinstance(ent, Entry):
                return f(ent.dn, ent.toTupleList(), *args[2:])
            else:
                return f(*args, **kwargs)
        else:
>           return f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, msgid = 3, all = 1, timeout = -1, resp_ctrl_classes = None

    def result3(self,msgid=ldap.RES_ANY,all=1,timeout=None,resp_ctrl_classes=None):
      resp_type, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4(
        msgid,all,timeout,
        add_ctrls=0,add_intermediates=0,add_extop=0,
>       resp_ctrl_classes=resp_ctrl_classes
      )

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:749: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (3, 1, -1), kwargs = {'add_ctrls': 0, 'add_extop': 0, 'add_intermediates': 0, 'resp_ctrl_classes': None}

    def inner(*args, **kwargs):
        if name == 'result':
            objtype, data = f(*args, **kwargs)
            # data is either a 2-tuple or a list of 2-tuples
            # print data
            if data:
                if isinstance(data, tuple):
                    return objtype, Entry(data)
                elif isinstance(data, list):
                    # AD sends back these search references
                    # if objtype == ldap.RES_SEARCH_RESULT and \
                    #    isinstance(data[-1],tuple) and \
                    #    not data[-1][0]:
                    #     print "Received search reference: "
                    #     pprint.pprint(data[-1][1])
                    #     data.pop() # remove the last non-entry element

                    return objtype, [Entry(x) for x in data]
                else:
                    raise TypeError("unknown data type %s returned by result" %
                                    type(data))
            else:
                return objtype, data
        elif name.startswith('add'):
            # the first arg is self
            # the second and third arg are the dn and the data to send
            # We need to convert the Entry into the format used by
            # python-ldap
            ent = args[0]
            if isinstance(ent, Entry):
                return f(ent.dn, ent.toTupleList(), *args[2:])
            else:
                return f(*args, **kwargs)
        else:
>           return f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, msgid = 3, all = 1, timeout = -1, add_ctrls = 0, add_intermediates = 0, add_extop = 0, resp_ctrl_classes = None

    def result4(self,msgid=ldap.RES_ANY,all=1,timeout=None,add_ctrls=0,add_intermediates=0,add_extop=0,resp_ctrl_classes=None):
      if timeout is None:
        timeout = self.timeout
>     ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop)

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:756: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (<built-in method result4 of LDAP object at 0x7fec22d8cd50>, 3, 1, -1, 0, 0, ...), kwargs = {}

    def inner(*args, **kwargs):
        if name == 'result':
            objtype, data = f(*args, **kwargs)
            # data is either a 2-tuple or a list of 2-tuples
            # print data
            if data:
                if isinstance(data, tuple):
                    return objtype, Entry(data)
                elif isinstance(data, list):
                    # AD sends back these search references
                    # if objtype == ldap.RES_SEARCH_RESULT and \
                    #    isinstance(data[-1],tuple) and \
                    #    not data[-1][0]:
                    #     print "Received search reference: "
                    #     pprint.pprint(data[-1][1])
                    #     data.pop() # remove the last non-entry element

                    return objtype, [Entry(x) for x in data]
                else:
                    raise TypeError("unknown data type %s returned by result" %
                                    type(data))
            else:
                return objtype, data
        elif name.startswith('add'):
            # the first arg is self
            # the second and third arg are the dn and the data to send
            # We need to convert the Entry into the format used by
            # python-ldap
            ent = args[0]
            if isinstance(ent, Entry):
                return f(ent.dn, ent.toTupleList(), *args[2:])
            else:
                return f(*args, **kwargs)
        else:
>           return f(*args, **kwargs)

/usr/lib/python3.7/site-packages/lib389/__init__.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, func = <built-in method result4 of LDAP object at 0x7fec22d8cd50>, args = (3, 1, -1, 0, 0, 0), kwargs = {}
diagnostic_message_success = None, exc_type = None, exc_value = None, exc_traceback = None

    def _ldap_call(self,func,*args,**kwargs):
      """
        Wrapper method mainly for serializing calls into OpenLDAP libs
        and trace logs
        """
      self._ldap_object_lock.acquire()
      if __debug__:
        if self._trace_level>=1:
          self._trace_file.write('*** %s %s - %s\n%s\n' % (
            repr(self),
            self._uri,
            '.'.join((self.__class__.__name__,func.__name__)),
            pprint.pformat((args,kwargs))
          ))
          if self._trace_level>=9:
            traceback.print_stack(limit=self._trace_stack_limit,file=self._trace_file)
      diagnostic_message_success = None
      try:
        try:
          result = func(*args,**kwargs)
          if __debug__ and self._trace_level>=2:
            if func.__name__!="unbind_ext":
              diagnostic_message_success = self._l.get_option(ldap.OPT_DIAGNOSTIC_MESSAGE)
        finally:
          self._ldap_object_lock.release()
      except LDAPError as e:
        exc_type,exc_value,exc_traceback = sys.exc_info()
        try:
          if 'info' not in e.args[0] and 'errno' in e.args[0]:
            e.args[0]['info'] = strerror(e.args[0]['errno'])
        except IndexError:
          pass
        if __debug__ and self._trace_level>=2:
          self._trace_file.write('=> LDAPError - %s: %s\n' % (e.__class__.__name__,str(e)))
        try:
>         reraise(exc_type, exc_value, exc_traceback)

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

exc_type = <class 'ldap.NO_SUCH_OBJECT'>, exc_value = NO_SUCH_OBJECT({'desc': 'No such object'}), exc_traceback = <traceback object at 0x7fec22c0d648>

    def reraise(exc_type, exc_value, exc_traceback):
        """Re-raise an exception given information from sys.exc_info()

            Note that unlike six.reraise, this does not support replacing the
            traceback. All arguments must come from a single sys.exc_info() call.
            """
        # In Python 3, all exception info is contained in one object.
>       raise exc_value

/usr/lib64/python3.7/site-packages/ldap/compat.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <lib389.DirSrv object at 0x7fec22d1a358>, func = <built-in method result4 of LDAP object at 0x7fec22d8cd50>, args = (3, 1, -1, 0, 0, 0), kwargs = {}
diagnostic_message_success = None, exc_type = None, exc_value = None, exc_traceback = None

    def _ldap_call(self,func,*args,**kwargs):
      """
        Wrapper method mainly for serializing calls into OpenLDAP libs
        and trace logs
        """
      self._ldap_object_lock.acquire()
      if __debug__:
        if self._trace_level>=1:
          self._trace_file.write('*** %s %s - %s\n%s\n' % (
            repr(self),
            self._uri,
            '.'.join((self.__class__.__name__,func.__name__)),
            pprint.pformat((args,kwargs))
          ))
          if self._trace_level>=9:
            traceback.print_stack(limit=self._trace_stack_limit,file=self._trace_file)
      diagnostic_message_success = None
      try:
        try:
>         result = func(*args,**kwargs)
E         ldap.NO_SUCH_OBJECT: {'desc': 'No such object'}

/usr/lib64/python3.7/site-packages/ldap/ldapobject.py:313: NO_SUCH_OBJECT

During handling of the above exception, another exception occurred:

topology_m2 = <lib389.topologies.TopologyMain object at 0x7fec22c603c8>

    def test_ticket47462(topology_m2):
        """
            Test that AES properly replaces DES during an update/restart, and that
            replication also works correctly.
        """

        #
        # First set config as if it's an older version.  Set DES to use
        # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
        # and set a DES password for the replication agreement.
        #
        # Add an extra attribute to the DES plugin args
        #
        plugin_des = Plugin(topology_m2.ms["master1"], DES_PLUGIN)
        plugin_des.set('nsslapd-pluginEnabled', 'on')
        plugin_des.set('nsslapd-pluginarg2', 'description')

        plugin_mmr = Plugin(topology_m2.ms["master1"], MMR_PLUGIN)
        plugin_mmr.remove('nsslapd-plugin-depends-on-named', 'AES')
        #
        # Delete the AES plugin
        #
        topology_m2.ms["master1"].delete_s(AES_PLUGIN)
        # restart the server so we must use DES plugin
        topology_m2.ms["master1"].restart(timeout=10)

        manager = BootstrapReplicationManager(topology_m2.ms["master2"])

        manager.create(properties={
                    'cn': 'replication manager',
                    'userPassword': 'password'
        })

        DN = topology_m2.ms["master2"].replica._get_mt_entry(DEFAULT_SUFFIX)

        topology_m2.ms["master2"].modify_s(DN, [(ldap.MOD_REPLACE,
                                                 'nsDS5ReplicaBindDN', ensure_bytes(defaultProperties[REPLICATION_BIND_DN]))])
        #
        # Create repl agreement from the newly promoted master to master1

        properties = {RA_NAME: 'meTo_{}:{}'.format(topology_m2.ms["master2"].host,
                                                   str(topology_m2.ms["master2"].port)),
                      RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
                      RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
                      RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
                      RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
        topology_m2.ms["master1"].agreement.create(suffix=SUFFIX,
                                                   host=topology_m2.ms["master2"].host,
                                                   port=topology_m2.ms["master2"].port,
                                                   properties=properties)
        #
        # Check replication works with the new DES password
        #
        try:
            topology_m2.ms["master1"].add_s(Entry((USER1_DN,
                                                   {'objectclass': "top person".split(),
                                                    'sn': 'sn',
                                                    'description': 'DES value to convert',
                                                    'cn': 'test_user'})))
            loop = 0
            ent = None
            while loop <= 10:
                try:
                    ent = topology_m2.ms["master2"].getEntry(USER1_DN, ldap.SCOPE_BASE,
                                                             "(objectclass=*)")
                    break
                except ldap.NO_SUCH_OBJECT:
                    time.sleep(1)
                    loop += 1
            if not ent:
                log.fatal('Replication test failed fo user1!')
                assert False
            else:
                log.info('Replication test passed')
        except ldap.LDAPError as e:
            log.fatal('Failed to add test user: ' + e.args[0]['desc'])
            assert False

        #
        # Add a backend (that has no entries)
        #
        try:
            topology_m2.ms["master1"].backend.create("o=empty", {BACKEND_NAME: "empty"})
        except ldap.LDAPError as e:
            log.fatal('Failed to create extra/empty backend: ' + e.args[0]['desc'])
            assert False

        #
        # Run the upgrade...
        #
        topology_m2.ms["master1"].stop()
        topology_m2.ms["master2"].stop()
        #import pdb;pdb.set_trace()
        topology_m2.ms["master1"].upgrade('offline')
        topology_m2.ms["master1"].restart()
        topology_m2.ms["master2"].restart()

        #
        # Check that the restart converted existing DES credentials
        #
        try:
            entry = topology_m2.ms["master1"].search_s('cn=config', ldap.SCOPE_SUBTREE,
                                                       'nsDS5ReplicaCredentials=*')
            if entry:
                val = entry[0].getValue('nsDS5ReplicaCredentials')
                if val.startswith(b'{AES-'):
                    log.info('The DES credentials have been converted to AES')
                else:
                    log.fatal('Failed to convert credentials from DES to AES!')
                    assert False
            else:
                log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
                assert False
        except ldap.LDAPError as e:
            log.fatal('Failed to search for replica credentials: ' +
                      e.args[0]['desc'])
            assert False

        #
        # Check that the AES plugin exists, and has all the attributes listed in
        # DES plugin.  The attributes might not be in the expected order so check
        # all the attributes.
        #
        try:
            entry = topology_m2.ms["master1"].search_s(AES_PLUGIN, ldap.SCOPE_BASE,
                                                       'objectclass=*')
            if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
                    not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
                    not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
                log.fatal('The AES plugin did not have the DES attribute copied ' +
                          'over correctly')
                assert False
            else:
                log.info('The AES plugin was correctly setup')
        except ldap.LDAPError as e:
            log.fatal('Failed to find AES plugin: ' + e.args[0]['desc'])
>           assert False
E           assert False

dirsrvtests/tests/tickets/ticket47462_test.py:173: AssertionError
--------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------
Instance slapd-master1 removed.
Instance slapd-master2 removed.
--------------------------------------------------------------------------- Captured stderr setup ---------------------------------------------------------------------------
INFO:lib389.topologies:Instance with parameters {'ldap-port': 39001, 'ldap-secureport': 63701, 'server-id': 'master1', 'suffix': 'dc=example,dc=com'} was created.
INFO:lib389.topologies:Instance with parameters {'ldap-port': 39002, 'ldap-secureport': 63702, 'server-id': 'master2', 'suffix': 'dc=example,dc=com'} was created.
INFO:lib389.topologies:Creating replication topology.
INFO:lib389.topologies:Joining master master2 to master1 ...
INFO:lib389.replica:SUCCESS: bootstrap to ldap://fedora29-aadhikar1:39002 completed
INFO:lib389.replica:SUCCESS: Agreement from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002 is was created
INFO:lib389.replica:SUCCESS: Agreement from ldap://fedora29-aadhikar1:39002 to ldap://fedora29-aadhikar1:39001 is was created
INFO:lib389.replica:SUCCESS: Replication from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002 is working
INFO:lib389.replica:SUCCESS: Replication from ldap://fedora29-aadhikar1:39002 to ldap://fedora29-aadhikar1:39001 is working
INFO:lib389.replica:SUCCESS: joined master from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002
INFO:lib389.topologies:Ensuring master master1 to master2 ...
INFO:lib389.replica:SUCCESS: Agreement from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002 already exists
INFO:lib389.topologies:Ensuring master master2 to master1 ...
INFO:lib389.replica:SUCCESS: Agreement from ldap://fedora29-aadhikar1:39002 to ldap://fedora29-aadhikar1:39001 already exists
---------------------------------------------------------------------------- Captured log setup -----------------------------------------------------------------------------
topologies.py              106 INFO     Instance with parameters {'ldap-port': 39001, 'ldap-secureport': 63701, 'server-id': 'master1', 'suffix': 'dc=example,dc=com'} was created.
topologies.py              106 INFO     Instance with parameters {'ldap-port': 39002, 'ldap-secureport': 63702, 'server-id': 'master2', 'suffix': 'dc=example,dc=com'} was created.
topologies.py              139 INFO     Creating replication topology.
topologies.py              153 INFO     Joining master master2 to master1 ...
replica.py                1533 INFO     SUCCESS: bootstrap to ldap://fedora29-aadhikar1:39002 completed
replica.py                1814 INFO     SUCCESS: Agreement from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002 is was created
replica.py                1814 INFO     SUCCESS: Agreement from ldap://fedora29-aadhikar1:39002 to ldap://fedora29-aadhikar1:39001 is was created
replica.py                1945 INFO     SUCCESS: Replication from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002 is working
replica.py                1945 INFO     SUCCESS: Replication from ldap://fedora29-aadhikar1:39002 to ldap://fedora29-aadhikar1:39001 is working
replica.py                1602 INFO     SUCCESS: joined master from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002
topologies.py              161 INFO     Ensuring master master1 to master2 ...
replica.py                1787 INFO     SUCCESS: Agreement from ldap://fedora29-aadhikar1:39001 to ldap://fedora29-aadhikar1:39002 already exists
topologies.py              161 INFO     Ensuring master master2 to master1 ...
replica.py                1787 INFO     SUCCESS: Agreement from ldap://fedora29-aadhikar1:39002 to ldap://fedora29-aadhikar1:39001 already exists
--------------------------------------------------------------------------- Captured stderr call ----------------------------------------------------------------------------
INFO:dirsrvtests.tests.tickets.ticket47462_test:Replication test passed
INFO:lib389:List backend with suffix=o=empty
INFO:lib389:Creating a local backend
INFO:lib389:List backend cn=empty,cn=ldbm database,cn=plugins,cn=config
INFO:lib389:Found entry dn: cn=empty,cn=ldbm database,cn=plugins,cn=config
cn: empty
nsslapd-cachememsize: 512000
nsslapd-cachesize: -1
nsslapd-directory: /var/lib/dirsrv/slapd-master1/db/empty
nsslapd-dncachememsize: 16777216
nsslapd-readonly: off
nsslapd-require-index: off
nsslapd-suffix: o=empty
objectClass: top
objectClass: extensibleObject
objectClass: nsBackendInstance


INFO:dirsrvtests.tests.tickets.ticket47462_test:The DES credentials have been converted to AES
CRITICAL:dirsrvtests.tests.tickets.ticket47462_test:Failed to find AES plugin: No such object
----------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
ticket47462_test.py        110 INFO     Replication test passed
backend.py                  76 INFO     List backend with suffix=o=empty
backend.py                 286 INFO     Creating a local backend
backend.py                  72 INFO     List backend cn=empty,cn=ldbm database,cn=plugins,cn=config
__init__.py               1833 INFO     Found entry dn: cn=empty,cn=ldbm database,cn=plugins,cn=config
cn: empty
nsslapd-cachememsize: 512000
nsslapd-cachesize: -1
nsslapd-directory: /var/lib/dirsrv/slapd-master1/db/empty
nsslapd-dncachememsize: 16777216
nsslapd-readonly: off
nsslapd-require-index: off
nsslapd-suffix: o=empty
objectClass: top
objectClass: extensibleObject
objectClass: nsBackendInstance


ticket47462_test.py        143 INFO     The DES credentials have been converted to AES
ticket47462_test.py        172 CRITICAL Failed to find AES plugin: No such object
------------------------------------------------------------------------- Captured stdout teardown --------------------------------------------------------------------------
Instance slapd-master1 removed.
Instance slapd-master2 removed.
========================================================================= 1 failed in 38.89 seconds =========================================================================

rebased onto 1afde987e7aa931140f47ceb7e8c4e063d70299e

4 years ago

rebased onto 4dbdbfac1ddb5e2db96d0b7024e2190dd41ef4e2

4 years ago

A better reason message can be suggested.

2 new commits added

  • Description: Explicitly changed strings to bytes in upgrade-script(tools.py)
  • Description: Fixed the failure by adding server version check,
4 years ago

rebased onto 55112be2da7eda254f080d5657d440c87f22f573

4 years ago

rebased onto ca5f54bf78bd6c5c89e73f44ece460d50835042c

4 years ago

rebased onto 7a24286

4 years ago

Pull-Request has been merged by vashirov

4 years ago

389-ds-base is moving from Pagure to Github. This means that new issues and pull requests
will be accepted only in 389-ds-base's github repository.

This pull request has been cloned to Github as issue and is available here:
- https://github.com/389ds/389-ds-base/issues/3346

If you want to continue to work on the PR, please navigate to the github issue,
download the patch from the attachments and file a new pull request.

Thank you for understanding. We apologize for all inconvenience.

Pull-Request has been closed by spichugi

3 years ago