"""
contains all available validation functions
"""
import common_utils as utils
import re
import logging
import output_messages
import basedefs
import types
import traceback
import os
import os.path
import tempfile
import cracklib
from setup_controller import Controller

def validateNFSMountPoint(param, options=[]):
    """ Validates the correct mount point for NFS local storage """

    if validateMountPoint(param) and validateDirSize(param, basedefs.CONST_MINIMUM_SPACE_ISODOMAIN):
            return True

    return False

def validateMountPoint(path):
    logging.info("validating %s as a valid mount point" % (path))
    if not utils.verifyStringFormat(path, "^\/[\w\_\-\s]+(\/[\w\_\-\s]+)*\/?$"):
        print output_messages.INFO_VAL_PATH_NAME_INVALID
        return False
    if _isPathInExportFs(path):
        print output_messages.INFO_VAL_PATH_NAME_IN_EXPORTS
        return False
    if os.path.exists(path) and len(os.listdir(path)):
        print output_messages.INFO_VAR_PATH_NOT_EMPTY % path
        return False
    if not _isPathWriteable(_getBasePath(path)):
        print output_messages.INFO_VAL_PATH_NOT_WRITEABLE
        return False

    return True

def validateDirSize(path, size):
    availableSpace = utils.getAvailableSpace(_getBasePath(path))
    if availableSpace < size:
        print output_messages.INFO_VAL_PATH_SPACE % (path,
                                                     utils.transformUnits(availableSpace),
                                                     utils.transformUnits(size))
        return False
    return True

def validateInteger(param, options=[]):
    try:
        int(param)
        return True
    except:
        logging.warn("validateInteger('%s') - failed" %(param))
        print output_messages.INFO_VAL_NOT_INTEGER
        return False

def validatePort(param, options = []):
    #TODO: add actual port check with socket open
    logging.debug("Validating %s as a valid TCP Port" % (param))
    minVal = 0
    controller = Controller()
    isProxyEnabled = utils.compareStrIgnoreCase(controller.CONF["OVERRIDE_HTTPD_CONFIG"], "yes")
    if not isProxyEnabled:
        minVal = 1024
    if not validateInteger(param, options):
        return False
    port = int(param)
    if not (port > minVal and port < 65535) :
        logging.warn(output_messages.INFO_VAL_PORT_NOT_RANGE %(minVal))
        print output_messages.INFO_VAL_PORT_NOT_RANGE %(minVal)
        return False
    if isProxyEnabled and param in[basedefs.JBOSS_HTTP_PORT, basedefs.JBOSS_HTTPS_PORT, basedefs.JBOSS_AJP_PORT]:
        logging.warn(output_messages.INFO_VAL_PORT_OCCUPIED_BY_JBOSS %(param))
        print output_messages.INFO_VAL_PORT_OCCUPIED_BY_JBOSS %(param)
        return False
    (portOpen, process, pid) = utils.isTcpPortOpen(param)
    if portOpen:
        logging.warn(output_messages.INFO_VAL_PORT_OCCUPIED % (param, process, pid))
        print output_messages.INFO_VAL_PORT_OCCUPIED % (param, process, pid)
        return False
    if isProxyEnabled and not checkAndSetHttpdPortPolicy(param):
        logging.warn(output_messages.INFO_VAL_FAILED_ADD_PORT_TO_HTTP_POLICY, port)
        print output_messages.INFO_VAL_FAILED_ADD_PORT_TO_HTTP_POLICY % port
        return False
    return True

def checkAndSetHttpdPortPolicy(port):
    def parsePorts(portsStr):
        ports = []
        for part in portsStr.split(","):
            part = part.strip().split("-")
            if len(part) > 1:
                for port in range(int(part[0]),int(part[1])):
                    ports.append(port)
            else:
                ports.append(int(part[0]))
        return ports

    newPort = int(port)
    cmd = [
        basedefs.EXEC_SEMANAGE, "port", "-l",
    ]
    out, rc = utils.execCmd(cmdList=cmd) #, "-t", "http_port_t"])
    if rc:
        return False
    httpPortsList = []
    pattern = re.compile("^http_port_t\s*tcp\s*([0-9, \-]*)$")
    for line in out.splitlines():
        httpPortPolicy = re.match(pattern, line)
        if httpPortPolicy:
            httpPortsList = parsePorts(httpPortPolicy.groups()[0])
    logging.debug("http_port_t = %s"%(httpPortsList))
    if newPort in httpPortsList:
        return True
    else:
        cmd = [
            basedefs.EXEC_SEMANAGE,
            "port",
            "-a",
            "-t", "http_port_t",
            "-p", "tcp",
            "%d"%(newPort),
        ]
        out, rc = utils.execCmd(cmdList=cmd, failOnError=False, usePipeFiles=True)
        if rc:
            logging.error(out)
            return False
    return True



def validateRemotePort(param, options = []):
    #Validate that the port is an integer betweeen 1024 and 65535
    logging.debug("Validating %s as a valid TCP Port" % (param))
    if validateInteger(param, options):
        port = int(param)
        if (port > 0 and port < 65535):
            return True
        else:
            logging.warn("validatePort('%s') - failed" %(param))
            print output_messages.INFO_VAL_PORT_NOT_RANGE

    return False

def validateStringNotEmpty(param, options=[]):
    if type(param) != types.StringType or len(param) == 0:
        logging.warn("validateStringNotEmpty('%s') - failed" %(param))
        print output_messages.INFO_VAL_STRING_EMPTY
        return False
    else:
        return True

def validatePassword(param, options=[]):
    logging.debug("Validating password")
    if not validateStringNotEmpty(param, options):
        return False
    try:
        cracklib.FascistCheck(param)
    except:
        logging.warn("Password failed check")
        logging.warn(traceback.format_exc())
        print output_messages.WARN_WEAK_PASS

    return True

def validateOptions(param, options=[]):
    logging.info("Validating %s as part of %s"%(param, options))
    if not validateStringNotEmpty(param, options):
        return False
    if "yes" in options and param.lower() == "y":
        return True
    if "no" in options and param.lower() == "n":
        return True
    if param.lower() in [option.lower() for option in options]:
        return True
    print output_messages.INFO_VAL_NOT_IN_OPTIONS % (", ".join(options))
    return False

def validateOverrideHttpdConfAndChangePortsAccordingly(param, options=[]):
    """
    This validation function is specific for the OVERRIDE_HTTPD_CONF param and it does more than validating the answer.
    It actually changes the default HTTP/S ports in case the user choose not to override the httpd configuration.
    """
    logging.info("validateOverrideHttpdConfAndChangePortsAccordingly %s as part of %s"%(param, options))
    retval = validateOptions(param, options)
    if retval and param.lower() == "no":
        logging.debug("Changing HTTP_PORT & HTTPS_PORT to the default jboss values (8080 & 8443)")
        controller = Controller()
        utils.setHttpPortsToNonProxyDefault(controller)
    elif retval:
        #stopping httpd service (in case it's up) when the configuration can be overridden
        logging.debug("stopping httpd service")
        utils.Service(basedefs.HTTPD_SERVICE_NAME).stop()
    return retval


def validateDomain(param, options=[]):
    """
    Validate domain name
    """
    logging.info("validating %s as a valid domain string" % (param))
    (errMsg, rc) = _validateString(param, 1, 1024, "^[\w\-\_]+\.[\w\.\-\_]+\w+$")

    # Right now we print a generic error, might want to change it in the future
    if rc != 0:
        print output_messages.INFO_VAL_NOT_DOMAIN
        return False
    else:
        return True

def validateUser(param, options=[]):
    """
    Validate Auth Username
    Setting a logical max value of 256
    """
    logging.info("validating %s as a valid user name" % (param))
    (errMsg, rc) = _validateString(param, 1, 256, "^\w[\w\.\-\_\%\@]{2,}$")

    # Right now we print a generic error, might want to change it in the future
    if rc != 0:
        print output_messages.INFO_VAL_NOT_USER
        return False
    else:
        return True

def validateRemoteHost(param, options=[]):
    """ Validate that the we are working with remote DB host
    """
    # If we received localhost, use default flow.
    # If not local, REMOTE_DB group is run.
    # It means returning True if remote, and False if local

    if "DB_REMOTE_INSTALL" in param.keys() and param["DB_REMOTE_INSTALL"] == "remote":
        return True
    else:
        return False

def validateRemoteDB(param={}, options=[]):
    """ Ensure, that params provided for the remote DB are
     working, and if not, issue the correct error.
    """

    logging.info("Validating %s as a RemoteDb" % param["DB_HOST"])
    if utils.localHost(param["DB_HOST"]):
        logging.info("%s is a local host, no connection checks needed" % param["DB_HOST"])
        return True

    if "DB_ADMIN" not in param.keys():
        param["DB_ADMIN"] = basedefs.DB_ADMIN
        param["DB_PORT"] = basedefs.DB_PORT
        param["DB_PASS"] = param["DB_LOCAL_PASS"]
    else:
        param["DB_PASS"] = param["DB_REMOTE_PASS"]

    # Create a new pgpass, store previous in backupFile
    backupFile = _createTempPgPass(param["DB_ADMIN"], param["DB_HOST"],
                                   param["DB_PORT"], param["DB_PASS"])

    # Now, let's check credentials:
    try:
        # Connection check
        _checkDbConnection(param["DB_ADMIN"], param["DB_HOST"], param["DB_PORT"])

        # DB Create check
        _checkCreateDbPrivilege(param["DB_ADMIN"], param["DB_HOST"], param["DB_PORT"])

        # UUID extention check.
        _checkUUIDExtension(param["DB_ADMIN"], param["DB_HOST"], param["DB_PORT"])

        # Delete DB check
        _checkDropDbPrivilege(param["DB_ADMIN"], param["DB_HOST"], param["DB_PORT"])

        # Everything is fine, return True
        return True

    except Exception,e:
        # Something failed, print the error on screen and return False
        print e
        return False

    finally:
        # if the test DB was created, drop it
        sqlQuery = "DROP DATABASE IF EXISTS ovirt_engine_test;"
        utils.execRemoteSqlCommand(param["DB_ADMIN"],
                                   param["DB_HOST"],
                                   param["DB_PORT"],
                                   basedefs.DB_POSTGRES, sqlQuery, False)

        # restore the original pgpass file in all cases
        if os.path.exists(backupFile):
            os.rename(backupFile, basedefs.DB_PASS_FILE)


def validateFQDN(param, options=[]):
    logging.info("Validating %s as a FQDN"%(param))
    if not validateDomain(param,options):
        return False
    try:
        #get set of IPs
        ipAddresses = utils.getConfiguredIps()
        if len(ipAddresses) < 1:
            logging.error("Could not find any configured IP address on the host")
            raise Exception(output_messages.ERR_EXP_CANT_FIND_IP)

        #resolve fqdn
        pattern = 'Address: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
        resolvedAddresses = _getPatternFromNslookup(param, pattern)
        if len(resolvedAddresses) < 1:
            logging.error("Failed to resolve %s"%(param))
            print output_messages.ERR_DIDNT_RESOLVED_IP%(param)
            return False

        #string is generated here since we use it in all latter error messages
        prettyString = " ".join(["%s"%string for string in resolvedAddresses])

        #compare found IP with list of local IPs and match.
        if not resolvedAddresses.issubset(ipAddresses):
            logging.error("the following address(es): %s are not configured on this host"%(prettyString))
            #different grammar for plural and single
            if len(resolvedAddresses) > 1:
                print output_messages.ERR_IPS_NOT_CONFIGED%(prettyString, param)
            else:
                print output_messages.ERR_IPS_NOT_CONFIGED_ON_INT%(prettyString, param)
            return False

        #reverse resolved IP and compare with given fqdn
        counter = 0
        pattern = '[\w\.-]+\s+name\s\=\s([\w\.\-]+)\.'
        for address in resolvedAddresses:
            addressSet = _getPatternFromNslookup(address, pattern)
            reResolvedAddress = None
            if len(addressSet) > 0:
                reResolvedAddress = addressSet.pop()
            if reResolvedAddress == param: