Convert one of a number of inputs into a valid ip:port string.

    Elements which are not provided are filled in as follows:

        * IP Address: the system is asked for the set of IP addresses associated
          with the machine and the first one is used, preferring those matching
          `address` if it is a wildcard.
        * Port number: a random port is selected from the pool of dynamically-available
          port numbers.

    This means you can pass any of: nothing; a hostname; an IP address; an IP address with wildcards; a port number

    If an IP address is supplied but is invalid, an InvalidAddressError
    exception is raised.

    :param address: (optional) Any of: an IP address, a port number, or both

    :returns: a valid ip:port string for this machine

def address(address=None):
    """Convert one of a number of inputs into a valid ip:port string.

    Elements which are not provided are filled in as follows:

        * IP Address: the system is asked for the set of IP addresses associated
          with the machine and the first one is used, preferring those matching
          `address` if it is a wildcard.
        * Port number: a random port is selected from the pool of dynamically-available
          port numbers.

    This means you can pass any of: nothing; a hostname; an IP address; an IP address with wildcards; a port number

    If an IP address is supplied but is invalid, an InvalidAddressError
    exception is raised.

    :param address: (optional) Any of: an IP address, a port number, or both

    :returns: a valid ip:port string for this machine
    """
    address = str(address or "").strip()
    #
    # If the address is an ip:port pair, split into its component parts.
    # Otherwise, try to determine whether we're looking at an IP
    # or at a port and leave the other one blank
    #
    host_or_ip, port = split_address(address)

    #
    # If the port has been supplied, make sure it's numeric and that it's a valid
    # port number. If it hasn't been supplied, remove a random one from the pool
    # of possible dynamically-allocated ports and use that.
    #
    if port:
        try:
            port = int(port)
        except ValueError:
            raise AddressError("Port %s must be a number" % port)
        if port not in config.VALID_PORTS:
            raise AddressError("Port %d must be in range %d - %d" % (
                port, min(config.VALID_PORTS), max(config.VALID_PORTS))
            )
    else:
        random.shuffle(PORT_POOL)
        port = PORT_POOL.pop()

    #
    # The address part could be an IP address (optionally including
    # wildcards to indicate a preference) or a hostname or nothing.
    # If it's a hostname we attempt to resolve it to an IP address.
    # It it's nothing or a wildcard we query the system for a matching IP address.
    #
    if (not host_or_ip) or is_valid_ip_pattern(host_or_ip):
        #
        # If a specific IP address is given, use that.
        # If an IP pattern is given (ie something with a wildcard in it) treat
        # that as no address with a preference for that wildcard.
        #
        prefer = None
        if "*" in host_or_ip:
            host_or_ip, prefer = None, [host_or_ip]

        #
        # If no IP (or only a wildcard) is specified, query the system for valid
        # addresses, preferring those which match the wildcard. NB if the preference
        # matches one we've previously used, we can return a cached address. But
        # different requests can specify different wildcard preferences.
        #
        if not host_or_ip:
            if _ip4 and _prefer == prefer:
                ip = _ip4
            else:
                ip = _find_ip4(prefer)
        else:
            ip = host_or_ip

    else:
        #
        # Treat the string as a hostname and resolve to an IP4 address
        #
        try:
            ip = socket.gethostbyname(host_or_ip)
        except socket.gaierror as exc:
            _logger.error("gaierror %d for %s", exc.errno, host_or_ip)
            raise InvalidAddressError(host_or_ip, exc.errno)
        else:
            #
            # Bizarrely specific check because BT Internet "helpfully"
            # redirects DNS fails to this address which hosts a sponsored
            # landing page!
            #
            if ip == "92.242.132.15":
                raise InvalidAddressError(host_or_ip, 0)

    return "%s:%s" % (ip, port)