import time
import ldap
import logging
import pytest
import os
import re
from lib389._constants import *
from lib389.config import Config
from lib389 import DirSrv, Entry
from lib389.topologies import topology_m3 as topo

DEBUGGING = os.getenv("DEBUGGING", default=False)
if DEBUGGING:
    logging.getLogger(__name__).setLevel(logging.DEBUG)
else:
    logging.getLogger(__name__).setLevel(logging.INFO)
log = logging.getLogger(__name__)

USER_CN="user"

def add_user(server, no, desc='dummy', sleep=True):
    cn = '%s%d' % (USER_CN, no)
    dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)
    log.fatal('Adding user (%s): ' % dn)
    server.add_s(Entry((dn, {'objectclass': ['top', 'person', 'inetuser', 'userSecurityInformation'],
                             'sn': ['_%s' % cn],
                             'description': [desc]})))
    time.sleep(1)

def check_user(server, no, timeout=10):
    
    cn = '%s%d' % (USER_CN, no)
    dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)
    found = False
    cpt = 0
    while cpt < timeout:
        try:
            server.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
            found = True
            break
        except ldap.NO_SUCH_OBJECT:
            time.sleep(1)
            cpt += 1
    return found

def pattern_errorlog(server, log_pattern):
    file_obj = open(server.errlog, "r")

    found = None
    # Use a while true iteration because 'for line in file: hit a
    while True:
        line = file_obj.readline()
        found = log_pattern.search(line)
        if ((line == '') or (found)):
            break

    return found

def fractional_server_to_replica(server, replica):
    server_to_replica = server.agreement.list(suffix=SUFFIX, consumer_host=replica.host, consumer_port=replica.port)
    server.modify_s(server_to_replica[0].dn,
                              [(ldap.MOD_REPLACE,
                                'nsDS5ReplicatedAttributeListTotal',
                                '(objectclass=*) $ EXCLUDE telephoneNumber'),
                               (ldap.MOD_REPLACE,
                                'nsDS5ReplicatedAttributeList',
                                '(objectclass=*) $ EXCLUDE telephoneNumber'),
                                (ldap.MOD_REPLACE, 'nsds5ReplicaStripAttrs',
                                'modifiersname modifytimestamp')])


def do_skipped_update(server, no):
    cn = '%s%d' % (USER_CN, no)
    dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)
    for i in range(110):
        server.modify_s(dn, [(ldap.MOD_REPLACE, 'telephoneNumber', str(i))])
        
def count_pattern_accesslog(server, log_pattern):
    file_obj = open(server.accesslog, "r")

    count = 0
    # Use a while true iteration because 'for line in file: hit a
    while True:
        line = file_obj.readline()
        if (log_pattern.search(line)):
            count = count + 1
        if ((line == '')):
            break
    file_obj.close()

    return count

def test_ticket_49463(topo):
    """Specify a test case purpose or name here

    :id: d1aa2e8b-e6ab-4fc6-9c63-c6f622544f2d
    :setup: Fill in set up configuration here
    :steps:
        1. Enable fractional replication
        2. Enable replication logging
        3. Check that replication is working fine
        4. Generate skipped updates to create keep alive entries
        5. Remove M3 from the topology
        6. issue cleanAllRuv that will run on M1 and M2
    :expectedresults:
        1. No report of failure when the RUV is updated
    """
    
    # Configure fractional (skip telephonenumber) replication
    M1 = topo.ms["master1"]
    M2 = topo.ms["master2"]
    M3 = topo.ms["master3"]
    fractional_server_to_replica(M1, M2)
    fractional_server_to_replica(M1, M3)
    fractional_server_to_replica(M2, M1)
    fractional_server_to_replica(M2, M3)
    fractional_server_to_replica(M3, M1)
    fractional_server_to_replica(M3, M2)
    for i in (M1, M2, M3):
        i.restart()
    

    # enable internal op logging and replication debug
    for i in (M1, M2, M3):
        i.config.loglevel(vals=[256 + 4], service='access')
        i.config.loglevel(vals=[LOG_REPLICA, LOG_DEFAULT], service='error')
    
    # Check that replication is working fine
    add_user(M1, 11, desc="add to M1")
    add_user(M2, 21, desc="add to M2")
    add_user(M3, 31, desc="add to M3")
    
    for i in (M1, M2, M3):
            assert check_user(i, 11)
            assert check_user(i, 21)
            assert check_user(i, 31)
    
        
    # Generate skipped updates to create keep alive entries
    for i in (M1, M2, M3):
        do_skipped_update(i, 11)
        
    time.sleep(10)
    
    # Remove M3 from the topology
    M3.stop()
    M1.agreement.delete(suffix=SUFFIX, consumer_host=M3.host, consumer_port=M3.port)
    M2.agreement.delete(suffix=SUFFIX, consumer_host=M3.host, consumer_port=M3.port)
    
    # Then issue cleanAllRuv that will run on M1 and M2
    M1.tasks.cleanAllRUV(suffix=SUFFIX, replicaid='3',
                        force=True, args={TASK_WAIT: True})

    # Count the number of received DEL of the keep alive 3
    time.sleep(20)
    regex = re.compile(".*DEL dn=.cn=repl keep alive 3.*")
    count = count_pattern_accesslog(M1, regex)
    log.debug("count = %d" % count)
    
    # check that DEL is replicated once (If DEL is kept in the fix)
    # check that DEL is is not replicated (If DEL is finally no long done in the fix)
    assert ((count == 1) or (count == 0)) 
    
    # Check replication M1 <-> M2 can recover
    add_user(M1, 12, desc="add to M1")
    add_user(M2, 22, desc="add to M2")
    for i in (M1, M2):
            assert check_user(i, 12)
            assert check_user(i, 22)
    
    # If you need any test suite initialization,
    # please, write additional fixture for that (including finalizer).
    # Topology for suites are predefined in lib389/topologies.py.

    # If you need host, port or any other data about instance,
    # Please, use the instance object attributes for that (for example, topo.ms["master1"].serverid)

    if DEBUGGING:
        # Add debugging steps(if any)...
        pass


if __name__ == '__main__':
    # Run isolated
    # -s for DEBUG mode
    CURRENT_FILE = os.path.realpath(__file__)
    pytest.main("-s %s" % CURRENT_FILE)

