import os
import sys
import time
import ldap
import logging
import pytest
from lib389 import DirSrv, Entry, tools, tasks
from lib389.tools import DirSrvTools
from lib389._constants import *
from lib389.properties import *
from lib389.tasks import *
from lib389.utils import *
from lib389.mit_krb5 import MitKrb5


#########################################
#
# WARNING!!!!! If this test is failing, and your here to find out why, the
# reason is very likely your hosts file!!!!
#
# IT MUST LOOK LIKE THIS BELOW: Note the unique IPS for each kdc name!
#
# 127.0.0.1       ldapkdc.example.com localhost
# 127.0.1.1       ldapkdc1.example.com
# 127.0.2.1       ldapkdc2.example.com
#
#########################################

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

REALM = "EXAMPLE.COM"

HOST_MASTER_1 = 'ldapkdc1.example.com'
HOST_MASTER_2 = 'ldapkdc2.example.com'

class TopologyReplication(object):
    def __init__(self, master1, master2):
        master1.open()
        self.master1 = master1
        master2.open()
        self.master2 = master2


@pytest.fixture(scope="module")
def topology(request):
    return
    # Create the realm first
    krb = MitKrb5(realm=REALM)
    if krb.check_realm():
        krb.destroy_realm()
    krb.create_realm()
    DEBUG = False

    # Creating master 1...
    master1 = DirSrv(verbose=DEBUG)
    args_instance[SER_HOST] = HOST_MASTER_1
    args_instance[SER_PORT] = PORT_MASTER_1
    args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
    args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
    args_instance[SER_REALM] = REALM
    args_instance[SER_STRICT_HOSTNAME_CHECKING] = False
    args_master = args_instance.copy()
    master1.allocate(args_master)
    instance_master1 = master1.exists()
    if instance_master1:
        master1.delete()
    master1.create() # There is some magic in .create that finds the realm, and adds the keytab for us.
    master1.open()
    master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)

    # Creating master 2...
    master2 = DirSrv(verbose=DEBUG)
    args_instance[SER_HOST] = HOST_MASTER_2
    args_instance[SER_PORT] = PORT_MASTER_2
    args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
    args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
    args_instance[SER_REALM] = REALM
    args_instance[SER_STRICT_HOSTNAME_CHECKING] = False
    args_master = args_instance.copy()
    master2.allocate(args_master)
    instance_master2 = master2.exists()
    if instance_master2:
        master2.delete()
    master2.create()
    master2.open()
    master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)

    # Delete each instance in the end
    def fin():
        master1.delete()
        master2.delete()
        if krb.check_realm():
            krb.destroy_realm()
    request.addfinalizer(fin)

    # Clear out the tmp dir
    master1.clearTmpDir(__file__)

    return TopologyReplication(master1, master2)

def _create_machine_ou(inst):
    inst.add_s( Entry(( "ou=Machines,%s" % DEFAULT_SUFFIX, {
            'objectClass' : 'top organizationalUnit'.split(),
            'ou' : 'Machines'
        }
        ))
    )

def _create_machine_account(inst, name):
    # Create the simple security objects for the servers to replicate to
    inst.add_s( Entry(( "uid=%s,ou=Machines,%s" % (name, DEFAULT_SUFFIX),
        {
            'objectClass' : 'top account'.split(),
            'uid' : name
        }
        )))

def _check_machine_account(inst, name):
    r = inst.search_s( 'ou=Machines,%s' % DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(uid=%s)' % name)
    if len(r) > 0:
        return True
    return False

def _allow_machine_account(inst, name):
    # First we need to get the mapping tree dn
    mt = inst.mappingtree.list(suffix=DEFAULT_SUFFIX)[0]
    inst.modify_s('cn=replica,%s' % mt.dn, [
        (ldap.MOD_REPLACE, 'nsDS5ReplicaBindDN', "uid=%s,ou=Machines,%s" % (name, DEFAULT_SUFFIX))
    ])

def test_gssapi_repl(topology):
    """
    Create a kdc, then using that, provision two masters which have a gssapi
    authenticated replication agreement.
    """
    return
    master1 = topology.master1
    master2 = topology.master2


    # Create the locations on each master for the other to bind to.
    _create_machine_ou(master1)
    _create_machine_ou(master2)

    _create_machine_account(master1, 'ldap/%s' % HOST_MASTER_1)
    _create_machine_account(master1, 'ldap/%s' % HOST_MASTER_2)
    _create_machine_account(master2, 'ldap/%s' % HOST_MASTER_1)
    _create_machine_account(master2, 'ldap/%s' % HOST_MASTER_2)

    # Set on the cn=replica config to accept the other masters princ mapping under mapping tree
    _allow_machine_account(master1, 'ldap/%s' % HOST_MASTER_2)
    _allow_machine_account(master2, 'ldap/%s' % HOST_MASTER_1)

    #
    # Create all the agreements
    #
    # Creating agreement from master 1 to master 2

    # Set the replica bind method to sasl gssapi
    properties = {RA_NAME:      r'meTo_$host:$port',
                  RA_METHOD:    'SASL/GSSAPI',
                  RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
    m1_m2_agmt = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
    if not m1_m2_agmt:
        log.fatal("Fail to create a master -> master replica agreement")
        sys.exit(1)
    log.debug("%s created" % m1_m2_agmt)

    # Creating agreement from master 2 to master 1

    # Set the replica bind method to sasl gssapi
    properties = {RA_NAME:      r'meTo_$host:$port',
                  RA_METHOD:    'SASL/GSSAPI',
                  RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
    m2_m1_agmt = master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
    if not m2_m1_agmt:
        log.fatal("Fail to create a master -> master replica agreement")
        sys.exit(1)
    log.debug("%s created" % m2_m1_agmt)

    # Allow the replicas to get situated with the new agreements...
    time.sleep(5)

    #
    # Initialize all the agreements
    #
    master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
    master1.waitForReplInit(m1_m2_agmt)

    # Check replication is working...
    if master1.testReplication(DEFAULT_SUFFIX, master2):
        log.info('Replication is working.')
    else:
        log.fatal('Replication is not working.')
        assert False

    # Add a user to master 1
    _create_machine_account(master1, 'http/one.example.com')
    # Check it's on 2
    time.sleep(5)
    assert(_check_machine_account(master2, 'http/one.example.com'))
    # Add a user to master 2
    _create_machine_account(master2, 'http/two.example.com')
    # Check it's on 1
    time.sleep(5)
    assert(_check_machine_account(master2, 'http/two.example.com'))


    log.info('Test complete')


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