This is an automated email from the git hooks/post-receive script.
spichugi pushed a commit to branch master
in repository lib389.
commit 3342c118fb034f7262f10470e055911ea3848b1c
Author: Simon Pichugin <spichugi(a)redhat.com>
Date: Fri Jul 21 17:32:46 2017 +0200
Issue 79 - Fix replica.py and add tests
Description: Move tests for ReplicaLegacy to replicaLegacy_test.py
module. Add a new test suite for Replicas(DSLdapObject).
Fix or change:
- Add enum34 to pip and python-enum34 to specfile for RHEL7/Fedora
- _constants.py:
- replace REPLICAROLE_* with ReplicaRole(Enum) object
- change the type of REPLICA_FLAGS_* from str to int
- remove not used constants REPLICA_TYPE_* and REPLICA_FLAGS_CON
- Agreement.create() - if property is None, define the dict object;
- Changelog.list() - make it consistent and return empry list
if nothing is found;
- Replica:
- Replace hard coded values with variables if possible;
- Fix get_role() functions so it will return right result;
- Fix deleteAgreements. It now doesn't have suffix param;
- Fix typos on variables;
- Make promote() and demote() methods more explicit;
- Rename Replicas.delete() to Replicas.disable because
it coveres changelog and agreements deletion too;
- Make docstring format consistent;
https://pagure.io/lib389/issue/79
Reviewed by: wibrown (Thanks!)
---
lib389/__init__.py | 6 +-
lib389/_constants.py | 16 +-
lib389/changelog.py | 12 +-
lib389/replica.py | 511 +++++++--------
lib389/repltools.py | 6 +-
lib389/tests/agreement_test.py | 4 +-
.../{replica_test.py => replicaLegacy_test.py} | 16 +-
lib389/tests/replica_test.py | 702 +++++++++------------
lib389/topologies.py | 53 +-
lib389/utils.py | 25 +-
python-lib389.spec | 1 +
requirements.txt | 3 +-
12 files changed, 626 insertions(+), 729 deletions(-)
diff --git a/lib389/__init__.py b/lib389/__init__.py
index 43ead6b..4f6dafb 100644
--- a/lib389/__init__.py
+++ b/lib389/__init__.py
@@ -2368,11 +2368,11 @@ class DirSrv(SimpleLDAPObject, object):
# setup replica
# map old style args to new style replica args
if repArgs['type'] == MASTER_TYPE:
- repArgs['role'] = REPLICAROLE_MASTER
+ repArgs['role'] = ReplicaRole.MASTER
elif repArgs['type'] == LEAF_TYPE:
- repArgs['role'] = REPLICAROLE_CONSUMER
+ repArgs['role'] = ReplicaRole.CONSUMER
else:
- repArgs['role'] = REPLICAROLE_HUB
+ repArgs['role'] = ReplicaRole.HUB
repArgs['rid'] = repArgs['id']
# remove invalid arguments from replica.add
diff --git a/lib389/_constants.py b/lib389/_constants.py
index 7e18553..4ca9d0c 100644
--- a/lib389/_constants.py
+++ b/lib389/_constants.py
@@ -7,6 +7,7 @@
# --- END COPYRIGHT BLOCK ---
import os
+from enum import Enum
from lib389.properties import *
(
@@ -17,24 +18,19 @@ from lib389.properties import *
INSTALL_LATEST_CONFIG = '999999999'
-ROLE_STANDALONE = "standalone"
-REPLICAROLE_MASTER = "master"
-REPLICAROLE_HUB = "hub"
-REPLICAROLE_CONSUMER = "consumer"
-
REPLICA_FLAGS_CON = 0
+# The structure is convenient for replica promote/demote methods
+ReplicaRole = Enum("Replica role", "CONSUMER HUB MASTER STANDALONE")
+
CONSUMER_REPLICAID = 65535
REPLICA_RDONLY_TYPE = 2 # CONSUMER and HUB
REPLICA_WRONLY_TYPE = 1 # SINGLE and MULTI MASTER
REPLICA_RDWR_TYPE = REPLICA_RDONLY_TYPE | REPLICA_WRONLY_TYPE
-REPLICA_FLAGS_RDONLY = '0'
-REPLICA_FLAGS_WRITE = '1'
-
-REPLICA_TYPE_MASTER = '3'
-REPLICA_TYPE_HUBCON = '2'
+REPLICA_FLAGS_RDONLY = 0
+REPLICA_FLAGS_WRITE = 1
REPLICA_RUV_UUID = "ffffffff-ffffffff-ffffffff-ffffffff"
REPLICA_RUV_FILTER =
('(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))')
diff --git a/lib389/changelog.py b/lib389/changelog.py
index 66ced8c..5affcee 100644
--- a/lib389/changelog.py
+++ b/lib389/changelog.py
@@ -26,15 +26,17 @@ class Changelog(object):
if name in Changelog.proxied_methods:
return DirSrv.__getattr__(self.conn, name)
- def list(self, suffix=None, changelogdn=None):
- if not changelogdn:
- raise InvalidArgumentError("changelog DN is missing")
-
+ def list(self, suffix=None, changelogdn=DN_CHANGELOG):
base = changelogdn
filtr = "(objectclass=extensibleobject)"
# now do the effective search
- ents = self.conn.search_s(base, ldap.SCOPE_BASE, filtr)
+ try:
+ ents = self.conn.search_s(base, ldap.SCOPE_BASE, filtr)
+ except ldap.NO_SUCH_OBJECT:
+ # There are no objects to select from, se we return an empty array
+ # as we do in DSLdapObjects
+ ents = []
return ents
def create(self, dbname=DEFAULT_CHANGELOG_DB):
diff --git a/lib389/replica.py b/lib389/replica.py
index 4de88be..9251ecd 100644
--- a/lib389/replica.py
+++ b/lib389/replica.py
@@ -18,9 +18,6 @@ from lib389.repltools import ReplTools
from lib389 import DirSrv, Entry, NoSuchEntryError, InvalidArgumentError
from lib389._mapped_object import DSLdapObjects, DSLdapObject
-ROLE_ORDER = {'master': 3, 'hub': 2, 'consumer': 1}
-ROLE_TO_NAME = {3: 'master', 2: 'hub', 1: 'consumer'}
-
class ReplicaLegacy(object):
proxied_methods = 'search_s getEntry'.split()
@@ -41,16 +38,16 @@ class ReplicaLegacy(object):
@staticmethod
def _valid_role(role):
- if role != REPLICAROLE_MASTER and \
- role != REPLICAROLE_HUB and \
- role != REPLICAROLE_CONSUMER:
+ if role != ReplicaRole.MASTER and \
+ role != ReplicaRole.HUB and \
+ role != ReplicaRole.CONSUMER:
return False
else:
return True
@staticmethod
def _valid_rid(role, rid=None):
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
if not decimal.Decimal(rid) or \
(rid <= 0) or \
(rid >= CONSUMER_REPLICAID):
@@ -257,17 +254,45 @@ class ReplicaLegacy(object):
properties=None):
raise NotImplementedError
+ def get_role(self, suffix):
+ """Return the replica role
+
+ @return: ReplicaRole.MASTER, ReplicaRole.HUB, ReplicaRole.CONSUMER
+ """
+
+ filter_str =
('(&(objectclass=nsDS5Replica)(nsDS5ReplicaRoot=%s))'.format(suffix))
+
+ try:
+ replica_entry = self.conn.search_s(DN_CONFIG, ldap.SCOPE_SUBTREE,
+ filter_str)
+ if replica_entry:
+ repltype = replica_entry[0].getValue(REPL_TYPE)
+ replflags = replica_entry[0].getValue(REPL_FLAGS)
+
+ if repltype == REPLICA_RDWR_TYPE and replflags == REPLICA_FLAGS_WRITE:
+ replicarole = ReplicaRole.MASTER
+ elif repltype == REPLICA_RDONLY_TYPE and replflags ==
REPLICA_FLAGS_WRITE:
+ replicarole = ReplicaRole.HUB
+ elif repltype == REPLICA_RDONLY_TYPE and replflags ==
REPLICA_FLAGS_RDONLY:
+ replicarole = ReplicaRole.CONSUMER
+ else:
+ raise ValueError("Failed to determine a replica role")
+
+ return replicarole
+ except ldap.LDAPError as e:
+ raise ValueError('Failed to get replica entry: %s' % str(e))
+
def create(self, suffix=None, role=None, rid=None, args=None):
"""
Create a replica entry on an existing suffix.
@param suffix - dn of suffix
- @param role - REPLICAROLE_MASTER, REPLICAROLE_HUB or
- REPLICAROLE_CONSUMER
+ @param role - ReplicaRole.MASTER, ReplicaRole.HUB or
+ ReplicaRole.CONSUMER
@param rid - number that identify the supplier replica
- (role=REPLICAROLE_MASTER) in the topology. For
- hub/consumer (role=REPLICAROLE_HUB or
- REPLICAROLE_CONSUMER), rid value is not used.
+ (role=ReplicaRole.MASTER) in the topology. For
+ hub/consumer (role=ReplicaRole.HUB or
+ ReplicaRole.CONSUMER), rid value is not used.
This parameter is mandatory for supplier.
@param args - dictionary of initial replica's properties
@@ -291,13 +316,11 @@ class ReplicaLegacy(object):
"""
# Check validity of role
if not role:
- self.log.fatal("Replica.create: replica role not specify" +
- " (REPLICAROLE_*)")
+ self.log.fatal("Replica.create: replica role is not specified
(ReplicaRole.*)")
raise InvalidArgumentError("role missing")
if not ReplicaLegacy._valid_role(role):
- self.log.fatal("enableReplication: replica role invalid (%s) " %
- role)
+ self.log.fatal("enableReplication: replica role invalid (%s) " %
role)
raise ValueError("invalid role: %s" % role)
# check the validity of 'rid'
@@ -314,7 +337,7 @@ class ReplicaLegacy(object):
nsuffix = normalizeDN(suffix)
# role is fine, set the replica type
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
rtype = REPLICA_RDWR_TYPE
else:
rtype = REPLICA_RDONLY_TYPE
@@ -340,8 +363,11 @@ class ReplicaLegacy(object):
ReplicaLegacy._set_or_default(REPLICA_BINDDN, properties,
[defaultProperties[REPLICATION_BIND_DN]])
- if role != REPLICAROLE_CONSUMER:
- properties[REPLICA_FLAGS] = "1"
+ # Set flags explicitly, so it will be more readable
+ if role == ReplicaRole.CONSUMER:
+ properties[REPLICA_FLAGS] = str(REPLICA_FLAGS_RDONLY)
+ else:
+ properties[REPLICA_FLAGS] = str(REPLICA_FLAGS_WRITE)
#
# Check if replica entry is already in the mapping-tree
@@ -397,7 +423,7 @@ class ReplicaLegacy(object):
except ldap.LDAPError as e:
self.log.fatal('Failed to delete replica agreement (%s),' +
' error: %s' %
- (admt.dn, str(e)))
+ (agmt.dn, str(e)))
raise
except ldap.LDAPError as e:
self.log.fatal('Failed to search for replication agreements ' +
@@ -407,7 +433,7 @@ class ReplicaLegacy(object):
def disableReplication(self, suffix=None):
'''
Delete a replica related to the provided suffix.
- If this replica role was REPLICAROLE_HUB or REPLICAROLE_MASTER, it
+ If this replica role was ReplicaRole.HUB or ReplicaRole.MASTER, it
also deletes the changelog associated to that replica. If it
exists some replication agreement below that replica, they are
deleted.
@@ -455,7 +481,7 @@ class ReplicaLegacy(object):
if not role:
self.log.fatal("enableReplication: replica role not specify " +
- "(REPLICAROLE_*)")
+ "(ReplicaRole.*)")
raise ValueError("role missing")
#
@@ -463,14 +489,14 @@ class ReplicaLegacy(object):
#
# First role and replicaID
- if role != REPLICAROLE_MASTER and \
- role != REPLICAROLE_HUB and \
- role != REPLICAROLE_CONSUMER:
+ if role != ReplicaRole.MASTER and \
+ role != ReplicaRole.HUB and \
+ role != ReplicaRole.CONSUMER:
self.log.fatal("enableReplication: replica role invalid (%s) " %
role)
raise ValueError("invalid role: %s" % role)
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
# check the replicaId [1..CONSUMER_REPLICAID[
if not decimal.Decimal(replicaId) or \
(replicaId <= 0) or \
@@ -517,7 +543,7 @@ class ReplicaLegacy(object):
pass
# First add the changelog if master/hub
- if (role == REPLICAROLE_MASTER) or (role == REPLICAROLE_HUB):
+ if (role == ReplicaRole.MASTER) or (role == ReplicaRole.HUB):
self.conn.changelog.create()
# Second create the default replica manager entry if it does not exist
@@ -648,50 +674,34 @@ class ReplicaLegacy(object):
@raise ValueError
"""
- if newrole != REPLICAROLE_MASTER and newrole != REPLICAROLE_HUB:
+ if newrole != ReplicaRole.MASTER and newrole != ReplicaRole.HUB:
raise ValueError('Can only prompt replica to "master" or
"hub"')
if not binddn:
raise ValueError('"binddn" required for promotion')
- if newrole == REPLICAROLE_MASTER:
+ if newrole == ReplicaRole.MASTER:
if not rid:
raise ValueError('"rid" required for promotion')
else:
# Must be a hub - set the rid
rid = CONSUMER_REPLICAID
- #
- # Get the replica entry
- #
+ # Get replica entry
filter_str = ('(&(objectclass=nsDS5Replica)(nsDS5ReplicaRoot=%s))' %
suffix)
try:
- replica_entry = self.conn.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ replica_entry = self.conn.search_s(DN_CONFIG, ldap.SCOPE_SUBTREE,
filter_str)
- if replica_entry:
- repltype = replica_entry[0].getValue(REPL_TYPE)
- replflags = replica_entry[0].getValue(REPL_FLAGS)
-
- if repltype == REPLICA_TYPE_MASTER and \
- replflags == REPLICA_FLAGS_WRITE:
- replicarole = 3
- elif (repltype == REPLICA_TYPE_HUBCON and
- replflags == REPLICA_TYPE_MASTER):
- replicarole = 2
- else:
- replicarole = 1
-
- if ROLE_ORDER[newrole] < replicarole:
- raise ValueError('Can not promote replica to lower role:' +
- ' %s -> %s' %
(ROLE_TO_NAME[replicarole],
- newrole))
- else:
- raise ValueError('Failed to find replica')
-
except ldap.LDAPError as e:
raise ValueError('Failed to get replica entry: %s' % str(e))
+ # Check the role type
+ replicarole = self.get_role(suffix)
+
+ if newrole.value < replicarole.value:
+ raise ValueError('Can not promote replica to lower role: {} ->
{}'.format(replicarole.name, newrole.name))
+
#
# Create the changelog
#
@@ -703,7 +713,7 @@ class ReplicaLegacy(object):
#
# Check that a RID was provided, and its a valid number
#
- if newrole == REPLICAROLE_MASTER:
+ if newrole == ReplicaRole.MASTER:
try:
rid = int(rid)
except:
@@ -726,7 +736,7 @@ class ReplicaLegacy(object):
#
# Set the replica type and flags
#
- if newrole == REPLICAROLE_HUB:
+ if newrole == ReplicaRole.HUB:
try:
self.conn.modify_s(replica_entry[0].dn,
[(ldap.MOD_REPLACE, REPL_TYPE, '2'),
@@ -748,44 +758,28 @@ class ReplicaLegacy(object):
@raise ValueError
"""
- if newrole != REPLICAROLE_CONSUMER and newrole != REPLICAROLE_HUB:
+ if newrole != ReplicaRole.CONSUMER and newrole != ReplicaRole.HUB:
raise ValueError('Can only demote replica to "hub" or
"consumer"')
- #
- # Get the replica entry, and check the role type
- #
+ # Get replica entry
filter_str = ('(&(objectclass=nsDS5Replica)(nsDS5ReplicaRoot=%s))' %
suffix)
try:
- replica_entry = self.conn.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ replica_entry = self.conn.search_s(DN_CONFIG, ldap.SCOPE_SUBTREE,
filter_str)
- if replica_entry:
- repltype = replica_entry[0].getValue(REPL_TYPE)
- replflags = replica_entry[0].getValue(REPL_FLAGS)
-
- if repltype == REPLICA_TYPE_MASTER and \
- replflags == REPLICA_FLAGS_WRITE:
- replicarole = 3
- elif (repltype == REPLICA_TYPE_HUBCON and
- replflags == REPLICA_FLAGS_WRITE):
- replicarole = 2
- else:
- replicarole = 1
-
- if ROLE_ORDER[newrole] > replicarole:
- raise ValueError('Can not demote replica to lower role:' +
- ' %s -> %s' %
(ROLE_TO_NAME[replicarole],
- newrole))
- else:
- raise ValueError('Failed to find replica entry')
-
except ldap.LDAPError as e:
raise ValueError('Failed to get replica entry: %s' % str(e))
+ # Check the role type
+ replicarole = self.get_role(suffix)
+
+ if newrole.value > replicarole.value:
+ raise ValueError('Can not demote replica to higher role: {} ->
{}'.format(replicarole.name, newrole.name))
+
#
# Demote it - set the replica type and flags
#
- if newrole == 'hub':
+ if newrole == ReplicaRole.HUB:
flag = '1'
else:
flag = '0'
@@ -800,14 +794,16 @@ class ReplicaLegacy(object):
class Replica(DSLdapObject):
- """Replica object. There is one "replica" per backend
- """
+ """Replica object. There is one "replica" per
backend"""
+
def __init__(self, instance, dn=None, batch=False):
"""Init the Replica object
+
@param instance - a DirSrv object
@param dn - A DN of the replica entry
@param batch - NOT IMPLELMENTED
"""
+
super(Replica, self).__init__(instance, dn, batch)
self._rdn_attribute = 'cn'
self._must_attributes = ['cn', REPL_LEGACY_CONS, REPL_TYPE,
@@ -825,21 +821,26 @@ class Replica(DSLdapObject):
@param role - A string containing the "role" name
@return - True if the role is a valid role name, otherwise return False
"""
- if role != REPLICAROLE_MASTER and \
- role != REPLICAROLE_HUB and \
- role != REPLICAROLE_CONSUMER:
+
+ if role != ReplicaRole.MASTER and \
+ role != ReplicaRole.HUB and \
+ role != ReplicaRole.CONSUMER:
return False
else:
return True
@staticmethod
def _valid_rid(role, rid=None):
- """ Return True if rid is valid for the replica role
+ """Return True if rid is valid for the replica role
+
@param role - A string containing the role name
@param rid - Only needed if the role is a "master"
@return - True is rid is valid, otherwise return False
"""
- if role == REPLICAROLE_MASTER:
+
+ if rid is None:
+ return False
+ if role == ReplicaRole.MASTER:
if not decimal.Decimal(rid) or \
(rid <= 0) or \
(rid >= CONSUMER_REPLICAID):
@@ -850,18 +851,17 @@ class Replica(DSLdapObject):
return True
def delete(self):
- '''
- Delete a replica related to the provided suffix.
- If this replica role was REPLICAROLE_HUB or REPLICAROLE_MASTER, it
- also deletes the changelog associated to that replica. If it
- exists some replication agreement below that replica, they are
- deleted.
+ """Delete a replica related to the provided suffix.
- @return None
- @raise InvalidArgumentError - if suffix is missing
- ldap.LDAPError - for all other update failures
+ If this replica role was ReplicaRole.HUB or ReplicaRole.MASTER, it
+ also deletes the changelog associated to that replica. If it
+ exists some replication agreement below that replica, they are
+ deleted.
- '''
+ @return None
+ @raise InvalidArgumentError - if suffix is missing
+ ldap.LDAPError - for all other update failures
+ """
# Get the suffix
suffix = self.get_attr_val(REPL_ROOT)
@@ -871,7 +871,7 @@ class Replica(DSLdapObject):
# Delete the agreements
try:
- self.deleteAgreements(suffix)
+ self.deleteAgreements()
except ldap.LDAPError as e:
self.log.fatal('Failed to delete replica agreements!')
raise e
@@ -881,14 +881,14 @@ class Replica(DSLdapObject):
super(Replica, self).delete()
except ldap.LDAPError as e:
self.log.fatal('Failed to delete replica configuration ' +
- '(%s), error: %s' % (dn_replica, str(e)))
+ '(%s), error: %s' % (self._dn, str(e)))
raise e
def deleteAgreements(self):
- '''
- Delete all the agreements for the suffix
+ """Delete all the agreements for the suffix
+
@raise LDAPError - If failing to delete or search for agreements
- '''
+ """
# Delete the agreements
try:
@@ -899,8 +899,7 @@ class Replica(DSLdapObject):
self._instance.delete_s(agmt.dn)
except ldap.LDAPError as e:
self.log.fatal('Failed to delete replica agreement (%s),' +
- ' error: %s' %
- (admt.dn, str(e)))
+ ' error: %s' % (agmt.dn, str(e)))
raise e
except ldap.LDAPError as e:
self.log.fatal('Failed to search for replication agreements ' +
@@ -908,13 +907,11 @@ class Replica(DSLdapObject):
raise e
def promote(self, newrole, binddn=None, rid=None):
- """
- Promote the replica
+ """Promote the replica to hub or master
@param newrole - The new replication role for the replica:
- REPLICAROLE_MASTER
- REPLICAROLE_HUB
- REPLICAROLE_CONSUMER
+ ReplicaRole.MASTER
+ ReplicaRole.HUB
@param binddn - The replication bind dn - only applied to master
@param rid - The replication ID, applies only to promotions to
"master"
@@ -923,51 +920,29 @@ class Replica(DSLdapObject):
@raise ValueError
"""
- if newrole != REPLICAROLE_MASTER and newrole != REPLICAROLE_HUB:
- raise ValueError('Can only prompt replica to "master" or
"hub"')
-
if not binddn:
- raise ValueError('"binddn" required for promotion')
+ binddn = defaultProperties[REPLICATION_BIND_DN]
- if newrole == REPLICAROLE_MASTER:
+ # Check the role type
+ replicarole = self.get_role()
+ if newrole.value <= replicarole.value:
+ raise ValueError('Can not promote replica to lower or the same role: {}
-> {}'.format(replicarole.name, newrole.name))
+
+ if newrole == ReplicaRole.MASTER:
if not rid:
raise ValueError('"rid" required for promotion')
else:
# Must be a hub - set the rid
rid = CONSUMER_REPLICAID
- #
- # Check the replica role and flags
- #
- repltype = self.get_attr_val(REPL_TYPE)
- replflags = self.get_attr_val(REPL_FLAGS)
-
- if repltype == REPLICA_TYPE_MASTER and \
- replflags == REPLICA_FLAGS_WRITE:
- replicarole = 3
- elif (repltype == REPLICA_TYPE_HUBCON and
- replflags == REPLICA_TYPE_MASTER):
- replicarole = 2
- else:
- replicarole = 1
-
- if ROLE_ORDER[newrole] < replicarole:
- raise ValueError('Can not promote replica to lower role:' +
- ' %s -> %s' % (ROLE_TO_NAME[replicarole],
- newrole))
-
- #
# Create the changelog
- #
try:
self._instance.changelog.create()
except ldap.LDAPError as e:
raise ValueError('Failed to create changelog: %s' % str(e))
- #
# Check that a RID was provided, and its a valid number
- #
- if newrole == REPLICAROLE_MASTER:
+ if newrole == ReplicaRole.MASTER:
try:
rid = int(rid)
except:
@@ -978,101 +953,97 @@ class Replica(DSLdapObject):
raise ValueError('"rid" value (%d) is not in range ' +
' 1 - 65534' % rid)
- #
# Set bind dn
- #
try:
self.set(REPL_BINDDN, binddn)
except ldap.LDAPError as e:
raise ValueError('Failed to update replica: ' + str(e))
- #
- # Set the replica type and flags
- #
- if newrole == REPLICAROLE_HUB:
+ # Promote it - set the replica type, flags and rid
+ if replicarole == ReplicaRole.CONSUMER and newrole == ReplicaRole.HUB:
try:
- self.apply_mods([(REPL_TYPE, '2'), (REPL_FLAGS, '1')])
+ self.set(REPL_FLAGS, str(REPLICA_FLAGS_WRITE))
except ldap.LDAPError as e:
raise ValueError('Failed to update replica: ' + str(e))
- else: # master
+ elif replicarole == ReplicaRole.CONSUMER and newrole == ReplicaRole.MASTER:
try:
- self.apply_mods([(REPL_TYPE, '3'), (REPL_FLAGS, '1'),
- (REPL_ID, str(rid))])
+ self.apply_mods([(REPL_TYPE, str(REPLICA_RDWR_TYPE)),
+ (REPL_FLAGS, str(REPLICA_FLAGS_WRITE)),
+ (REPL_ID, str(rid))])
+ except ldap.LDAPError as e:
+ raise ValueError('Failed to update replica: ' + str(e))
+ elif replicarole == ReplicaRole.HUB and newrole == ReplicaRole.MASTER:
+ try:
+ self.apply_mods([(REPL_TYPE, str(REPLICA_RDWR_TYPE)),
+ (REPL_ID, str(rid))])
except ldap.LDAPError as e:
raise ValueError('Failed to update replica: ' + str(e))
def demote(self, newrole):
- """
- Demote a replica to a hub or consumer
+ """Demote a replica to a hub or consumer
+
@param suffix - The replication suffix
@param newrole - The new replication role of this replica
- REPLICAROLE_HUB
- REPLICAROLE_CONSUMER
+ ReplicaRole.HUB
+ ReplicaRole.CONSUMER
@raise ValueError
"""
- if newrole != REPLICAROLE_CONSUMER and newrole != REPLICAROLE_HUB:
- raise ValueError('Can only demote replica to "hub" or
"consumer"')
- #
# Check the role type
- #
- repltype = self.get_attr_val(REPL_TYPE)
- replflags = self.get_attr_val(REPL_FLAGS)
-
- if repltype == REPLICA_TYPE_MASTER and \
- replflags == REPLICA_FLAGS_WRITE:
- replicarole = 3
- elif (repltype == REPLICA_TYPE_HUBCON and
- replflags == REPLICA_FLAGS_WRITE):
- replicarole = 2
- else:
- replicarole = 1
-
- if ROLE_ORDER[newrole] > replicarole:
- raise ValueError('Can not demote replica to lower role:' +
- ' %s -> %s' % (ROLE_TO_NAME[replicarole],
- newrole))
+ replicarole = self.get_role()
+ if newrole.value >= replicarole.value:
+ raise ValueError('Can not demote replica to higher or the same role: {}
-> {}'.format(replicarole.name, newrole.name))
- #
- # Demote it - set the replica type and flags
- #
- if newrole == 'hub':
- flag = '1'
- else:
- flag = '0'
- try:
- self.apply_mods([(REPL_TYPE, '2'), (REPL_FLAGS, flag),
- (REPL_ID, str(CONSUMER_REPLICAID))])
- except ldap.LDAPError as e:
- raise ValueError('Failed to update replica: ' + str(e))
+ # Demote it - set the replica type, flags and rid
+ if replicarole == ReplicaRole.MASTER and newrole == ReplicaRole.HUB:
+ try:
+ self.apply_mods([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)),
+ (REPL_ID, str(CONSUMER_REPLICAID))])
+ except ldap.LDAPError as e:
+ raise ValueError('Failed to update replica: ' + str(e))
+ elif replicarole == ReplicaRole.MASTER and newrole == ReplicaRole.CONSUMER:
+ try:
+ self.apply_mods([(REPL_TYPE, str(REPLICA_RDONLY_TYPE)),
+ (REPL_FLAGS, str(REPLICA_FLAGS_RDONLY)),
+ (REPL_ID, str(CONSUMER_REPLICAID))])
+ except ldap.LDAPError as e:
+ raise ValueError('Failed to update replica: ' + str(e))
+ elif replicarole == ReplicaRole.HUB and newrole == ReplicaRole.CONSUMER:
+ try:
+ self.set(REPL_FLAGS, str(REPLICA_FLAGS_RDONLY))
+ except ldap.LDAPError as e:
+ raise ValueError('Failed to update replica: ' + str(e))
def get_role(self):
"""Return the replica role:
- @return: "master", "hub", or "consumer"
+ @return: 3 for ReplicaRole.MASTER, 2 for ReplicaRole.HUB, 3 for
ReplicaRole.CONSUMER
"""
- repltype = self.get_attr_val(REPL_TYPE)
- replflags = self.get_attr_val(REPL_FLAGS)
-
- if repltype == REPLICA_TYPE_MASTER and \
- replflags == REPLICA_FLAGS_WRITE:
- replicarole = 3
- elif (repltype == REPLICA_TYPE_HUBCON and
- replflags == REPLICA_TYPE_MASTER):
- replicarole = 2
+
+ repltype = self.get_attr_val_int(REPL_TYPE)
+ replflags = self.get_attr_val_int(REPL_FLAGS)
+
+ if repltype == REPLICA_RDWR_TYPE and replflags == REPLICA_FLAGS_WRITE:
+ replicarole = ReplicaRole.MASTER
+ elif repltype == REPLICA_RDONLY_TYPE and replflags == REPLICA_FLAGS_WRITE:
+ replicarole = ReplicaRole.HUB
+ elif repltype == REPLICA_RDONLY_TYPE and replflags == REPLICA_FLAGS_RDONLY:
+ replicarole = ReplicaRole.CONSUMER
else:
- replicarole = 1
+ raise ValueError("Failed to determine a replica role")
- return ROLE_TO_NAME[replicarole]
+ return replicarole
def check_init(self, agmtdn):
"""Check that a total update has completed
+
@returns tuple - first element is done/not done, 2nd is no error/has
error
@param agmtdn - the agreement dn
THIS SHOULD BE IN THE NEW AGREEMENT CLASS
"""
+
done, hasError = False, 0
attrlist = ['cn',
'nsds5BeginReplicaRefresh',
@@ -1084,8 +1055,7 @@ class Replica(DSLdapObject):
entry = self._instance.getEntry(
agmtdn, ldap.SCOPE_BASE, "(objectclass=*)", attrlist)
except NoSuchEntryError:
- self.log.exception("Error reading status from agreement %r" %
- agmtdn)
+ self.log.exception("Error reading status from agreement
{}".format(agmtdn))
hasError = 1
else:
refresh = entry.nsds5BeginReplicaRefresh
@@ -1115,11 +1085,13 @@ class Replica(DSLdapObject):
def wait_init(self, agmtdn):
"""Initialize replication and wait for completion.
+
@oaram agmtdn - agreement dn
@return - 0 if the initialization is complete
THIS SHOULD BE IN THE NEW AGREEMENT CLASS
"""
+
done = False
haserror = 0
while not done and not haserror:
@@ -1134,6 +1106,7 @@ class Replica(DSLdapObject):
@return - 0 if successful
THIS SHOULD BE IN THE NEW AGREEMENT CLASS
"""
+
rc = self.start_async(agmtdn)
if not rc:
rc = self.wait_init(agmtdn)
@@ -1143,10 +1116,11 @@ class Replica(DSLdapObject):
def start_async(self, agmtdn):
"""Initialize replication without waiting.
- @param agmtdn - agreement dn
+ @param agmtdn - agreement dn
- THIS SHOULD BE IN THE NEW AGREEMENT CLASS
+ THIS SHOULD BE IN THE NEW AGREEMENT CLASS
"""
+
self.log.info("Starting async replication %s" % agmtdn)
mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]
self._instance.modify_s(agmtdn, mod)
@@ -1157,6 +1131,7 @@ class Replica(DSLdapObject):
@raise ValeuError - If suffix is not setup for replication
LDAPError - If there is a problem trying to search for the RUV
"""
+
try:
entry = self._instance.search_s(self._suffix,
ldap.SCOPE_SUBTREE,
@@ -1164,20 +1139,19 @@ class Replica(DSLdapObject):
if entry:
return entry[0]
else:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
self._suffix)
except ldap.LDAPError as e:
raise e
def test(self, *replica_dirsrvs):
- '''Make a "dummy" update on the the replicated suffix, and
check
- all the provided replicas to see if they received the update.
+ """Make a "dummy" update on the the replicated suffix,
and check
+ all the provided replicas to see if they received the update.
- @param *replica_dirsrvs - DirSrv instance, DirSrv instance, ...
- @return True - if all servers have recevioed the update by this
- replica, otherwise return False
- @raise LDAPError - when failing to update/search database
- '''
+ @param *replica_dirsrvs - DirSrv instance, DirSrv instance, ...
+ @return True - if all servers have recevioed the update by this
+ replica, otherwise return False
+ @raise LDAPError - when failing to update/search database
+ """
# Generate a unique test value
test_value = ('test replication from ' + self._instance.serverid +
@@ -1217,20 +1191,23 @@ class Replica(DSLdapObject):
class Replicas(DSLdapObjects):
"""Class of all the Replicas"""
+
def __init__(self, instance, batch=False):
"""Init Replicas
+
@param instance - a DirSrv objectc
@param batch - NOT IMPLELMENTED
"""
+
super(Replicas, self).__init__(instance=instance, batch=False)
self._objectclasses = [REPLICA_OBJECTCLASS_VALUE]
self._filterattrs = [REPL_ROOT]
self._childobject = Replica
- self._basedn = 'cn=mapping tree,cn=config'
+ self._basedn = DN_MAPPING_TREE
def get(self, selector=[], dn=None):
- """Wrap Replicas' "get", and set the suffix after we
get the replica
- """
+ """Wrap Replicas' "get", and set the suffix after we
get the replica """
+
replica = super(Replicas, self).get(selector, dn)
if replica:
# Get and set the replica's suffix
@@ -1240,40 +1217,38 @@ class Replicas(DSLdapObjects):
def enable(self, suffix, role, replicaID=None, args=None):
"""Enable replication for this suffix
- @param suffix - The suffix to enable replication for
- @param role - REPLICAROLE_MASTER, REPLICAROLE_HUB or
- REPLICAROLE_CONSUMER
- @param rid - number that identify the supplier replica
- (role=REPLICAROLE_MASTER) in the topology. For
- hub/consumer (role=REPLICAROLE_HUB or
- REPLICAROLE_CONSUMER), rid value is not used. This
- parameter is mandatory for supplier.
-
- @param args - dictionary of additional replica properties
+ @param suffix - The suffix to enable replication for
+ @param role - ReplicaRole.MASTER, ReplicaRole.HUB or
+ ReplicaRole.CONSUMER
+ @param rid - number that identify the supplier replica
+ (role=ReplicaRole.MASTER) in the topology. For
+ hub/consumer (role=ReplicaRole.HUB or
+ ReplicaRole.CONSUMER), rid value is not used. This
+ parameter is mandatory for supplier.
- @return replica DN
+ @param args - dictionary of additional replica properties
- @raise InvalidArgumentError - if missing mandatory arguments
- ValueError - argument with invalid value
- LDAPError - failed to add replica entry
+ @return replica DN
+ @raise InvalidArgumentError - if missing mandatory arguments
+ ValueError - argument with invalid value
+ LDAPError - failed to add replica entry
"""
+
# Normalize the suffix
suffix = normalizeDN(suffix)
# Check validity of role
if not role:
- self._log.fatal("Replica.create: replica role not specify" +
- " (REPLICAROLE_*)")
+ self._log.fatal("Replica.create: replica role is not specified
(ReplicaRole.*)")
raise InvalidArgumentError("role missing")
if not Replica._valid_role(role):
- self._log.fatal("enableReplication: replica role invalid (%s) " %
- role)
+ self._log.fatal("enableReplication: replica role invalid (%s) " %
role)
raise ValueError("invalid role: %s" % role)
# role is fine, set the replica type
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
rtype = REPLICA_RDWR_TYPE
# check the validity of 'rid'
if not Replica._valid_rid(role, rid=replicaID):
@@ -1299,24 +1274,24 @@ class Replicas(DSLdapObjects):
# Now set default values of unset properties
if REPLICA_LEGACY_CONS not in properties:
properties[REPL_LEGACY_CONS] = 'off'
- if role != REPLICAROLE_CONSUMER:
- properties[REPL_FLAGS] = "1"
- #
+ # Set flags explicitly, so it will be more readable
+ if role == ReplicaRole.CONSUMER:
+ properties[REPL_FLAGS] = str(REPLICA_FLAGS_RDONLY)
+ else:
+ properties[REPL_FLAGS] = str(REPLICA_FLAGS_WRITE)
+
# Check if replica entry is already in the mapping-tree
- #
try:
replica = self.get(suffix)
# Should we return an error, or just return the existing relica?
self._log.warn("Already setup replica for suffix %s" % suffix)
return replica
- except:
+ except ldap.NO_SUCH_OBJECT:
pass
- #
# Create changelog
- #
- if (role == REPLICAROLE_MASTER) or (role == REPLICAROLE_HUB):
+ if (role == ReplicaRole.MASTER) or (role == ReplicaRole.HUB):
self._instance.changelog.create()
# Create the default replica manager entry if it does not exist
@@ -1333,9 +1308,7 @@ class Replicas(DSLdapObjects):
repl_manager_dn=properties[REPL_BINDDN],
repl_manager_pw=repl_pw)
- #
# Now create the replica entry
- #
mtents = self._instance.mappingtree.list(suffix=suffix)
suffix_dn = mtents[0].dn
replica = self.create(RDN_REPLICA, properties)
@@ -1343,17 +1316,22 @@ class Replicas(DSLdapObjects):
return replica
- def delete(self, suffix):
+ def disable(self, suffix):
"""Disable replication on the suffix specified
@param suffix - Replicated suffix to disable
@raise ValueError is suffix is not being replicated
"""
+
try:
replica = self.get(suffix)
except ldap.NO_SUCH_OBJECT:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
suffix)
+
+ role = replica.get_role()
+ if role in (ReplicaRole.MASTER, ReplicaRole.HUB):
+ self._instance.changelog.delete()
+
try:
replica.delete()
except ldap.LDAPError as e:
@@ -1365,36 +1343,37 @@ class Replicas(DSLdapObjects):
@param suffix - The replication suffix
@param newrole - The new replication role for the replica:
- REPLICAROLE_MASTER
- REPLICAROLE_HUB
+ ReplicaRole.MASTER
+ ReplicaRole.HUB
@param binddn - The replication bind dn - only applied to master
@param rid - The replication ID - applies only promotions to "master"
@raise ldap.NO_SUCH_OBJECT - If suffix is not replicated
"""
+
replica = self.get(suffix)
try:
replica = self.get(suffix)
except ldap.NO_SUCH_OBJECT:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
suffix)
replica.promote(newrole, binddn, rid)
def demote(self, suffix, newrole):
"""Promote the replica speficied by the suffix to the new role
+
@param suffix - The replication suffix
@param newrole - The new replication role of this replica
- REPLICAROLE_HUB
- REPLICAROLE_CONSUMER
+ ReplicaRole.HUB
+ ReplicaRole.CONSUMER
@raise ldap.NO_SUCH_OBJECT - If suffix is not replicated
"""
+
replica = self.get(suffix)
try:
replica = self.get(suffix)
except ldap.NO_SUCH_OBJECT:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
suffix)
replica.demote(newrole)
def get_dn(self, suffix):
@@ -1407,36 +1386,36 @@ class Replicas(DSLdapObjects):
try:
replica = self.get(suffix)
except ldap.NO_SUCH_OBJECT:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
suffix)
return replica._dn
def get_ruv_entry(self, suffix):
"""Return the database RUV entry for the provided suffix
+
@return - The database RUV entry
@raise ValeuError - If suffix is not setup for replication
LDAPError - If there is a problem trying to search for the RUV
"""
+
try:
replica = self.get(suffix)
except ldap.NO_SUCH_OBJECT:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
suffix)
return replica.get_ruv_entry()
def test(self, suffix, *replica_dirsrvs):
"""Make a "dummy" update on the the replicated suffix,
and check
- all the provided replicas to see if they received the update.
+ all the provided replicas to see if they received the update.
- @param suffix - the replicated suffix we want to check
- @param *replica_dirsrvs - DirSrv instance, DirSrv instance, ...
- @return True - if all servers have recevioed the update by this
- replica, otherwise return False
- @raise LDAPError - when failing to update/search database
+ @param suffix - the replicated suffix we want to check
+ @param *replica_dirsrvs - DirSrv instance, DirSrv instance, ...
+ @return True - if all servers have recevioed the update by this
+ replica, otherwise return False
+ @raise LDAPError - when failing to update/search database
"""
+
try:
replica = self.get(suffix)
except ldap.NO_SUCH_OBJECT:
- raise ValueError('Suffix (%s) is not setup for replication' %
- suffix)
+ raise ValueError('Suffix (%s) is not setup for replication' %
suffix)
return replica.test(*replica_dirsrvs)
diff --git a/lib389/repltools.py b/lib389/repltools.py
index d1f4193..67eb5c3 100644
--- a/lib389/repltools.py
+++ b/lib389/repltools.py
@@ -160,11 +160,11 @@ class ReplTools(object):
ctime = _getCSNTime(inst, csnstr)
if ctime:
role = replObj.get_role()
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
txt = ' Master (%s)' % (inst.serverid)
- elif role == REPLICAROLE_HUB:
+ elif role == ReplicaRole.HUB:
txt = ' Hub (%s)' % (inst.serverid)
- elif role == REPLICAROLE_CONSUMER:
+ elif role == ReplicaRole.CONSUMER:
txt = ' Consumer (%s)' % (inst.serverid)
else:
txt = '?'
diff --git a/lib389/tests/agreement_test.py b/lib389/tests/agreement_test.py
index 456cbfa..c39b08c 100644
--- a/lib389/tests/agreement_test.py
+++ b/lib389/tests/agreement_test.py
@@ -61,7 +61,7 @@ def topology(request):
# Enable replication
master.replica.enableReplication(suffix=SUFFIX,
- role=REPLICAROLE_MASTER,
+ role=ReplicaRole.MASTER,
replicaId=REPLICAID_MASTER)
# Consumer
@@ -80,7 +80,7 @@ def topology(request):
# Enable replication
consumer.replica.enableReplication(suffix=SUFFIX,
- role=REPLICAROLE_CONSUMER)
+ role=ReplicaRole.CONSUMER)
# Delete each instance in the end
def fin():
diff --git a/lib389/tests/replica_test.py b/lib389/tests/replicaLegacy_test.py
similarity index 96%
copy from lib389/tests/replica_test.py
copy to lib389/tests/replicaLegacy_test.py
index 9eb0078..670b81d 100644
--- a/lib389/tests/replica_test.py
+++ b/lib389/tests/replicaLegacy_test.py
@@ -166,7 +166,7 @@ def test_create(topology):
# create a master
topology.master.replica.create(suffix=NEW_SUFFIX_1,
- role=REPLICAROLE_MASTER,
+ role=ReplicaRole.MASTER,
rid=1)
ents = topology.master.replica.list()
assert len(ents) == 1
@@ -174,7 +174,7 @@ def test_create(topology):
# create a consumer
topology.master.replica.create(suffix=NEW_SUFFIX_2,
- role=REPLICAROLE_CONSUMER)
+ role=ReplicaRole.CONSUMER)
ents = topology.master.replica.list()
assert len(ents) == 2
ents = topology.master.replica.list(suffix=NEW_SUFFIX_2)
@@ -185,14 +185,14 @@ def test_create(topology):
#
# create a master
topology.consumer.replica.create(suffix=NEW_SUFFIX_1,
- role=REPLICAROLE_CONSUMER)
+ role=ReplicaRole.CONSUMER)
ents = topology.consumer.replica.list()
assert len(ents) == 1
log.info('Consumer replica %s' % ents[0].dn)
# create a consumer
topology.consumer.replica.create(suffix=NEW_SUFFIX_2,
- role=REPLICAROLE_CONSUMER)
+ role=ReplicaRole.CONSUMER)
ents = topology.consumer.replica.list()
assert len(ents) == 2
ents = topology.consumer.replica.list(suffix=NEW_SUFFIX_2)
@@ -334,11 +334,11 @@ def test_enableReplication(topology):
# a supplier should have replicaId in [1..CONSUMER_REPLICAID[
with pytest.raises(ValueError) as excinfo:
topology.master.replica.enableReplication(suffix=NEW_SUFFIX_3,
- role=REPLICAROLE_MASTER,
+ role=ReplicaRole.MASTER,
replicaId=CONSUMER_REPLICAID)
log.info("Exception (expected): %s" % str(excinfo.value))
topology.master.replica.enableReplication(suffix=NEW_SUFFIX_3,
- role=REPLICAROLE_MASTER,
+ role=ReplicaRole.MASTER,
replicaId=1)
#
@@ -362,11 +362,11 @@ def test_enableReplication(topology):
# A consumer should have CONSUMER_REPLICAID not '1'
with pytest.raises(ValueError) as excinfo:
topology.master.replica.enableReplication(suffix=NEW_SUFFIX_4,
- role=REPLICAROLE_CONSUMER,
+ role=ReplicaRole.CONSUMER,
replicaId=1)
log.info("Exception (expected): %s" % str(excinfo.value))
topology.master.replica.enableReplication(suffix=NEW_SUFFIX_4,
- role=REPLICAROLE_CONSUMER)
+ role=ReplicaRole.CONSUMER)
def test_disableReplication(topology):
diff --git a/lib389/tests/replica_test.py b/lib389/tests/replica_test.py
index 9eb0078..7250d67 100644
--- a/lib389/tests/replica_test.py
+++ b/lib389/tests/replica_test.py
@@ -1,457 +1,373 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2017 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
-import ldap
import os
+import ldap
import pytest
import logging
-from lib389 import InvalidArgumentError
-from lib389._constants import *
-from lib389.properties import *
-from lib389 import DirSrv, Entry
+from lib389 import NoSuchEntryError
+from lib389.replica import Replicas
+from lib389.backend import Backends
+from lib389.idm.domain import Domain
+from lib389._constants import (ReplicaRole, BACKEND_SUFFIX, BACKEND_NAME,
REPLICA_RUV_FILTER, CONSUMER_REPLICAID,
+ REPLICA_FLAGS_WRITE, REPLICA_RDWR_TYPE)
+from lib389.properties import (REPL_FLAGS, REPL_TYPE)
+from lib389.topologies import topology_i3 as topo
logging.getLogger(__name__).setLevel(logging.DEBUG)
log = logging.getLogger(__name__)
-# Used for One master / One consumer topology
-HOST_MASTER = LOCALHOST
-PORT_MASTER = 40389
-SERVERID_MASTER = 'master'
-REPLICAID_MASTER = 1
+DEBUGGING = os.getenv('DEBUGGING', default=False)
+NEW_SUFFIX = 'dc=test,dc=com'
+NEW_BACKEND = 'test_backend'
+REPLICA_MASTER_ID = 1
-HOST_CONSUMER = LOCALHOST
-PORT_CONSUMER = 50389
-SERVERID_CONSUMER = 'consumer'
-TEST_REPL_DN = "uid=test,%s" % DEFAULT_SUFFIX
-INSTANCE_PORT = 54321
-INSTANCE_SERVERID = 'dirsrv'
-INSTANCE_BACKUP = os.environ.get('BACKUPDIR', DEFAULT_BACKUPDIR)
-NEW_SUFFIX_1 = 'ou=test_master'
-NEW_BACKEND_1 = 'test_masterdb'
-NEW_RM_1 = "cn=replrepl,%s" % NEW_SUFFIX_1
+(a)pytest.fixture(scope="module")
+def new_suffixes(topo):
+ """Create new suffix, backend and mapping tree"""
+
+ for num in range(1, 4):
+ backends = Backends(topo.ins["standalone{}".format(num)])
+ backends.create(properties={BACKEND_SUFFIX: NEW_SUFFIX,
+ BACKEND_NAME: NEW_BACKEND})
+ domain = Domain(topo.ins["standalone{}".format(num)], NEW_SUFFIX)
+ domain.create(properties={'dc': 'test', 'description':
NEW_SUFFIX})
+
+
+(a)pytest.fixture(scope="function")
+def simple_replica(topo, new_suffixes, request):
+ """Enable simple multi-master replication"""
+
+ master1 = topo.ins["standalone1"]
+ master2 = topo.ins["standalone2"]
+
+ log.info("Enable two master replicas")
+ replicas_m1 = Replicas(master1)
+ replica_m1 = replicas_m1.enable(suffix=NEW_SUFFIX,
+ role=ReplicaRole.MASTER,
+ replicaID=REPLICA_MASTER_ID)
+ replicas_m2 = Replicas(master2)
+ replica_m2 = replicas_m2.enable(suffix=NEW_SUFFIX,
+ role=ReplicaRole.MASTER,
+ replicaID=REPLICA_MASTER_ID+1)
+
+ log.info("Create agreements between the instances")
+ master1.agreement.create(suffix=NEW_SUFFIX,
+ host=master2.host,
+ port=master2.port)
+ master2.agreement.create(suffix=NEW_SUFFIX,
+ host=master1.host,
+ port=master1.port)
+
+ log.info("Test replication")
+ replicas_m1.test(NEW_SUFFIX, master2)
-NEW_SUFFIX_2 = 'ou=test_consumer'
-NEW_BACKEND_2 = 'test_consumerdb'
+ def fin():
+ replicas_m1.disable(NEW_SUFFIX)
+ replicas_m2.disable(NEW_SUFFIX)
+ request.addfinalizer(fin)
-NEW_SUFFIX_3 = 'ou=test_enablereplication_1'
-NEW_BACKEND_3 = 'test_enablereplicationdb_1'
+ return [replica_m1, replica_m2]
-NEW_SUFFIX_4 = 'ou=test_enablereplication_2'
-NEW_BACKEND_4 = 'test_enablereplicationdb_2'
-NEW_SUFFIX_5 = 'ou=test_enablereplication_3'
-NEW_BACKEND_5 = 'test_enablereplicationdb_3'
+(a)pytest.fixture(scope="function")
+def clean_up(topo, new_suffixes, request):
+ """Check that all replicas were disabled and disable if
not"""
+ def fin():
+ for num in range(1, 4):
+ try:
+ replicas = Replicas(topo.ins["standalone{}".format(num)])
+ replicas.disable(NEW_SUFFIX)
+ log.info("standalone{} is disabled now".format(num))
+ except:
+ pass
+ request.addfinalizer(fin)
-class TopologyReplication(object):
- def __init__(self, master, consumer):
- master.open()
- consumer.open()
- self.master = master
- self.consumer = consumer
+def test_delete_agreements(topo, simple_replica):
+ """Check deleteAgreements method
-(a)pytest.fixture(scope="module")
-def topology(request):
- # Create the master instance
- master = DirSrv(verbose=False)
- master.log.debug("Master allocated")
- args = {SER_HOST: HOST_MASTER,
- SER_PORT: PORT_MASTER,
- SER_SERVERID_PROP: SERVERID_MASTER}
- master.allocate(args)
- if master.exists():
- master.delete()
- master.create()
- master.open()
-
- # Create the consumer instance
- consumer = DirSrv(verbose=False)
- consumer.log.debug("Consumer allocated")
- args = {SER_HOST: HOST_CONSUMER,
- SER_PORT: PORT_CONSUMER,
- SER_SERVERID_PROP: SERVERID_CONSUMER}
- consumer.allocate(args)
- if consumer.exists():
- consumer.delete()
- consumer.create()
- consumer.open()
-
- # Delete each instance in the end
- def fin():
- master.delete()
- consumer.delete()
- request.addfinalizer(fin)
+ :feature: Replication
+ :steps: 1. Enable replication with agreements
+ 2. Delete the agreements
+ 3. Check that agreements were deleted
+ 4. Disable replication
+ :expectedresults: No errors happen, agreements successfully deleted
+ """
+
+ master1 = topo.ins["standalone1"]
+ master2 = topo.ins["standalone2"]
+
+ log.info("Check that agreements in place")
+ ents = master1.agreement.list(suffix=NEW_SUFFIX)
+ assert(len(ents) == 1)
+ ents = master2.agreement.list(suffix=NEW_SUFFIX)
+ assert(len(ents) == 1)
+
+ log.info("Delete the agreements")
+ simple_replica[0].deleteAgreements()
+ simple_replica[1].deleteAgreements()
+
+ log.info("Check that agreements were deleted")
+ ents = master1.agreement.list(suffix=NEW_SUFFIX)
+ assert(len(ents) == 0)
+ ents = master2.agreement.list(suffix=NEW_SUFFIX)
+ assert(len(ents) == 0)
- return TopologyReplication(master, consumer)
+def test_get_ruv_entry(topo, simple_replica):
+ """Check get_ruv_entry method
-def test_create(topology):
- """This test creates
- - suffix/backend (NEW_SUFFIX_[12], NEW_BACKEND_[12]) : Master
- - suffix/backend (NEW_SUFFIX_[12], NEW_BACKEND_[12]) : Consumer
- - replica NEW_SUFFIX_1 as MASTER : Master
- - replica NEW_SUFFIX_2 as CONSUMER : Master
+ :feature: Replication
+ :steps: 1. Enable replication with agreements
+ 2. Get ruv entry with get_ruv_entry() method
+ 3. Get ruv entry with ldap.search
+ 4. Disable replication
+ :expectedresults: Entries should be equal
"""
- log.info("\n\n##########\n### CREATE\n############")
- #
- # MASTER (suffix/backend)
- #
- backendEntry = topology.master.backend.create(
- suffix=NEW_SUFFIX_1, properties={BACKEND_NAME: NEW_BACKEND_1})
- backendEntry = topology.master.backend.create(
- suffix=NEW_SUFFIX_2, properties={BACKEND_NAME: NEW_BACKEND_2})
-
- ents = topology.master.mappingtree.list()
- master_nb_mappingtree = len(ents)
-
- # create a first additional mapping tree
- topology.master.mappingtree.create(NEW_SUFFIX_1, bename=NEW_BACKEND_1)
- ents = topology.master.mappingtree.list()
- assert len(ents) == (master_nb_mappingtree + 1)
- topology.master.add_s(Entry((NEW_SUFFIX_1,
- {'objectclass': "top
organizationalunit".split(),
- 'ou': NEW_SUFFIX_1.split('=', 1)[1]})))
-
- # create a second additional mapping tree
- topology.master.mappingtree.create(NEW_SUFFIX_2, bename=NEW_BACKEND_2)
- ents = topology.master.mappingtree.list()
- assert len(ents) == (master_nb_mappingtree + 2)
- topology.master.add_s(Entry((NEW_SUFFIX_2,
- {'objectclass': "top
organizationalunit".split(),
- 'ou': NEW_SUFFIX_2.split('=', 1)[1]})))
- log.info('Master it exists now %d suffix(es)' % len(ents))
-
- #
- # CONSUMER (suffix/backend)
- #
- backendEntry = topology.consumer.backend.create(
- suffix=NEW_SUFFIX_1, properties={BACKEND_NAME: NEW_BACKEND_1})
- backendEntry = topology.consumer.backend.create(
- suffix=NEW_SUFFIX_2, properties={BACKEND_NAME: NEW_BACKEND_2})
-
- ents = topology.consumer.mappingtree.list()
- consumer_nb_mappingtree = len(ents)
-
- # create a first additional mapping tree
- topology.consumer.mappingtree.create(NEW_SUFFIX_1, bename=NEW_BACKEND_1)
- ents = topology.consumer.mappingtree.list()
- assert len(ents) == (consumer_nb_mappingtree + 1)
- topology.consumer.add_s(Entry((NEW_SUFFIX_1,
- {'objectclass': "top
organizationalunit".split(),
- 'ou': NEW_SUFFIX_1.split('=', 1)[1]})))
-
- # create a second additional mapping tree
- topology.consumer.mappingtree.create(NEW_SUFFIX_2, bename=NEW_BACKEND_2)
- ents = topology.consumer.mappingtree.list()
- assert len(ents) == (consumer_nb_mappingtree + 2)
- topology.consumer.add_s(Entry((NEW_SUFFIX_2,
- {'objectclass': "top
organizationalunit".split(),
- 'ou': NEW_SUFFIX_2.split('=', 1)[1]})))
- log.info('Consumer it exists now %d suffix(es)' % len(ents))
-
- #
- # Now create REPLICAS on master
- #
- # check it exists this entry to stores the changelogs
- topology.master.changelog.create()
-
- # create a master
- topology.master.replica.create(suffix=NEW_SUFFIX_1,
- role=REPLICAROLE_MASTER,
- rid=1)
- ents = topology.master.replica.list()
- assert len(ents) == 1
- log.info('Master replica %s' % ents[0].dn)
-
- # create a consumer
- topology.master.replica.create(suffix=NEW_SUFFIX_2,
- role=REPLICAROLE_CONSUMER)
- ents = topology.master.replica.list()
- assert len(ents) == 2
- ents = topology.master.replica.list(suffix=NEW_SUFFIX_2)
- log.info('Consumer replica %s' % ents[0].dn)
-
- #
- # Now create REPLICAS on consumer
- #
- # create a master
- topology.consumer.replica.create(suffix=NEW_SUFFIX_1,
- role=REPLICAROLE_CONSUMER)
- ents = topology.consumer.replica.list()
- assert len(ents) == 1
- log.info('Consumer replica %s' % ents[0].dn)
+ ruv_entry = simple_replica[0].get_ruv_entry()
+ entry = topo.ins["standalone1"].search_s(NEW_SUFFIX, ldap.SCOPE_SUBTREE,
REPLICA_RUV_FILTER)[0]
- # create a consumer
- topology.consumer.replica.create(suffix=NEW_SUFFIX_2,
- role=REPLICAROLE_CONSUMER)
- ents = topology.consumer.replica.list()
- assert len(ents) == 2
- ents = topology.consumer.replica.list(suffix=NEW_SUFFIX_2)
- log.info('Consumer replica %s' % ents[0].dn)
+ assert ruv_entry == entry
-def test_list(topology):
- """This test checks:
- - existing replicas can be retrieved
- - access to unknown replica does not fail
+def test_get_role(topo, simple_replica):
+ """Check get_role method
- PRE-CONDITION:
- It exists on MASTER two replicas NEW_SUFFIX_1 and NEW_SUFFIX_2
- created by test_create()
+ :feature: Replication
+ :steps: 1. Enable replication with agreements
+ 2. Get role with get_role() method
+ 3. Get repl_flags, repl_type from the replica entry with ldap.search
+ 4. Compare the values
+ 5. Disable replication
+ :expectedresults: The role 'master' should have flags=1 and type=3
"""
- log.info("\n\n############\n### LIST\n############")
- ents = topology.master.replica.list()
- assert len(ents) == 2
+ replica_role = simple_replica[0].get_role()
+ replica_flags = simple_replica[0].get_attr_val_int(REPL_FLAGS)
+ replica_type = simple_replica[0].get_attr_val_int(REPL_TYPE)
- # Check we can retrieve a replica with its suffix
- ents = topology.master.replica.list(suffix=NEW_SUFFIX_1)
- assert len(ents) == 1
- replica_dn_1 = ents[0].dn
+ log.info("Check that we've got role 'master', while {}=1 and
{}=3".format(REPL_FLAGS, REPL_TYPE))
+ assert replica_role == ReplicaRole.MASTER and replica_flags == REPLICA_FLAGS_WRITE \
+ and replica_type == REPLICA_RDWR_TYPE, \
+ "Failure, get_role() gave {}, while {} has {} and {} has
{}".format(replica_role,
+ REPL_FLAGS,
replica_flags,
+ REPL_TYPE,
replica_type)
- # Check we can retrieve a replica with its suffix
- ents = topology.master.replica.list(suffix=NEW_SUFFIX_2)
- assert len(ents) == 1
- replica_dn_2 = ents[0].dn
- # Check we can retrieve a replica with its DN
- ents = topology.master.replica.list(replica_dn=replica_dn_1)
- assert len(ents) == 1
- assert replica_dn_1 == ents[0].dn
+def test_basic(topo, new_suffixes, clean_up):
+ """Check basic replica functionality
+
+ :feature: Replication
+ :steps: 1. Enable replication on master. hub and consumer
+ 2. Create agreements: master-hub, hub-consumer
+ 3. Test master-consumer replication
+ 4. Disable replication
+ 5. Check that replica, agreements and changelog were deleted
+ :expectedresults: No errors happen, replication is successfully enabled and disabled
+ """
- # Check we can retrieve a replica if we provide DN and suffix
- ents = topology.master.replica.list(suffix=NEW_SUFFIX_2,
- replica_dn=replica_dn_2)
+ master = topo.ins["standalone1"]
+ hub = topo.ins["standalone2"]
+ consumer = topo.ins["standalone3"]
+
+ log.info("Enable replicas (create replica and changelog entries)")
+ master_replicas = Replicas(master)
+ master_replicas.enable(suffix=NEW_SUFFIX,
+ role=ReplicaRole.MASTER,
+ replicaID=REPLICA_MASTER_ID)
+ ents = master_replicas.list()
+ assert len(ents) == 1
+ ents = master.changelog.list()
assert len(ents) == 1
- assert replica_dn_2 == ents[0].dn
- # Check DN is used before suffix name
- ents = topology.master.replica.list(suffix=NEW_SUFFIX_2,
- replica_dn=replica_dn_1)
+ hub_replicas = Replicas(hub)
+ hub_replicas.enable(suffix=NEW_SUFFIX,
+ role=ReplicaRole.HUB,
+ replicaID=CONSUMER_REPLICAID)
+ ents = hub_replicas.list()
+ assert len(ents) == 1
+ ents = hub.changelog.list()
assert len(ents) == 1
- assert replica_dn_1 == ents[0].dn
-
- # Check that invalid value does not break
- ents = topology.master.replica.list(suffix="X")
- for ent in ents:
- log.critical("Unexpected replica: %s" % ent.dn)
- assert len(ents) == 0
-
-
-def test_create_repl_manager(topology):
- """The tests are
- - create the default Replication manager/Password
- - create a specific Replication manager/ default Password
- - Check we can bind successfully
- - create a specific Replication manager / specific Password
- - Check we can bind successfully
- """
- log.info("\n\n###########\n### CREATE_REPL_MANAGER\n###########")
- # First create the default replication manager
- topology.consumer.replica.create_repl_manager()
- ents = topology.consumer.search_s(defaultProperties[REPLICATION_BIND_DN],
- ldap.SCOPE_BASE, "objectclass=*")
+ consumer_replicas = Replicas(consumer)
+ consumer_replicas.enable(suffix=NEW_SUFFIX,
+ role=ReplicaRole.CONSUMER)
+ ents = consumer_replicas.list()
assert len(ents) == 1
- assert ents[0].dn == defaultProperties[REPLICATION_BIND_DN]
- # Second create a custom replication manager under NEW_SUFFIX_2
- rm_dn = "cn=replrepl,%s" % NEW_SUFFIX_2
- topology.consumer.replica.create_repl_manager(repl_manager_dn=rm_dn)
- ents = topology.consumer.search_s(rm_dn, ldap.SCOPE_BASE, "objectclass=*")
+ log.info("Create agreements between the instances")
+ master.agreement.create(suffix=NEW_SUFFIX,
+ host=hub.host,
+ port=hub.port)
+ ents = master.agreement.list(suffix=NEW_SUFFIX)
assert len(ents) == 1
- assert ents[0].dn == rm_dn
-
- # Check we can bind
- topology.consumer.simple_bind_s(rm_dn,
- defaultProperties[REPLICATION_BIND_PW])
-
- # Check we fail to bind
- with pytest.raises(ldap.INVALID_CREDENTIALS) as excinfo:
- topology.consumer.simple_bind_s(rm_dn, "dummy")
- log.info("Exception: %s" % str(excinfo.value))
-
- # now rebind
- topology.consumer.simple_bind_s(topology.consumer.binddn,
- topology.consumer.bindpw)
-
- # Create a custom replication manager under NEW_SUFFIX_1
- # with a specified password
- rm_dn = NEW_RM_1
- topology.consumer.replica.create_repl_manager(repl_manager_dn=rm_dn,
- repl_manager_pw="Secret123")
- ents = topology.consumer.search_s(rm_dn, ldap.SCOPE_BASE, "objectclass=*")
+ hub.agreement.create(suffix=NEW_SUFFIX,
+ host=consumer.host,
+ port=consumer.port)
+ ents = hub.agreement.list(suffix=NEW_SUFFIX)
assert len(ents) == 1
- assert ents[0].dn == rm_dn
- # Check we can bind
- topology.consumer.simple_bind_s(rm_dn, "Secret123")
+ log.info("Test replication")
+ master_replicas.test(NEW_SUFFIX, consumer)
+
+ log.info("Disable replication")
+ master_replicas.disable(suffix=NEW_SUFFIX)
+ hub_replicas.disable(suffix=NEW_SUFFIX)
+ consumer_replicas.disable(suffix=NEW_SUFFIX)
+
+ log.info("Check that replica, agreements and changelog were deleted")
+ for num in range(1, 4):
+ log.info("Checking standalone{} instance".format(num))
+ inst = topo.ins["standalone{}".format(num)]
+
+ log.info("Checking that replica entries don't exist")
+ replicas = Replicas(inst)
+ ents = replicas.list()
+ assert len(ents) == 0
+
+ log.info("Checking that changelog doesn't exist")
+ ents = inst.changelog.list()
+ assert len(ents) == 0
+
+ log.info("Checking that agreements can't be acquired because the replica
entry doesn't exist")
+ with pytest.raises(NoSuchEntryError) as e:
+ inst.agreement.list(suffix=NEW_SUFFIX)
+ assert "no replica set up" in e.msg
+
+
+(a)pytest.mark.parametrize('role_from,role_to',
+ ((ReplicaRole.CONSUMER, ReplicaRole.HUB),
+ (ReplicaRole.CONSUMER, ReplicaRole.MASTER),
+ (ReplicaRole.HUB, ReplicaRole.MASTER)))
+def test_promote(topo, new_suffixes, clean_up, role_from, role_to):
+ """Check that replica promote method works properly
+
+ :feature: Replication
+ :steps: 1. Enable replication on the instance
+ 2. Promote it to another role
+ (check consumer-hub, consumer-master, hub-master
+ 3. Check that role was successfully changed
+ 4. Disable replication
+ :expectedresults: No errors happen, replica successfully promoted
+ """
- # Check we fail to bind
- with pytest.raises(ldap.INVALID_CREDENTIALS) as excinfo:
- topology.consumer.simple_bind_s(rm_dn, "dummy")
- log.info("Exception: %s" % str(excinfo.value))
- topology.consumer.simple_bind_s(topology.consumer.binddn,
- topology.consumer.bindpw)
+ inst = topo.ins["standalone1"]
+ log.info("Enable replication on instance with a role -
{}".format(role_from))
+ replicas = Replicas(inst)
+ replica = replicas.enable(suffix=NEW_SUFFIX,
+ role=role_from)
-def test_enableReplication(topology):
- """It checks
- - Ability to enable replication on a supplier
- - Ability to enable replication on a consumer
- - Failure to enable replication with wrong replicaID on supplier
- - Failure to enable replication with wrong replicaID on consumer
- """
+ log.info("Promote replica to {}".format(role_to))
+ replica.promote(newrole=role_to,
+ rid=REPLICA_MASTER_ID)
- log.info("\n\n############\n### ENABLEREPLICATION\n##########")
- #
- # MASTER (suffix/backend)
- #
- backendEntry = topology.master.backend.create(suffix=NEW_SUFFIX_3,
- properties={BACKEND_NAME:
- NEW_BACKEND_3})
-
- ents = topology.master.mappingtree.list()
- master_nb_mappingtree = len(ents)
-
- # create a first additional mapping tree
- topology.master.mappingtree.create(NEW_SUFFIX_3, bename=NEW_BACKEND_3)
- ents = topology.master.mappingtree.list()
- assert len(ents) == (master_nb_mappingtree + 1)
- topology.master.add_s(Entry((NEW_SUFFIX_3,
- {'objectclass': "top
organizationalunit".split(),
- 'ou': NEW_SUFFIX_3.split('=', 1)[1]})))
-
- # a supplier should have replicaId in [1..CONSUMER_REPLICAID[
- with pytest.raises(ValueError) as excinfo:
- topology.master.replica.enableReplication(suffix=NEW_SUFFIX_3,
- role=REPLICAROLE_MASTER,
- replicaId=CONSUMER_REPLICAID)
- log.info("Exception (expected): %s" % str(excinfo.value))
- topology.master.replica.enableReplication(suffix=NEW_SUFFIX_3,
- role=REPLICAROLE_MASTER,
- replicaId=1)
-
- #
- # MASTER (suffix/backend)
- #
- backendEntry = topology.master.backend.create(suffix=NEW_SUFFIX_4,
- properties={BACKEND_NAME:
- NEW_BACKEND_4})
-
- ents = topology.master.mappingtree.list()
- master_nb_mappingtree = len(ents)
-
- # create a first additional mapping tree
- topology.master.mappingtree.create(NEW_SUFFIX_4, bename=NEW_BACKEND_4)
- ents = topology.master.mappingtree.list()
- assert len(ents) == (master_nb_mappingtree + 1)
- topology.master.add_s(Entry((NEW_SUFFIX_4,
- {'objectclass': "top
organizationalunit".split(),
- 'ou': NEW_SUFFIX_4.split('=', 1)[1]})))
-
- # A consumer should have CONSUMER_REPLICAID not '1'
- with pytest.raises(ValueError) as excinfo:
- topology.master.replica.enableReplication(suffix=NEW_SUFFIX_4,
- role=REPLICAROLE_CONSUMER,
- replicaId=1)
- log.info("Exception (expected): %s" % str(excinfo.value))
- topology.master.replica.enableReplication(suffix=NEW_SUFFIX_4,
- role=REPLICAROLE_CONSUMER)
-
-
-def test_disableReplication(topology):
- """It checks
- - Ability to disable replication on a supplier
- - Ability to disable replication on a consumer
- - Failure to disable replication with wrong suffix on supplier
- - Failure to disable replication with wrong suffix on consumer
+ log.info("Check that replica was successfully promoted")
+ replica_role = replica.get_role()
+ assert replica_role == role_to
+
+
+(a)pytest.mark.parametrize('role_from,role_to',
+ ((ReplicaRole.MASTER, ReplicaRole.HUB),
+ (ReplicaRole.MASTER, ReplicaRole.CONSUMER),
+ (ReplicaRole.HUB, ReplicaRole.CONSUMER)))
+def test_demote(topo, new_suffixes, clean_up, role_from, role_to):
+ """Check that replica demote method works properly
+
+ :feature: Replication
+ :steps: 1. Enable replication on the instance
+ 2. Demote it to another role
+ (check master-hub, master-consumer, hub-consumer)
+ 3. Check that role was successfully changed
+ 4. Disable replication
+ :expectedresults: No errors happen, replica successfully demoted
"""
- log.info("\n\n############\n### DISABLEREPLICATION\n##########")
- topology.master.replica.disableReplication(suffix=NEW_SUFFIX_3)
- with pytest.raises(ldap.LDAPError) as excinfo:
- topology.master.replica.disableReplication(suffix=NEW_SUFFIX_3)
- log.info("Exception (expected): %s" % str(excinfo.value))
+ inst = topo.ins["standalone1"]
+
+ log.info("Enable replication on instance with a role -
{}".format(role_from.name))
+ replicas = Replicas(inst)
+ replica = replicas.enable(suffix=NEW_SUFFIX,
+ role=role_from,
+ replicaID=REPLICA_MASTER_ID)
+
+ log.info("Promote replica to {}".format(role_to.name))
+ replica.demote(newrole=role_to)
- topology.master.replica.disableReplication(suffix=NEW_SUFFIX_4)
- with pytest.raises(ldap.LDAPError) as excinfo:
- topology.master.replica.disableReplication(suffix=NEW_SUFFIX_4)
- log.info("Exception (expected): %s" % str(excinfo.value))
+ log.info("Check that replica was successfully promoted")
+ replica_role = replica.get_role()
+ assert replica_role == role_to
-def test_setProperties(topology):
- """Set some properties
- Verified that valid properties are set
- Verified that invalid properties raise an Exception
+(a)pytest.mark.parametrize('role_from', (ReplicaRole.MASTER,
+ ReplicaRole.HUB,
+ ReplicaRole.CONSUMER))
+def test_promote_fail(topo, new_suffixes, clean_up, role_from):
+ """Check that replica promote method fails
+ when promoted to wrong direction
- PRE-REQUISITE: it exists a replica for NEW_SUFFIX_1
+ :feature: Replication
+ :steps: 1. Enable replication on the instance
+ 2. Try to promote it to wrong role
+ (for example, master-hub, hub-consumer)
+ 3. Disable replication
+ :expectedresults: Replica shouldn't be promoted
"""
- log.info("\n\n##########\n### SETPROPERTIES\n############")
- # set valid values to SUFFIX_1
- properties = {REPLICA_LEGACY_CONS: 'off',
- REPLICA_BINDDN: NEW_RM_1,
- REPLICA_PURGE_INTERVAL: str(3600),
- REPLICA_PURGE_DELAY: str(5 * 24 * 3600),
- REPLICA_REFERRAL: "ldap://%s:1234/" % LOCALHOST}
- topology.master.replica.setProperties(suffix=NEW_SUFFIX_1,
- properties=properties)
-
- # Check the values have been written
- replicas = topology.master.replica.list(suffix=NEW_SUFFIX_1)
- assert len(replicas) == 1
- for prop in properties:
- attr = REPLICA_PROPNAME_TO_ATTRNAME[prop]
- val = replicas[0].getValue(attr)
- log.info("Replica[%s] -> %s: %s" % (prop, attr, val))
- assert val == properties[prop]
-
- # Check invalid properties raise exception
- with pytest.raises(ValueError) as excinfo:
- properties = {"dummy": 'dummy'}
- topology.master.replica.setProperties(suffix=NEW_SUFFIX_1,
- properties=properties)
- log.info("Exception (expected): %s" % str(excinfo.value))
-
- # check call without suffix/dn/entry raise InvalidArgumentError
- with pytest.raises(InvalidArgumentError) as excinfo:
- properties = {REPLICA_LEGACY_CONS: 'off'}
- topology.master.replica.setProperties(properties=properties)
- log.info("Exception (expected): %s" % str(excinfo.value))
-
- # check that if we do not provide a valid entry it raises ValueError
- with pytest.raises(ValueError) as excinfo:
- properties = {REPLICA_LEGACY_CONS: 'off'}
- topology.master.replica.setProperties(replica_entry="dummy",
- properties=properties)
- log.info("Exception (expected): %s" % str(excinfo.value))
-
- # check that with an invalid suffix or replica_dn it raise ValueError
- with pytest.raises(ValueError) as excinfo:
- properties = {REPLICA_LEGACY_CONS: 'off'}
- topology.master.replica.setProperties(suffix="dummy",
- properties=properties)
- log.info("Exception (expected): %s" % str(excinfo.value))
-
-
-def test_getProperties(topology):
- """Currently not implemented"""
-
- log.info("\n\n############\n### GETPROPERTIES\n###########")
- with pytest.raises(NotImplementedError) as excinfo:
- properties = {REPLICA_LEGACY_CONS: 'off'}
- topology.master.replica.getProperties(suffix=NEW_SUFFIX_1)
- log.info("Exception (expected): %s" % str(excinfo.value))
+ inst = topo.ins["standalone1"]
+
+ log.info("Enable replication on instance with a role -
{}".format(role_from.name))
+ replicas = Replicas(inst)
+ replica = replicas.enable(suffix=NEW_SUFFIX,
+ role=role_from,
+ replicaID=REPLICA_MASTER_ID)
+
+ for role_to in [x for x in range(1, 4) if x <= role_from.value]:
+ role_to = ReplicaRole(role_to)
+ log.info("Try to promote replica to {}".format(role_to.name))
+ with pytest.raises(ValueError):
+ replica.promote(newrole=role_to,
+ rid=REPLICA_MASTER_ID)
+
+
+(a)pytest.mark.parametrize('role_from', (ReplicaRole.MASTER,
+ ReplicaRole.HUB,
+ ReplicaRole.CONSUMER))
+def test_demote_fail(topo, new_suffixes, clean_up, role_from):
+ """Check that replica demote method fails
+ when demoted to wrong direction
+
+ :feature: Replication
+ :steps: 1. Enable replication on the instance
+ 2. Try to demote it to wrong role
+ (for example, consumer-master, hub-master)
+ 3. Disable replication
+ :expectedresults: Replica shouldn't be demoted
+ """
+
+ inst = topo.ins["standalone1"]
+
+ log.info("Enable replication on instance with a role -
{}".format(role_from.name))
+ replicas = Replicas(inst)
+ replica = replicas.enable(suffix=NEW_SUFFIX,
+ role=role_from,
+ replicaID=REPLICA_MASTER_ID)
+
+ for role_to in [x for x in range(1, 4) if x >= role_from.value]:
+ role_to = ReplicaRole(role_to)
+ log.info("Try to demote replica to {}".format(role_to.name))
+ with pytest.raises(ValueError):
+ replica.demote(newrole=role_to)
if __name__ == "__main__":
diff --git a/lib389/topologies.py b/lib389/topologies.py
index 161d887..225e138 100644
--- a/lib389/topologies.py
+++ b/lib389/topologies.py
@@ -16,8 +16,7 @@ from lib389 import DirSrv
from lib389.utils import generate_ds_params
from lib389.replica import Replicas
from lib389._constants import (args_instance, SER_HOST, SER_PORT, SER_SERVERID_PROP,
SER_CREATION_SUFFIX,
- ROLE_STANDALONE, REPLICAROLE_MASTER, REPLICAROLE_HUB,
REPLICAROLE_CONSUMER,
- DEFAULT_SUFFIX, REPLICA_ID)
+ ReplicaRole, DEFAULT_SUFFIX, REPLICA_ID)
DEBUGGING = os.getenv('DEBUGGING', default=False)
if DEBUGGING:
@@ -35,7 +34,7 @@ def create_topology(topo_dict):
"""
if not topo_dict:
- ValueError("You need to specify the dict. For instance: {ROLE_STANDALONE:
1}")
+ ValueError("You need to specify the dict. For instance:
{ReplicaRole.STANDALONE: 1}")
instances = {}
ms = {}
@@ -68,22 +67,22 @@ def create_topology(topo_dict):
instance.delete()
instance.create()
instance.open()
- if role == ROLE_STANDALONE:
+ if role == ReplicaRole.STANDALONE:
ins[instance.serverid] = instance
instances.update(ins)
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
ms[instance.serverid] = instance
instances.update(ms)
- if role == REPLICAROLE_HUB:
+ if role == ReplicaRole.HUB:
hs[instance.serverid] = instance
instances.update(hs)
- if role == REPLICAROLE_CONSUMER:
+ if role == ReplicaRole.CONSUMER:
cs[instance.serverid] = instance
instances.update(cs)
log.info("Instance with parameters {} was
created.".format(args_copied))
# Set up replication
- if role in (REPLICAROLE_MASTER, REPLICAROLE_HUB, REPLICAROLE_CONSUMER):
+ if role in (ReplicaRole.MASTER, ReplicaRole.HUB, ReplicaRole.CONSUMER):
replicas = Replicas(instance)
replica = replicas.enable(DEFAULT_SUFFIX, role,
instance_data[REPLICA_ID])
replica_dict[replica] = instance
@@ -91,16 +90,18 @@ def create_topology(topo_dict):
# Create agreements
for role_from in topo_dict.keys():
for inst_num_from in range(1, topo_dict[role]+1):
- roles_to = [REPLICAROLE_HUB, REPLICAROLE_CONSUMER]
- if role == REPLICAROLE_MASTER:
- roles_to.append(REPLICAROLE_MASTER)
+ roles_to = [ReplicaRole.HUB, ReplicaRole.CONSUMER]
+ if role == ReplicaRole.MASTER:
+ roles_to.append(ReplicaRole.MASTER)
for role_to in [role for role in topo_dict if role in roles_to]:
for inst_num_to in range(1, topo_dict[role]+1):
# Exclude our instance
if role_from != role_to or inst_num_from != inst_num_to:
- inst_from = instances["{}{}".format(role_from,
inst_num_from)]
- inst_to = instances["{}{}".format(role_to,
inst_num_to)]
+ inst_from_id = "{}{}".format(role_from.name.lower(),
inst_num_from)
+ inst_to_id = "{}{}".format(role_to.name.lower(),
inst_num_to)
+ inst_from = instances[inst_from_id]
+ inst_to = instances[inst_to_id]
agmt = inst_from.agreement.create(suffix=DEFAULT_SUFFIX,
host=inst_to.host,
port=inst_to.port)
@@ -163,7 +164,7 @@ class TopologyMain(object):
def topology_st(request):
"""Create DS standalone instance"""
- topology = create_topology({ROLE_STANDALONE: 1})
+ topology = create_topology({ReplicaRole.STANDALONE: 1})
def fin():
if DEBUGGING:
@@ -179,7 +180,7 @@ def topology_st(request):
def topology_i2(request):
"""Create two instance DS deployment"""
- topology = create_topology({ROLE_STANDALONE: 2})
+ topology = create_topology({ReplicaRole.STANDALONE: 2})
def fin():
if DEBUGGING:
@@ -195,7 +196,7 @@ def topology_i2(request):
def topology_i3(request):
"""Create three instance DS deployment"""
- topology = create_topology({ROLE_STANDALONE: 3})
+ topology = create_topology({ReplicaRole.STANDALONE: 3})
def fin():
if DEBUGGING:
@@ -211,8 +212,8 @@ def topology_i3(request):
def topology_m1c1(request):
"""Create Replication Deployment with one master and one
consumer"""
- topology = create_topology({REPLICAROLE_MASTER: 1,
- REPLICAROLE_CONSUMER: 1})
+ topology = create_topology({ReplicaRole.MASTER: 1,
+ ReplicaRole.CONSUMER: 1})
replicas = Replicas(topology.ms["master1"])
replicas.test(DEFAULT_SUFFIX, topology.cs["consumer1"])
@@ -230,9 +231,9 @@ def topology_m1c1(request):
def topology_m1h1c1(request):
"""Create Replication Deployment with one master, one consumer and one
hub"""
- topology = create_topology({REPLICAROLE_MASTER: 1,
- REPLICAROLE_HUB: 1,
- REPLICAROLE_CONSUMER: 1})
+ topology = create_topology({ReplicaRole.MASTER: 1,
+ ReplicaRole.HUB: 1,
+ ReplicaRole.CONSUMER: 1})
replicas = Replicas(topology.ms["master1"])
replicas.test(DEFAULT_SUFFIX, topology.cs["consumer1"])
@@ -250,7 +251,7 @@ def topology_m1h1c1(request):
def topology_m2(request):
"""Create Replication Deployment with two masters"""
- topology = create_topology({REPLICAROLE_MASTER: 2})
+ topology = create_topology({ReplicaRole.MASTER: 2})
replicas = Replicas(topology.ms["master1"])
replicas.test(DEFAULT_SUFFIX, topology.ms["master2"])
@@ -268,7 +269,7 @@ def topology_m2(request):
def topology_m3(request):
"""Create Replication Deployment with three masters"""
- topology = create_topology({REPLICAROLE_MASTER: 3})
+ topology = create_topology({ReplicaRole.MASTER: 3})
replicas = Replicas(topology.ms["master1"])
replicas.test(DEFAULT_SUFFIX, topology.ms["master3"])
@@ -286,7 +287,7 @@ def topology_m3(request):
def topology_m4(request):
"""Create Replication Deployment with four masters"""
- topology = create_topology({REPLICAROLE_MASTER: 4})
+ topology = create_topology({ReplicaRole.MASTER: 4})
replicas = Replicas(topology.ms["master1"])
replicas.test(DEFAULT_SUFFIX, topology.ms["master4"])
@@ -304,8 +305,8 @@ def topology_m4(request):
def topology_m2c2(request):
"""Create Replication Deployment with two masters and two
consumers"""
- topology = create_topology({REPLICAROLE_MASTER: 2,
- REPLICAROLE_CONSUMER: 2})
+ topology = create_topology({ReplicaRole.MASTER: 2,
+ ReplicaRole.CONSUMER: 2})
replicas = Replicas(topology.ms["master1"])
replicas.test(DEFAULT_SUFFIX, topology.cs["consumer1"])
diff --git a/lib389/utils.py b/lib389/utils.py
index 5cb3230..70748a6 100644
--- a/lib389/utils.py
+++ b/lib389/utils.py
@@ -39,8 +39,8 @@ from contextlib import closing
import lib389
from lib389.paths import Paths
from lib389._constants import (
- DEFAULT_USER, VALGRIND_WRAPPER, DN_CONFIG, CFGSUFFIX, LOCALHOST,
ROLE_STANDALONE,
- REPLICAROLE_MASTER, REPLICAROLE_HUB, REPLICAROLE_CONSUMER, CONSUMER_REPLICAID
+ DEFAULT_USER, VALGRIND_WRAPPER, DN_CONFIG, CFGSUFFIX, LOCALHOST,
+ ReplicaRole, CONSUMER_REPLICAID
)
from lib389.properties import (
SER_HOST, SER_USER_ID, SER_GROUP_ID, SER_STRICT_HOSTNAME_CHECKING, SER_PORT,
@@ -747,44 +747,45 @@ def formatInfData(args):
return content
-def generate_ds_params(inst_num, role=ROLE_STANDALONE):
+def generate_ds_params(inst_num, role=ReplicaRole.STANDALONE):
"""Generate a host, port, secure port, server ID and replica ID
for the selected role and instance number. I.e. inst_num=1, role="master".
@param inst_num - an instance number in a range from 1 to 99
- @param role - ROLE_STANDALONE, REPLICAROLE_MASTER, REPLICAROLE_HUB,
REPLICAROLE_CONSUMER
+ @param role - ReplicaRole.STANDALONE, ReplicaRole.MASTER, ReplicaRole.HUB,
ReplicaRole.CONSUMER
@return - the dict with next keys: host, port, secure port, server id and replica id
"""
if inst_num not in range(1, 100):
raise ValueError("Instance number should be in a range from 1 to 99")
- if role not in (ROLE_STANDALONE, REPLICAROLE_MASTER, REPLICAROLE_HUB,
REPLICAROLE_CONSUMER):
- raise ValueError('Role should be {}, {}, {}, {}'.format(ROLE_STANDALONE,
REPLICAROLE_MASTER,
- REPLICAROLE_HUB,
REPLICAROLE_CONSUMER))
+ if role not in (ReplicaRole.STANDALONE, ReplicaRole.MASTER, ReplicaRole.HUB,
ReplicaRole.CONSUMER):
+ raise ValueError('Role should be {}, {}, {},
{}'.format(ReplicaRole.STANDALONE, ReplicaRole.MASTER,
+ ReplicaRole.HUB,
ReplicaRole.CONSUMER))
instance_data = {}
relevant_num = 38900
# Set relevant number for ports
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
relevant_num += 100
- elif role == REPLICAROLE_HUB:
+ elif role == ReplicaRole.HUB:
relevant_num += 200
- elif role == REPLICAROLE_CONSUMER:
+ elif role == ReplicaRole.CONSUMER:
relevant_num += 300
# Define replica ID
- if role == REPLICAROLE_MASTER:
+ if role == ReplicaRole.MASTER:
replica_id = inst_num
else:
replica_id = CONSUMER_REPLICAID
# Fill the dict with data
+ server_id = "{}{}".format(role.name.lower(), inst_num)
instance_data[SER_HOST] = LOCALHOST
instance_data[SER_PORT] = relevant_num + inst_num
instance_data[SER_SECURE_PORT] = relevant_num + inst_num + 24700
- instance_data[SER_SERVERID_PROP] = "{}{}".format(role, inst_num)
+ instance_data[SER_SERVERID_PROP] = server_id
instance_data[REPLICA_ID] = replica_id
return instance_data
diff --git a/python-lib389.spec b/python-lib389.spec
index 3200e59..88901c9 100644
--- a/python-lib389.spec
+++ b/python-lib389.spec
@@ -34,6 +34,7 @@ Requires: krb5-workstation
Requires: krb5-server
Requires: openssl
Requires: iproute
+Requires: python-enum34
# Conditional will need to change later.
%if 0%{?rhel} >= 8 || 0%{?fedora}
Requires: python2
diff --git a/requirements.txt b/requirements.txt
index 1f58b78..f208938 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,5 @@ pyasn1
pyasn1-modules
six
pytest
-python-dateutil
\ No newline at end of file
+python-dateutil
+enum34
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.