# --- BEGIN COPYRIGHT BLOCK ---
# Copyright (C) 2022 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
import logging
import pytest
import os
import ldap
from lib389.backend import Backends
from lib389._constants import *
from test389.topologies import topology_st
from lib389.utils import ds_is_older, ensure_str, is_fips
from subprocess import check_output

pytestmark = pytest.mark.tier1

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

FD_ATTR = "nsslapd-maxdescriptors"
RESRV_FD_ATTR = "nsslapd-reservedescriptors"
SLAPD_DEFAULT_MAXDESCRIPTORS = 1048576
SYSTEMD_LIMIT = ensure_str(check_output("systemctl show -p LimitNOFILE dirsrv@standalone1".split(" ")).strip()).split('=')[1]
# Server caps maxdescriptors at min(its own rlim_max, SLAPD_DEFAULT_MAXDESCRIPTORS); pytest's rlim_max is unrelated.
if SYSTEMD_LIMIT == "infinity":
    MAX_FD_VAL = SLAPD_DEFAULT_MAXDESCRIPTORS
else:
    MAX_FD_VAL = min(int(SYSTEMD_LIMIT), SLAPD_DEFAULT_MAXDESCRIPTORS)
CUSTOM_VAL = str(MAX_FD_VAL - 10)
RESRV_DESC_VAL_LOW = 10
TOO_HIGH_VAL = str(MAX_FD_VAL + 1)
TOO_HIGH_VAL2 = str(MAX_FD_VAL + 1)
TOO_LOW_VAL = "0"

@pytest.mark.skipif(ds_is_older("1.4.1.2"), reason="Not implemented")
def test_fd_limits(topology_st):
    """Test the default limits, and custom limits

    :id: fa0a5106-612f-428f-84c0-9c85c34d0433
    :setup: Standalone Instance
    :steps:
        1. Check default limit
        2. Change default limit
        3. Check invalid/too high limits are rejected
        4. Check invalid/too low limit is rejected
    :expectedresults:
        1. Success
        2. Success
        3. Success
        4. Success
    """

    # Check systemd default
    max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
    assert max_fd == str(MAX_FD_VAL)

    # Check custom value is applied
    topology_st.standalone.config.set(FD_ATTR, CUSTOM_VAL)
    max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
    assert max_fd == CUSTOM_VAL

    # # Attempt to use value that is higher than the global system limit
    with pytest.raises(ldap.UNWILLING_TO_PERFORM):
        topology_st.standalone.config.set(FD_ATTR, TOO_HIGH_VAL)
    max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
    assert max_fd == CUSTOM_VAL

    # Attempt to use value that is higher than the value defined in the systemd service
    with pytest.raises(ldap.UNWILLING_TO_PERFORM):
        topology_st.standalone.config.set(FD_ATTR, TOO_HIGH_VAL2)
    max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
    assert max_fd == CUSTOM_VAL

    # Attempt to use val that is too low
    with pytest.raises(ldap.OPERATIONS_ERROR):
        topology_st.standalone.config.set(FD_ATTR, TOO_LOW_VAL)
    max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
    assert max_fd == CUSTOM_VAL

    log.info("test_fd_limits PASSED")

@pytest.mark.skipif(ds_is_older("1.4.1.2"), reason="Not implemented")
def test_reserve_descriptor_validation(topology_st):
    """Test the reserve descriptor self check

    :id: 9bacdbcc-7754-4955-8a56-1d8c82bce274
    :setup: Standalone Instance
    :steps:
        1. Set attr nsslapd-reservedescriptors to a low value (10)
        2. Verify low value has been set
        3. Restart instance (On restart the reservedescriptor attr will be validated)
        4. Verify corrected value for nsslapd-reservedescriptors > low value
    :expectedresults:
        1. Success
        2. A value of RESRV_DESC_VAL_LOW (10) is returned
        3. Success
        4. Corrected value for nsslapd-reservedescriptors > low value
    """

    # Set nsslapd-reservedescriptors to a low value (10)
    topology_st.standalone.config.set(RESRV_FD_ATTR, str(RESRV_DESC_VAL_LOW))
    resrv_fd = int(topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR))
    assert resrv_fd == RESRV_DESC_VAL_LOW

    # An instance restart triggers a validation of the configured nsslapd-reservedescriptors attribute
    topology_st.standalone.restart()

    # Get the corrected value
    corrected_fd = int(topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR))
    assert corrected_fd > RESRV_DESC_VAL_LOW

    log.info(f"test_reserve_descriptor_validation PASSED (corrected from {RESRV_DESC_VAL_LOW} to {corrected_fd})")

@pytest.mark.skipif(ds_is_older("1.4.1.2"), reason="Not implemented")
def test_reserve_descriptors_high(topology_st):
    """Test setting reserve descriptor value to higher than average.

    :id: 19c8991b-ef78-485e-bdf9-a0977fcbcd04
    :setup: Standalone Instance
    :steps:
        1. Set attr nsslapd-maxdescriptors to the server's effective max
        2. Verify value has been set
        3. Set attr nsslapd-reservedescriptors to 2 less than nsslapd-maxdescriptors
        4. Verify value has been set
        5. Restart instance
    :expectedresults:
        1. Success
        2. Value of MAX_FD_VAL is returned
        3. Success
        4. Value of MAX_FD_VAL - 2 is returned
        5. Instance starts correctly
    """

    # Set nsslapd-maxdescriptors to a custom value
    topology_st.standalone.config.set(FD_ATTR, str(MAX_FD_VAL))
    max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
    assert max_fd == str(MAX_FD_VAL)

    # Set nsslapd-reservedescriptors to 2 less than custom value
    topology_st.standalone.config.set(RESRV_FD_ATTR, str(MAX_FD_VAL - 2))
    resrv_fd = topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR)
    assert resrv_fd == str(MAX_FD_VAL - 2)

    # Verify instance restart
    topology_st.standalone.restart()
    assert (topology_st.standalone.status())

    log.info("test_reserve_descriptors_high PASSED")

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