summaryrefslogtreecommitdiff
path: root/neutron/api/v2/attributes.py
diff options
context:
space:
mode:
Diffstat (limited to 'neutron/api/v2/attributes.py')
-rw-r--r--neutron/api/v2/attributes.py73
1 files changed, 58 insertions, 15 deletions
diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py
index 21486dbd39..85e9b80cf1 100644
--- a/neutron/api/v2/attributes.py
+++ b/neutron/api/v2/attributes.py
@@ -235,27 +235,38 @@ def _validate_fixed_ips(data, valid_values=None):
return msg
+def _validate_ip_or_hostname(host):
+ ip_err = _validate_ip_address(host)
+ if not ip_err:
+ return
+ name_err = _validate_hostname(host)
+ if not name_err:
+ return
+ msg = _("%(host)s is not a valid IP or hostname. Details: "
+ "%(ip_err)s, %(name_err)s") % {'ip_err': ip_err, 'host': host,
+ 'name_err': name_err}
+ return msg
+
+
def _validate_nameservers(data, valid_values=None):
if not hasattr(data, '__iter__'):
msg = _("Invalid data format for nameserver: '%s'") % data
LOG.debug(msg)
return msg
- ips = []
- for ip in data:
- msg = _validate_ip_address(ip)
+ hosts = []
+ for host in data:
+ # This may be an IP or a hostname
+ msg = _validate_ip_or_hostname(host)
if msg:
- # This may be a hostname
- msg = _validate_regex(ip, HOSTNAME_PATTERN)
- if msg:
- msg = _("'%s' is not a valid nameserver") % ip
- LOG.debug(msg)
- return msg
- if ip in ips:
- msg = _("Duplicate nameserver '%s'") % ip
+ msg = _("'%(host)s' is not a valid nameserver. %(msg)s") % {
+ 'host': host, 'msg': msg}
+ return msg
+ if host in hosts:
+ msg = _("Duplicate nameserver '%s'") % host
LOG.debug(msg)
return msg
- ips.append(ip)
+ hosts.append(host)
def _validate_hostroutes(data, valid_values=None):
@@ -332,6 +343,41 @@ def _validate_subnet_or_none(data, valid_values=None):
return _validate_subnet(data, valid_values)
+def _validate_hostname(data):
+ # NOTE: An individual name regex instead of an entire FQDN was used
+ # because its easier to make correct. Feel free to replace with a
+ # full regex solution. The logic should validate that the hostname
+ # matches RFC 1123 (section 2.1) and RFC 952.
+ hostname_pattern = "[a-zA-Z0-9-]{1,63}$"
+ try:
+ # Trailing periods are allowed to indicate that a name is fully
+ # qualified per RFC 1034 (page 7).
+ trimmed = data if data[-1] != '.' else data[:-1]
+ if len(trimmed) > 255:
+ raise TypeError(
+ _("'%s' exceeds the 255 character hostname limit") % trimmed)
+ names = trimmed.split('.')
+ for name in names:
+ if not name:
+ raise TypeError(_("Encountered an empty component."))
+ if name[-1] == '-' or name[0] == '-':
+ raise TypeError(
+ _("Name '%s' must not start or end with a hyphen.") % name)
+ if not re.match(hostname_pattern, name):
+ raise TypeError(
+ _("Name '%s' must be 1-63 characters long, each of "
+ "which can only be alphanumeric or a hyphen.") % name)
+ # RFC 1123 hints that a TLD can't be all numeric. last is a TLD if
+ # it's an FQDN.
+ if len(names) > 1 and re.match("^[0-9]+$", names[-1]):
+ raise TypeError(_("TLD '%s' must not be all numeric") % names[-1])
+ except TypeError as e:
+ msg = _("'%(data)s' is not a valid hostname. Reason: %(reason)s") % {
+ 'data': data, 'reason': e.message}
+ LOG.debug(msg)
+ return msg
+
+
def _validate_regex(data, valid_values=None):
try:
if re.match(valid_values, data):
@@ -540,9 +586,6 @@ def convert_to_list(data):
return [data]
-HOSTNAME_PATTERN = ("(?=^.{1,254}$)(^(?:(?!\d+.|-)[a-zA-Z0-9_\-]{1,62}"
- "[a-zA-Z0-9]\.?)+(?:[a-zA-Z]{2,})$)")
-
HEX_ELEM = '[0-9A-Fa-f]'
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
HEX_ELEM + '{4}', HEX_ELEM + '{4}',