diff --git a/swift/common/utils/__init__.py b/swift/common/utils/__init__.py index 200fff1db2..7e4ffda80b 100644 --- a/swift/common/utils/__init__.py +++ b/swift/common/utils/__init__.py @@ -65,7 +65,6 @@ from eventlet.event import Event from eventlet.green import socket, threading import eventlet.hubs import eventlet.queue -import netifaces import codecs utf8_decoder = codecs.getdecoder('utf-8') utf8_encoder = codecs.getencoder('utf-8') @@ -118,7 +117,14 @@ from swift.common.utils.timestamp import ( # noqa last_modified_date_to_timestamp, normalize_delete_at_timestamp, ) - +from swift.common.utils.ipaddrs import ( # noqa + is_valid_ip, + is_valid_ipv4, + is_valid_ipv6, + expand_ipv6, + parse_socket_string, + whataremyips, +) from logging.handlers import SysLogHandler import logging @@ -134,9 +140,6 @@ SWIFT_CONF_FILE = '/etc/swift/swift.conf' O_TMPFILE = getattr(os, 'O_TMPFILE', 0o20000000 | os.O_DIRECTORY) -# Used by the parse_socket_string() function to validate IPv6 addresses -IPV6_RE = re.compile(r"^\[(?P
.*)\](:(?P[0-9]+))?$") - MD5_OF_EMPTY_STRING = 'd41d8cd98f00b204e9800998ecf8427e' RESERVED_BYTE = b'\x00' RESERVED_STR = u'\x00' @@ -2051,125 +2054,6 @@ def parse_options(parser=None, once=False, test_args=None): return config, options -def is_valid_ip(ip): - """ - Return True if the provided ip is a valid IP-address - """ - return is_valid_ipv4(ip) or is_valid_ipv6(ip) - - -def is_valid_ipv4(ip): - """ - Return True if the provided ip is a valid IPv4-address - """ - try: - socket.inet_pton(socket.AF_INET, ip) - except socket.error: # not a valid IPv4 address - return False - return True - - -def is_valid_ipv6(ip): - """ - Returns True if the provided ip is a valid IPv6-address - """ - try: - socket.inet_pton(socket.AF_INET6, ip) - except socket.error: # not a valid IPv6 address - return False - return True - - -def expand_ipv6(address): - """ - Expand ipv6 address. - :param address: a string indicating valid ipv6 address - :returns: a string indicating fully expanded ipv6 address - - """ - packed_ip = socket.inet_pton(socket.AF_INET6, address) - return socket.inet_ntop(socket.AF_INET6, packed_ip) - - -def whataremyips(ring_ip=None): - """ - Get "our" IP addresses ("us" being the set of services configured by - one `*.conf` file). If our REST listens on a specific address, return it. - Otherwise, if listen on '0.0.0.0' or '::' return all addresses, including - the loopback. - - :param str ring_ip: Optional ring_ip/bind_ip from a config file; may be - IP address or hostname. - :returns: list of Strings of ip addresses - """ - if ring_ip: - # See if bind_ip is '0.0.0.0'/'::' - try: - _, _, _, _, sockaddr = socket.getaddrinfo( - ring_ip, None, 0, socket.SOCK_STREAM, 0, - socket.AI_NUMERICHOST)[0] - if sockaddr[0] not in ('0.0.0.0', '::'): - return [ring_ip] - except socket.gaierror: - pass - - addresses = [] - for interface in netifaces.interfaces(): - try: - iface_data = netifaces.ifaddresses(interface) - for family in iface_data: - if family not in (netifaces.AF_INET, netifaces.AF_INET6): - continue - for address in iface_data[family]: - addr = address['addr'] - - # If we have an ipv6 address remove the - # %ether_interface at the end - if family == netifaces.AF_INET6: - addr = expand_ipv6(addr.split('%')[0]) - addresses.append(addr) - except ValueError: - pass - return addresses - - -def parse_socket_string(socket_string, default_port): - """ - Given a string representing a socket, returns a tuple of (host, port). - Valid strings are DNS names, IPv4 addresses, or IPv6 addresses, with an - optional port. If an IPv6 address is specified it **must** be enclosed in - [], like *[::1]* or *[::1]:11211*. This follows the accepted prescription - for `IPv6 host literals`_. - - Examples:: - - server.org - server.org:1337 - 127.0.0.1:1337 - [::1]:1337 - [::1] - - .. _IPv6 host literals: https://tools.ietf.org/html/rfc3986#section-3.2.2 - """ - port = default_port - # IPv6 addresses must be between '[]' - if socket_string.startswith('['): - match = IPV6_RE.match(socket_string) - if not match: - raise ValueError("Invalid IPv6 address: %s" % socket_string) - host = match.group('address') - port = match.group('port') or port - else: - if ':' in socket_string: - tokens = socket_string.split(':') - if len(tokens) > 2: - raise ValueError("IPv6 addresses must be between '[]'") - host, port = tokens - else: - host = socket_string - return (host, port) - - def select_ip_port(node_dict, use_replication=False): """ Get the ip address and port that should be used for the given diff --git a/swift/common/utils/ipaddrs.py b/swift/common/utils/ipaddrs.py new file mode 100644 index 0000000000..8375a0a1f4 --- /dev/null +++ b/swift/common/utils/ipaddrs.py @@ -0,0 +1,141 @@ +# Copyright (c) 2010-2012 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import netifaces +import re +import socket + + +# Used by the parse_socket_string() function to validate IPv6 addresses +IPV6_RE = re.compile(r"^\[(?P
.*)\](:(?P[0-9]+))?$") + + +def is_valid_ip(ip): + """ + Return True if the provided ip is a valid IP-address + """ + return is_valid_ipv4(ip) or is_valid_ipv6(ip) + + +def is_valid_ipv4(ip): + """ + Return True if the provided ip is a valid IPv4-address + """ + try: + socket.inet_pton(socket.AF_INET, ip) + except socket.error: # not a valid IPv4 address + return False + return True + + +def is_valid_ipv6(ip): + """ + Returns True if the provided ip is a valid IPv6-address + """ + try: + socket.inet_pton(socket.AF_INET6, ip) + except socket.error: # not a valid IPv6 address + return False + return True + + +def expand_ipv6(address): + """ + Expand ipv6 address. + :param address: a string indicating valid ipv6 address + :returns: a string indicating fully expanded ipv6 address + + """ + packed_ip = socket.inet_pton(socket.AF_INET6, address) + return socket.inet_ntop(socket.AF_INET6, packed_ip) + + +def whataremyips(ring_ip=None): + """ + Get "our" IP addresses ("us" being the set of services configured by + one `*.conf` file). If our REST listens on a specific address, return it. + Otherwise, if listen on '0.0.0.0' or '::' return all addresses, including + the loopback. + + :param str ring_ip: Optional ring_ip/bind_ip from a config file; may be + IP address or hostname. + :returns: list of Strings of ip addresses + """ + if ring_ip: + # See if bind_ip is '0.0.0.0'/'::' + try: + _, _, _, _, sockaddr = socket.getaddrinfo( + ring_ip, None, 0, socket.SOCK_STREAM, 0, + socket.AI_NUMERICHOST)[0] + if sockaddr[0] not in ('0.0.0.0', '::'): + return [ring_ip] + except socket.gaierror: + pass + + addresses = [] + for interface in netifaces.interfaces(): + try: + iface_data = netifaces.ifaddresses(interface) + for family in iface_data: + if family not in (netifaces.AF_INET, netifaces.AF_INET6): + continue + for address in iface_data[family]: + addr = address['addr'] + + # If we have an ipv6 address remove the + # %ether_interface at the end + if family == netifaces.AF_INET6: + addr = expand_ipv6(addr.split('%')[0]) + addresses.append(addr) + except ValueError: + pass + return addresses + + +def parse_socket_string(socket_string, default_port): + """ + Given a string representing a socket, returns a tuple of (host, port). + Valid strings are DNS names, IPv4 addresses, or IPv6 addresses, with an + optional port. If an IPv6 address is specified it **must** be enclosed in + [], like *[::1]* or *[::1]:11211*. This follows the accepted prescription + for `IPv6 host literals`_. + + Examples:: + + server.org + server.org:1337 + 127.0.0.1:1337 + [::1]:1337 + [::1] + + .. _IPv6 host literals: https://tools.ietf.org/html/rfc3986#section-3.2.2 + """ + port = default_port + # IPv6 addresses must be between '[]' + if socket_string.startswith('['): + match = IPV6_RE.match(socket_string) + if not match: + raise ValueError("Invalid IPv6 address: %s" % socket_string) + host = match.group('address') + port = match.group('port') or port + else: + if ':' in socket_string: + tokens = socket_string.split(':') + if len(tokens) > 2: + raise ValueError("IPv6 addresses must be between '[]'") + host, port = tokens + else: + host = socket_string + return (host, port) diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py index d06bf5c967..9fee92bb91 100644 --- a/test/unit/common/test_utils.py +++ b/test/unit/common/test_utils.py @@ -65,7 +65,6 @@ from io import BytesIO from shutil import rmtree from functools import partial from tempfile import TemporaryFile, NamedTemporaryFile, mkdtemp -from netifaces import AF_INET6 from mock import MagicMock, patch from six.moves.configparser import NoSectionError, NoOptionError from uuid import uuid4 @@ -74,8 +73,7 @@ from swift.common.exceptions import Timeout, MessageTimeout, \ ConnectionTimeout, LockTimeout, ReplicationLockTimeout, \ MimeInvalid from swift.common import utils -from swift.common.utils import is_valid_ip, is_valid_ipv4, is_valid_ipv6, \ - set_swift_dir, md5, ShardRangeList +from swift.common.utils import set_swift_dir, md5, ShardRangeList from swift.common.container_sync_realms import ContainerSyncRealms from swift.common.header_key_dict import HeaderKeyDict from swift.common.storage_policy import POLICIES, reload_storage_policies @@ -1511,132 +1509,6 @@ class TestUtils(unittest.TestCase): self.assertEqual(utils.node_to_string(dev, replication=True), '[fe80::0204:61ff:ff9d:1234]:6400/sdb') - def test_is_valid_ip(self): - self.assertTrue(is_valid_ip("127.0.0.1")) - self.assertTrue(is_valid_ip("10.0.0.1")) - ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "fe80::204:61ff:fe9d:f156" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "fe80::204:61ff:254.157.241.86" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "fe80::" - self.assertTrue(is_valid_ip(ipv6)) - ipv6 = "::1" - self.assertTrue(is_valid_ip(ipv6)) - not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a" - self.assertFalse(is_valid_ip(not_ipv6)) - not_ipv6 = "1:2:3:4:5:6::7:8" - self.assertFalse(is_valid_ip(not_ipv6)) - - def test_is_valid_ipv4(self): - self.assertTrue(is_valid_ipv4("127.0.0.1")) - self.assertTrue(is_valid_ipv4("10.0.0.1")) - ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "fe80::204:61ff:fe9d:f156" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "fe80::204:61ff:254.157.241.86" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "fe80::" - self.assertFalse(is_valid_ipv4(ipv6)) - ipv6 = "::1" - self.assertFalse(is_valid_ipv4(ipv6)) - not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a" - self.assertFalse(is_valid_ipv4(not_ipv6)) - not_ipv6 = "1:2:3:4:5:6::7:8" - self.assertFalse(is_valid_ipv4(not_ipv6)) - - def test_is_valid_ipv6(self): - self.assertFalse(is_valid_ipv6("127.0.0.1")) - self.assertFalse(is_valid_ipv6("10.0.0.1")) - ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "fe80::204:61ff:fe9d:f156" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "fe80::204:61ff:254.157.241.86" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "fe80::" - self.assertTrue(is_valid_ipv6(ipv6)) - ipv6 = "::1" - self.assertTrue(is_valid_ipv6(ipv6)) - not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a" - self.assertFalse(is_valid_ipv6(not_ipv6)) - not_ipv6 = "1:2:3:4:5:6::7:8" - self.assertFalse(is_valid_ipv6(not_ipv6)) - - def test_expand_ipv6(self): - expanded_ipv6 = "fe80::204:61ff:fe9d:f156" - upper_ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" - self.assertEqual(expanded_ipv6, utils.expand_ipv6(upper_ipv6)) - omit_ipv6 = "fe80:0000:0000::0204:61ff:fe9d:f156" - self.assertEqual(expanded_ipv6, utils.expand_ipv6(omit_ipv6)) - less_num_ipv6 = "fe80:0:00:000:0204:61ff:fe9d:f156" - self.assertEqual(expanded_ipv6, utils.expand_ipv6(less_num_ipv6)) - - def test_whataremyips(self): - myips = utils.whataremyips() - self.assertTrue(len(myips) > 1) - self.assertTrue('127.0.0.1' in myips) - - def test_whataremyips_bind_to_all(self): - for any_addr in ('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000', - '::0', '::0000', '::', - # Wacky parse-error input produces all IPs - 'I am a bear'): - myips = utils.whataremyips(any_addr) - self.assertTrue(len(myips) > 1) - self.assertTrue('127.0.0.1' in myips) - - def test_whataremyips_bind_ip_specific(self): - self.assertEqual(['1.2.3.4'], utils.whataremyips('1.2.3.4')) - - def test_whataremyips_error(self): - def my_interfaces(): - return ['eth0'] - - def my_ifaddress_error(interface): - raise ValueError - - with patch('netifaces.interfaces', my_interfaces), \ - patch('netifaces.ifaddresses', my_ifaddress_error): - self.assertEqual(utils.whataremyips(), []) - - def test_whataremyips_ipv6(self): - test_ipv6_address = '2001:6b0:dead:beef:2::32' - test_interface = 'eth0' - - def my_ipv6_interfaces(): - return ['eth0'] - - def my_ipv6_ifaddresses(interface): - return {AF_INET6: - [{'netmask': 'ffff:ffff:ffff:ffff::', - 'addr': '%s%%%s' % (test_ipv6_address, test_interface)}]} - with patch('netifaces.interfaces', my_ipv6_interfaces), \ - patch('netifaces.ifaddresses', my_ipv6_ifaddresses): - myips = utils.whataremyips() - self.assertEqual(len(myips), 1) - self.assertEqual(myips[0], test_ipv6_address) - def test_hash_path(self): # Yes, these tests are deliberately very fragile. We want to make sure # that if someones changes the results hash_path produces, they know it diff --git a/test/unit/common/utils/test_ipaddrs.py b/test/unit/common/utils/test_ipaddrs.py new file mode 100644 index 0000000000..3d49c595bf --- /dev/null +++ b/test/unit/common/utils/test_ipaddrs.py @@ -0,0 +1,162 @@ +# Copyright (c) 2010-2012 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from mock import patch +import socket +import unittest + +# Continue importing from utils, as 3rd parties may depend on those imports +from swift.common import utils +from swift.common.utils import ipaddrs as utils_ipaddrs + + +class TestIsValidIP(unittest.TestCase): + def test_is_valid_ip(self): + self.assertTrue(utils.is_valid_ip("127.0.0.1")) + self.assertTrue(utils.is_valid_ip("10.0.0.1")) + ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "fe80::204:61ff:fe9d:f156" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "fe80::204:61ff:254.157.241.86" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "fe80::" + self.assertTrue(utils.is_valid_ip(ipv6)) + ipv6 = "::1" + self.assertTrue(utils.is_valid_ip(ipv6)) + not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a" + self.assertFalse(utils.is_valid_ip(not_ipv6)) + not_ipv6 = "1:2:3:4:5:6::7:8" + self.assertFalse(utils.is_valid_ip(not_ipv6)) + + def test_is_valid_ipv4(self): + self.assertTrue(utils.is_valid_ipv4("127.0.0.1")) + self.assertTrue(utils.is_valid_ipv4("10.0.0.1")) + ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "fe80::204:61ff:fe9d:f156" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "fe80::204:61ff:254.157.241.86" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "fe80::" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + ipv6 = "::1" + self.assertFalse(utils.is_valid_ipv4(ipv6)) + not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a" + self.assertFalse(utils.is_valid_ipv4(not_ipv6)) + not_ipv6 = "1:2:3:4:5:6::7:8" + self.assertFalse(utils.is_valid_ipv4(not_ipv6)) + + def test_is_valid_ipv6(self): + self.assertFalse(utils.is_valid_ipv6("127.0.0.1")) + self.assertFalse(utils.is_valid_ipv6("10.0.0.1")) + ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "fe80::204:61ff:fe9d:f156" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "fe80::204:61ff:254.157.241.86" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "fe80::" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + ipv6 = "::1" + self.assertTrue(utils.is_valid_ipv6(ipv6)) + not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a" + self.assertFalse(utils.is_valid_ipv6(not_ipv6)) + not_ipv6 = "1:2:3:4:5:6::7:8" + self.assertFalse(utils.is_valid_ipv6(not_ipv6)) + + +class TestExpandIPv6(unittest.TestCase): + def test_expand_ipv6(self): + expanded_ipv6 = "fe80::204:61ff:fe9d:f156" + upper_ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156" + self.assertEqual(expanded_ipv6, utils.expand_ipv6(upper_ipv6)) + omit_ipv6 = "fe80:0000:0000::0204:61ff:fe9d:f156" + self.assertEqual(expanded_ipv6, utils.expand_ipv6(omit_ipv6)) + less_num_ipv6 = "fe80:0:00:000:0204:61ff:fe9d:f156" + self.assertEqual(expanded_ipv6, utils.expand_ipv6(less_num_ipv6)) + + +class TestWhatAreMyIPs(unittest.TestCase): + def test_whataremyips(self): + myips = utils.whataremyips() + self.assertTrue(len(myips) > 1) + self.assertIn('127.0.0.1', myips) + + def test_whataremyips_bind_to_all(self): + for any_addr in ('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000', + '::0', '::0000', '::', + # Wacky parse-error input produces all IPs + 'I am a bear'): + myips = utils.whataremyips(any_addr) + self.assertTrue(len(myips) > 1) + self.assertIn('127.0.0.1', myips) + + def test_whataremyips_bind_ip_specific(self): + self.assertEqual(['1.2.3.4'], utils.whataremyips('1.2.3.4')) + + def test_whataremyips_netifaces_error(self): + class FakeNetifaces(object): + @staticmethod + def interfaces(): + return ['eth0'] + + @staticmethod + def ifaddresses(interface): + raise ValueError + + with patch.object(utils_ipaddrs, 'netifaces', FakeNetifaces): + self.assertEqual(utils.whataremyips(), []) + + def test_whataremyips_netifaces_ipv6(self): + test_ipv6_address = '2001:6b0:dead:beef:2::32' + test_interface = 'eth0' + + class FakeNetifaces(object): + AF_INET = int(socket.AF_INET) + AF_INET6 = int(socket.AF_INET6) + + @staticmethod + def interfaces(): + return ['eth0'] + + @staticmethod + def ifaddresses(interface): + return {int(socket.AF_INET6): [ + {'netmask': 'ffff:ffff:ffff:ffff::', + 'addr': '%s%%%s' % (test_ipv6_address, test_interface)}]} + + with patch.object(utils_ipaddrs, 'netifaces', FakeNetifaces): + myips = utils.whataremyips() + self.assertEqual(len(myips), 1) + self.assertEqual(myips[0], test_ipv6_address)