Skip to content
This repository has been archived by the owner on Oct 22, 2019. It is now read-only.

Commit

Permalink
Adding optional error return on validation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh Conant authored and Josh Conant committed Jun 9, 2016
1 parent 8a8708d commit 17ca5b2
Show file tree
Hide file tree
Showing 2 changed files with 336 additions and 72 deletions.
205 changes: 151 additions & 54 deletions calico_containers/pycalico/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ def get_hostname():
return socket.gethostname()


def validate_port_str(port_str):
def validate_port_str(port_str, return_error=False):
"""
Checks whether the command line word specifying a set of ports is valid.
"""
return validate_ports(port_str.split(","))
return validate_ports(port_str.split(","), return_error=return_error)


def validate_ports(port_list):
def validate_ports(port_list, return_error=False):
"""
Checks whether a list of ports are within range of 0 and 65535.
The port list must include a number or a number range.
Expand All @@ -123,7 +123,9 @@ def validate_ports(port_list):
:param port_list:
:return: a Boolean: True if in range, False if not in range
"""
err = None
in_range = True

for port in port_list:
if ":" in str(port):
ports = port.split(":")
Expand All @@ -135,152 +137,211 @@ def validate_ports(port_list):
except ValueError:
in_range = False
if not in_range:
err = ValueError("Port is invalid. Value must be between 0 and "
"65536 ('{0}' given).".format(port))
break

return in_range
return err if return_error else in_range


def validate_characters(input_string):
def validate_characters(input_string, return_error=False):
"""
Validate that characters in string are supported by Felix.
Felix supports letters a-z, numbers 0-9, and symbols _.-
:param input_string: string to be validated
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if valid, False if invalid
"""
err = None
# List of valid characters that Felix permits
valid_chars = '[a-zA-Z0-9_\.\-]'

# Check for invalid characters
valid = True
if not re.match("^%s+$" % valid_chars, input_string):
return False
else:
return True
valid = False
err = ValueError("Invalid string. Felix only supports alphanumeric "
"and the symbols '_', '.', and '-' ('{0}' given)"
"".format(input_string))

return err if return_error else valid


def validate_icmp_type(icmp_type):
def validate_icmp_type(icmp_type, return_error=False):
"""
Validate that icmp_type is an integer between 0 and 255.
If not return False.
:param icmp_type: int value representing an icmp type
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if valid icmp type, False if not
"""
err = None

try:
valid = 0 <= int(icmp_type) < 255
except ValueError:
valid = False
return valid

if not valid and return_error:
err = ValueError("ICMP type is invalid. Value must be between 0 and "
"255 ('{0}' given).".format(icmp_type))

return err if return_error else valid


def validate_hostname_port(hostname_port):
def validate_hostname_port(hostname_port, return_error=False):
"""
Validate the hostname and port format. (<HOSTNAME>:<PORT>)
An IPv4 address is a valid hostname.
:param hostname_port: The string to verify
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if valid, False if invalid
"""
# Should contain a single ":" separating hostname and port
if not isinstance(hostname_port, str):
_log.error("Must provide string for hostname:port validation, not: %s"
% type(hostname_port))
return False
err_mess = ("Must provide string for hostname:port validation, not: "
"{0}".format(type(hostname_port)))
_log.error(err_mess)
return TypeError(err_mess) if return_error else False

try:
(hostname, port) = hostname_port.split(":")
except ValueError:
_log.error("Must provide a string splittable by ':' for hostname-port.")
return False
err_mess = "Must provide a string splittable by ':' for hostname-port."
_log.error(err_mess)
return ValueError(err_mess) if return_error else False

# Check the hostname format.
if not validate_hostname(hostname):
return False
result = validate_hostname(hostname, return_error=return_error)
if return_error and result:
return result
elif not return_error and not result:
return result

# Check port range.
try:
port = int(port)
except ValueError:
_log.error("Port must be an integer.")
return False
err_mess = "Port must be an integer."
_log.error(err_mess)
return TypeError(err_mess) if return_error else False
if port < 1 or port > 65535:
_log.error("Provided port (%d) must be between 1 and 65535." % port)
return False
return True
err_mess = ("Provided port {0} must be between 1 and 65535."
"".format(port))
_log.error(err_mess)
return ValueError(err_mess) if return_error else False
return None if return_error else True


def validate_hostname(hostname):
def validate_hostname(hostname, return_error=False):
"""
Validate a hostname string. This allows standard hostnames and IPv4
addresses.
:param hostname: The hostname to validate.
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if valid, False if invalid
"""
# Hostname length is limited.
if not isinstance(hostname, str):
_log.error("Hostname must be a string, not %s" % type(hostname))
return False
err_mess = "Hostname must be a string, not {0}".format(hostname)
_log.error(err_mess)
return TypeError(err_mess) if return_error else False
hostname_len = len(hostname)
if hostname_len > 255:
_log.error("Hostname length (%d) should be less than 255 characters."
% hostname_len)
return False
err_mess = ("Hostname length (%d) should be less than 255 characters."
"".format(hostname_len))
_log.error(err_mess)
return ValueError(err_mess) if return_error else False

# Hostname labels may consist of numbers, letters and hyphens, but may not
# end or begin with a hyphen.
allowed = re.compile("(?!-)[a-z\d-]{1,63}(?<!-)$", re.IGNORECASE)
if not all(allowed.match(x) for x in hostname.split(".")):
_log.error("Hostname label may only consist of numbers, letters, and "
"hyphens (but may not end or begin with a hyphen.")
return False
return True
err_mess = ("Hostname label may only consist of numbers, letters, "
"and hyphens (but may not end or begin with a hyphen.")
_log.error(err_mess)
return ValueError(err_mess) if return_error else False
return None if return_error else True


def validate_asn(asn):
def validate_asn(asn, return_error=False):
"""
Validate the format of a 2-byte or 4-byte autonomous system number
:param asn: User input of AS number
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if valid format, False if invalid format
"""
asn_ok = True
err = None
err_mess = None

try:
if "." in str(asn):
left_asn, right_asn = str(asn).split(".")
asn_ok = (0 <= int(left_asn) <= 65535) and \
(0 <= int(right_asn) <= 65535)
if not (0 <= int(left_asn) <= 65535):
asn_ok = False
err_mess = ("Left side of as.dot not in range ('{0}' given)"
"".format(left_asn))
elif not (0 <= int(right_asn) <= 65535):
asn_ok = False
err_mess = ("Right side of as.dot not in range ('{0}' given)"
"".format(right_asn))
else:
asn_ok = 0 <= int(asn) <= 4294967295
except ValueError:
asn_ok = False
if not (0 <= int(asn) <= 4294967295):
asn_ok = False
err_mess = ("Asplain number not in range ('{0}' given)"
"".format(asn))
except ValueError as exc:
err = TypeError("AS Number incompatible. " + str(exc).capitalize())
return err if return_error else False

return asn_ok
if err_mess:
err = ValueError("AS Number is invalid. " + err_mess)

return err if return_error else asn_ok

def validate_cidr(cidr):

def validate_cidr(cidr, return_error=False):
"""
Validate cidr is in correct CIDR notation
:param cidr: IP addr and associated routing prefix
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if valid IP, False if invalid
"""
err = None

try:
netaddr.IPNetwork(cidr)
return True
except (AddrFormatError, ValueError):
except (AddrFormatError, ValueError) as exc:
# Some versions of Netaddr have a bug causing them to return a
# ValueError rather than an AddrFormatError, so catch both.
return False
err = ValueError("CIDR is invalid. " + str(exc).capitalize())
return err if return_error else False

return None if return_error else True

def validate_cidr_versions(cidrs, ip_version=None):
def validate_cidr_versions(cidrs, ip_version=None, return_error=False):
"""
Validate CIDR versions match each other and (if specified) the given IP
version.
:param cidrs: List of CIDRs whose versions need verification
:param ip_version: Expected IP version that CIDRs should use (4, 6, None)
If None, CIDRs should all have same IP version
:param return_error: When True, any errors produced during validation will
be returned and None if there are no errors
:return: Boolean: True if versions match each other and ip_version,
False otherwise
"""
Expand All @@ -290,25 +351,61 @@ def validate_cidr_versions(cidrs, ip_version=None):
if ip_version is None:
ip_version = network.version
elif ip_version != network.version:
return False
except (AddrFormatError, ValueError):
err = ValueError("IP Version does not match CIDR.")
return err if return_error else False
except (AddrFormatError, ValueError) as exc:
# Some versions of Netaddr have a bug causing them to return a
# ValueError rather than an AddrFormatError, so catch both.
return False
return True
err = ValueError("CIDR is invalid. " + str(exc).capitalize())
return err if return_error else False

return None if return_error else True

def validate_ip(ip_addr, version):

def validate_ip(ip_addr, version=None, return_error=False):
"""
Validate that ip_addr is a valid IPv4 or IPv6 address
:param ip_addr: IP address to be validated
:param version: 4 or 6
:return: Boolean: True if valid, False if invalid.
"""
assert version in (4, 6)
if version:
try:
version_num = int(version)
except ValueError as exc:
err = TypeError("Version is invalid. Should be 4 or 6, but "
"'{0}' as given".format(version))
if return_error:
return err
else:
# NOTE: A raise makes more sense here, but keeping assert for
# backward compatability.
assert()

if version_num not in (4, 6):
err = ValueError("Version is invalid. Should be 4 or 6, but "
"'{0}' was given.".format(version))
if return_error:
return err
else:
assert()

if version == 4:
return netaddr.valid_ipv4(ip_addr)
if version == 6:
return netaddr.valid_ipv6(ip_addr)
if not netaddr.valid_ipv4(ip_addr):
err = ValueError("'{0}' is not a valid IPv4 address"
"".format(ip_addr))
return err if return_error else False
elif version == 6:
if not netaddr.valid_ipv6(ip_addr):
err = ValueError("'{0}' is not a valid IPv6 address"
"".format(ip_addr))
return err if return_error else False
else:
try:
IPAddress(ip_addr)
except AddrFormatError:
err = ValueError("'{0}' is not a valid IP address")
return err if return_error else False

return None if return_error else True
Loading

0 comments on commit 17ca5b2

Please sign in to comment.