Merge "Fixing HostName and adding support for HostAddress"
This commit is contained in:
commit
bb8846a3ab
@ -16,6 +16,7 @@ Option Definitions
|
||||
.. autoclass:: IPOpt
|
||||
.. autoclass:: PortOpt
|
||||
.. autoclass:: HostnameOpt
|
||||
.. autoclass:: HostAddressOpt
|
||||
.. autoclass:: URIOpt
|
||||
.. autoclass:: DeprecatedOpt
|
||||
.. autoclass:: SubCommandOpt
|
||||
|
@ -59,6 +59,7 @@ Type Option
|
||||
:class:`oslo_config.types.Dict` :class:`oslo_config.cfg.DictOpt`
|
||||
:class:`oslo_config.types.IPAddress` :class:`oslo_config.cfg.IPOpt`
|
||||
:class:`oslo_config.types.Hostname` :class:`oslo_config.cfg.HostnameOpt`
|
||||
:class:`oslo_config.types.HostAddress`:class:`oslo_config.cfg.HostAddressOpt`
|
||||
:class:`oslo_config.types.URI` :class:`oslo_config.cfg.URIOpt`
|
||||
==================================== ======
|
||||
|
||||
@ -1391,6 +1392,20 @@ class HostnameOpt(Opt):
|
||||
**kwargs)
|
||||
|
||||
|
||||
class HostAddressOpt(Opt):
|
||||
"""Option for either an IP or a hostname.
|
||||
|
||||
Accepts valid hostnames and valid IP addresses.
|
||||
|
||||
.. versionadded:: 3.22
|
||||
"""
|
||||
|
||||
def __init__(self, name, version=None, **kwargs):
|
||||
super(HostAddressOpt, self).__init__(name,
|
||||
type=types.HostAddress(version),
|
||||
**kwargs)
|
||||
|
||||
|
||||
class URIOpt(Opt):
|
||||
|
||||
"""Opt with URI type
|
||||
|
@ -90,7 +90,8 @@ def _format_defaults(opt):
|
||||
elif opt.default is None:
|
||||
default_str = '<None>'
|
||||
elif (isinstance(opt, (cfg.StrOpt, cfg.IPOpt,
|
||||
cfg.HostnameOpt, cfg.URIOpt))):
|
||||
cfg.HostnameOpt, cfg.HostAddressOpt,
|
||||
cfg.URIOpt))):
|
||||
default_str = opt.default
|
||||
elif isinstance(opt, cfg.BoolOpt):
|
||||
default_str = str(opt.default).lower()
|
||||
|
@ -77,6 +77,7 @@ _TYPE_DESCRIPTIONS = {
|
||||
cfg.PortOpt: 'port number',
|
||||
cfg.HostnameOpt: 'hostname',
|
||||
cfg.URIOpt: 'URI',
|
||||
cfg.HostAddressOpt: 'host address',
|
||||
cfg._ConfigFileOpt: 'list of filenames',
|
||||
cfg._ConfigDirOpt: 'list of directory names',
|
||||
}
|
||||
|
@ -707,6 +707,32 @@ class IPv6AddressTypeTests(IPAddressTypeTests):
|
||||
self.assertInvalid('192.168.0.1')
|
||||
|
||||
|
||||
class HostAddressTypeTests(TypeTestHelper, unittest.TestCase):
|
||||
type = types.HostAddress()
|
||||
|
||||
def test_invalid_host_addresses(self):
|
||||
self.assertInvalid('-1')
|
||||
self.assertInvalid('_foo')
|
||||
self.assertInvalid('3.14')
|
||||
self.assertInvalid('10.0')
|
||||
self.assertInvalid('host..name')
|
||||
self.assertInvalid('org.10')
|
||||
self.assertInvalid('0.0.00')
|
||||
|
||||
def test_valid_host_addresses(self):
|
||||
self.assertConvertedValue('foo.bar', 'foo.bar')
|
||||
self.assertConvertedValue('192.168.0.1', '192.168.0.1')
|
||||
self.assertConvertedValue('abcd:ef::1', 'abcd:ef::1')
|
||||
self.assertConvertedValue('home-site-here.org.com',
|
||||
'home-site-here.org.com')
|
||||
self.assertConvertedValue('3com.com', '3com.com')
|
||||
self.assertConvertedValue('10.org', '10.org')
|
||||
self.assertConvertedValue('cell1.nova.site1', 'cell1.nova.site1')
|
||||
self.assertConvertedValue('ab-c.com', 'ab-c.com')
|
||||
self.assertConvertedValue('abc.com-org', 'abc.com-org')
|
||||
self.assertConvertedValue('abc.0-0', 'abc.0-0')
|
||||
|
||||
|
||||
class HostnameTypeTests(TypeTestHelper, unittest.TestCase):
|
||||
type = types.Hostname()
|
||||
|
||||
@ -746,6 +772,12 @@ class HostnameTypeTests(TypeTestHelper, unittest.TestCase):
|
||||
self.assertInvalid(".host.name.com")
|
||||
self.assertInvalid("no spaces")
|
||||
|
||||
def test_invalid_hostnames_with_numeric_characters(self):
|
||||
self.assertInvalid("10.0.0.0")
|
||||
self.assertInvalid("3.14")
|
||||
self.assertInvalid("org.10")
|
||||
self.assertInvalid('0.0.00')
|
||||
|
||||
def test_no_start_end_hyphens(self):
|
||||
self.assertInvalid("-host.com")
|
||||
self.assertInvalid("-hostname.com-")
|
||||
@ -759,9 +791,13 @@ class HostnameTypeTests(TypeTestHelper, unittest.TestCase):
|
||||
self.assertConvertedEqual('cell1.nova.site1')
|
||||
self.assertConvertedEqual('site01001')
|
||||
self.assertConvertedEqual('home-site-here.org.com')
|
||||
self.assertConvertedEqual('192.168.0.1')
|
||||
self.assertConvertedEqual('1.1.1')
|
||||
self.assertConvertedEqual('localhost')
|
||||
self.assertConvertedEqual('3com.com')
|
||||
self.assertConvertedEqual('10.org')
|
||||
self.assertConvertedEqual('10ab.10ab')
|
||||
self.assertConvertedEqual('ab-c.com')
|
||||
self.assertConvertedEqual('abc.com-org')
|
||||
self.assertConvertedEqual('abc.0-0')
|
||||
|
||||
def test_max_segment_size(self):
|
||||
self.assertConvertedEqual('host.%s.com' % ('x' * 63))
|
||||
|
@ -704,7 +704,7 @@ class IPAddress(ConfigType):
|
||||
|
||||
|
||||
class Hostname(ConfigType):
|
||||
"""Hostname type.
|
||||
"""Host domain name type.
|
||||
|
||||
A hostname refers to a valid DNS or hostname. It must not be longer than
|
||||
253 characters, have a segment greater than 63 characters, nor start or
|
||||
@ -724,10 +724,14 @@ class Hostname(ConfigType):
|
||||
- Contains at least one character and a maximum of 63 characters
|
||||
- Consists only of allowed characters: letters (A-Z and a-z),
|
||||
digits (0-9), and hyphen (-)
|
||||
- Ensures that the final segment (representing the top level domain
|
||||
name) contains at least one non-numeric character
|
||||
- Does not begin or end with a hyphen
|
||||
- maximum total length of 253 characters
|
||||
|
||||
For more details , please see: http://tools.ietf.org/html/rfc1035
|
||||
For more details , please see: http://tools.ietf.org/html/rfc1035,
|
||||
https://www.ietf.org/rfc/rfc1912, and
|
||||
https://tools.ietf.org/html/rfc1123
|
||||
"""
|
||||
|
||||
if len(value) == 0:
|
||||
@ -738,6 +742,10 @@ class Hostname(ConfigType):
|
||||
if value.endswith("."):
|
||||
value = value[:-1]
|
||||
allowed = re.compile("(?!-)[A-Z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||
if not re.search('[a-zA-Z-]', value.split(".")[-1]):
|
||||
raise ValueError('%s contains no non-numeric characters in the '
|
||||
'top-level domain part of the host name and is '
|
||||
'invalid' % value)
|
||||
if any((not allowed.match(x)) for x in value.split(".")):
|
||||
raise ValueError("%s is an invalid hostname" % value)
|
||||
return value
|
||||
@ -752,6 +760,55 @@ class Hostname(ConfigType):
|
||||
return value
|
||||
|
||||
|
||||
class HostAddress(object):
|
||||
"""Host Address type.
|
||||
|
||||
Represents both valid IP addresses and valid host domain names
|
||||
including fully qualified domain names.
|
||||
Performs strict checks for both IP addresses and valid hostnames,
|
||||
matching the opt values to the respective types as per RFC1912.
|
||||
|
||||
:param version: defines which version should be explicitly
|
||||
checked (4 or 6) in case of an IP address
|
||||
:param type_name: Type name to be used in the sample config file.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, version=None, type_name='host address value'):
|
||||
"""Check for valid version in case an IP address is provided
|
||||
|
||||
"""
|
||||
|
||||
self.ip_address = IPAddress(version, type_name)
|
||||
self.hostname = Hostname('localhost')
|
||||
|
||||
def __call__(self, value):
|
||||
"""Checks if is a valid IP/hostname.
|
||||
|
||||
If not a valid IP, makes sure it is not a mistyped IP before
|
||||
performing checks for it as a hostname.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
value = self.ip_address(value)
|
||||
except ValueError:
|
||||
try:
|
||||
value = self.hostname(value)
|
||||
except ValueError:
|
||||
raise ValueError("%s is not a valid host address", value)
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
return 'HostAddress'
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__class__ == other.__class__
|
||||
|
||||
def _formatter(self, value):
|
||||
return value
|
||||
|
||||
|
||||
class URI(ConfigType):
|
||||
|
||||
"""URI type
|
||||
|
13
releasenotes/notes/add-HostAddressOpt-6e7e2afe7c7863cb.yaml
Normal file
13
releasenotes/notes/add-HostAddressOpt-6e7e2afe7c7863cb.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
prelude: >
|
||||
Configuration option type of ``HostAddressOpt`` added to accept and
|
||||
validate both IP addresses and hostnames. Please refer to the
|
||||
``features`` section for more information.
|
||||
|
||||
features:
|
||||
- Configuration option type of ``HostAddressOpt`` added to accept both
|
||||
valid IP address (IPv4 and IPv6) values as well as hostnames.
|
||||
The ``HostAddressOpt`` will accept both IPv4 and IPv6 addresses
|
||||
and ensure that strict checks are performed on the IP versions.
|
||||
This option type will also accept and accurately validate hostnames
|
||||
ensuring that no invalid IP passes as a valid hostname.
|
Loading…
x
Reference in New Issue
Block a user