Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP1:GA
salt.3776
0009-Rewrite-minion-ID-generator-bsc-967803.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0009-Rewrite-minion-ID-generator-bsc-967803.patch of Package salt.3776
From e8fc3e770aba4c4bba46e559146ab198cd271f54 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk <bo@suse.de> Date: Thu, 2 Jun 2016 17:24:17 +0200 Subject: [PATCH 09/38] Rewrite minion ID generator (bsc#967803) * Remove hostname sorting * Keep order on deduplication * Fix logical expression * Rewrite generate_minion_id function * Use api of the distinct list * Use FQDN at last, as it may differ * Rename network unit test into a proper name * Fix network test for Solaris * Get hostname as an attribute of a raw socket. * Make generate_minioin_id more testable. * Add unit test for check if the minion ID is distinct in the pool * Add unit test to check on duplicate hostnames in the pool * Add test for the first in line from platform.node is used (mostly) * Add unit test to filter out localhost from the first occurrence * Add unit test to check if any of localhost or local IP addresses are filtered * Add unit test check for non-localhost IP is accepted as a minion ID * Fix the documentation * Fix lint * Remove unnecessary tests: no fallback to what was already looked at * Return 'localhost' if no other name has been found. * Check raw socket attribute only if other socket checks had failed. * Test fix: look for attribute name only when other socket checks had failed * Test fix: only non-localhost IP should persist * Add unit test: minion ID should be localhost, if everything is localhost. * Add unit test: check if FQDN is picked up * Add unit test: addrinfo from the raw socket should be picked up if all other socket checks had failed to localhost * Do not use cached localhost name. * Fix lint: unused import * Regression fix: minion ID generator should use FQDN first, if available (#34876) * Regression fix: use FQDN first, if available * Adjust the tests to the new behaviour (FQDN first) --- salt/config.py | 2 +- salt/utils/network.py | 260 ++++++++---------------------- tests/unit/config_test.py | 61 ++++---- tests/unit/utils/network.py | 193 ----------------------- tests/unit/utils/network_test.py | 331 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 424 insertions(+), 423 deletions(-) delete mode 100644 tests/unit/utils/network.py create mode 100644 tests/unit/utils/network_test.py diff --git a/salt/config.py b/salt/config.py index 968ceeb..9ad1d6a 100644 --- a/salt/config.py +++ b/salt/config.py @@ -2816,7 +2816,7 @@ def get_id(opts, cache_minion_id=False): bname = salt.utils.to_bytes(name) if bname.startswith(codecs.BOM): # Remove BOM if exists name = salt.utils.to_str(bname.replace(codecs.BOM, '', 1)) - if name: + if name and name != 'localhost': log.debug('Using cached minion ID from {0}: {1}'.format(id_cache, name)) return name, False except (IOError, OSError): diff --git a/salt/utils/network.py b/salt/utils/network.py index fb89e9c..dfda364 100644 --- a/salt/utils/network.py +++ b/salt/utils/network.py @@ -10,6 +10,7 @@ import re import shlex import socket import logging +import platform from string import ascii_letters, digits # Import 3rd-party libs @@ -72,207 +73,78 @@ def host_to_ip(host): return ip -def _filter_localhost_names(name_list): - ''' - Returns list without local hostnames and ip addresses. - ''' - h = [] - re_filters = [ - 'localhost.*', - 'ip6-.*', - '127.*', - r'0\.0\.0\.0', - '::1.*', - 'fe00::.*', - 'fe02::.*', - '1.0.0.*.ip6.arpa', - ] - for name in name_list: - filtered = False - for f in re_filters: - if re.match(f, name): - filtered = True - break - if not filtered: - h.append(name) - return h - - -def _sort_hostnames(hostname_list): - ''' - sort minion ids favoring in order of: - - FQDN - - public ipaddress - - localhost alias - - private ipaddress - ''' - # punish matches in order of preference - punish = [ - 'localhost.localdomain', - 'localhost.my.domain', - 'localhost4.localdomain4', - 'localhost', - 'ip6-localhost', - 'ip6-loopback', - 'ipv6-localhost', - 'ipv6-loopback', - '127.0.2.1', - '127.0.1.1', - '127.0.0.1', - '0.0.0.0', - '::1', - 'fe00::', - 'fe02::', - ] - - def _key_hostname(e): - # should never have a space in hostname - # favor hostnames w/o spaces - if ' ' in e: - first = 1 - else: - first = -1 - - # punish localhost list - if e in punish: - second = punish.index(e) - else: - second = -1 - - # punish ipv6 - third = e.count(':') - - # punish ipv4 - # punish ipv4 addresses that start with '127.' more - e_is_ipv4 = e.count('.') == 3 and not any(c.isalpha() for c in e) - if e_is_ipv4: - if e.startswith('127.'): - fourth = 1 - else: - fourth = 0 - else: - fourth = -1 - - # favor hosts with more dots - fifth = -(e.count('.')) - - # favor longest fqdn - sixth = -(len(e)) - - return (first, second, third, fourth, fifth, sixth) - - return sorted(hostname_list, key=_key_hostname) - - -def get_hostnames(): - ''' - Get list of hostnames using multiple strategies +def _generate_minion_id(): ''' - h = [] - h.append(socket.gethostname()) - h.append(socket.getfqdn()) + Get list of possible host names and convention names. - # try socket.getaddrinfo - try: - addrinfo = socket.getaddrinfo( - socket.gethostname(), 0, socket.AF_UNSPEC, socket.SOCK_STREAM, - socket.SOL_TCP, socket.AI_CANONNAME - ) - for info in addrinfo: - # info struct [family, socktype, proto, canonname, sockaddr] - if len(info) >= 4: - h.append(info[3]) - except socket.gaierror: - pass - - # try /etc/hostname - try: - name = '' - with salt.utils.fopen('/etc/hostname') as hfl: - name = hfl.read() - h.append(name) - except (IOError, OSError): - pass + :return: + ''' + # There are three types of hostnames: + # 1. Network names. How host is accessed from the network. + # 2. Host aliases. They might be not available in all the network or only locally (/etc/hosts) + # 3. Convention names, an internal nodename. - # try /etc/nodename (SunOS only) - if salt.utils.is_sunos(): + class DistinctList(list): + ''' + List, which allows to append only distinct objects. + Needs to work on Python 2.6, because of collections.OrderedDict only since 2.7 version. + Override 'filter()' for custom filtering. + ''' + localhost_matchers = ['localhost.*', 'ip6-.*', '127.*', r'0\.0\.0\.0', + '::1.*', 'ipv6-.*', 'fe00::.*', 'fe02::.*', '1.0.0.*.ip6.arpa'] + + def append(self, p_object): + if p_object and p_object not in self and not self.filter(p_object): + super(self.__class__, self).append(p_object) + return self + + def extend(self, iterable): + for obj in iterable: + self.append(obj) + return self + + def filter(self, element): + 'Returns True if element needs to be filtered' + for rgx in self.localhost_matchers: + if re.match(rgx, element): + return True + + def first(self): + return self and self[0] or None + + hosts = DistinctList().append(socket.getfqdn()).append(platform.node()).append(socket.gethostname()) + if not hosts: try: - name = '' - with salt.utils.fopen('/etc/nodename') as hfl: - name = hfl.read() - h.append(name) - except (IOError, OSError): - pass - - # try /etc/hosts - try: - with salt.utils.fopen('/etc/hosts') as hfl: - for line in hfl: - names = line.split() - try: - ip = names.pop(0) - except IndexError: - continue - if ip.startswith('127.') or ip == '::1': - for name in names: - h.append(name) - except (IOError, OSError): - pass + for a_nfo in socket.getaddrinfo(hosts.first(), None, socket.AF_INET, + socket.SOCK_RAW, socket.IPPROTO_IP, socket.AI_CANONNAME): + if len(a_nfo) > 3: + hosts.append(a_nfo[3]) + except socket.gaierror: + log.warn('Cannot resolve address {addr} info via socket: {message}'.format( + addr=hosts.first(), message=socket.gaierror) + ) + # Universal method for everywhere (Linux, Slowlaris, Windows etc) + for f_name in ['/etc/hostname', '/etc/nodename', '/etc/hosts', + r'{win}\system32\drivers\etc\hosts'.format(win=os.getenv('WINDIR'))]: + if not os.path.exists(f_name): + continue + with salt.utils.fopen(f_name) as f_hdl: + for hst in (line.strip().split('#')[0].strip().split() or None for line in f_hdl.read().split(os.linesep)): + if hst and (hst[0][:4] in ['127.', '::1'] or len(hst) == 1): + hosts.extend(hst) - # try windows hosts - if salt.utils.is_windows(): - try: - windir = os.getenv('WINDIR') - with salt.utils.fopen(windir + r'\system32\drivers\etc\hosts') as hfl: - for line in hfl: - # skip commented or blank lines - if line[0] == '#' or len(line) <= 1: - continue - # process lines looking for '127.' in first column - try: - entry = line.split() - if entry[0].startswith('127.'): - for name in entry[1:]: # try each name in the row - h.append(name) - except IndexError: - pass # could not split line (malformed entry?) - except (IOError, OSError): - pass - - # strip spaces and ignore empty strings - hosts = [] - for name in h: - name = name.strip() - if len(name) > 0: - hosts.append(name) - - # remove duplicates - hosts = list(set(hosts)) - return hosts + # include public and private ipaddresses + return hosts.extend([addr for addr in salt.utils.network.ip_addrs() + if not ipaddress.ip_address(addr).is_loopback]) def generate_minion_id(): ''' - Returns a minion id after checking multiple sources for a FQDN. - If no FQDN is found you may get an ip address - ''' - possible_ids = get_hostnames() + Return only first element of the hostname from all possible list. - # include public and private ipaddresses - for addr in salt.utils.network.ip_addrs(): - addr = ipaddress.ip_address(addr) - if addr.is_loopback: - continue - possible_ids.append(str(addr)) - - possible_ids = _filter_localhost_names(possible_ids) - - # if no minion id - if len(possible_ids) == 0: - return 'noname' - - hosts = _sort_hostnames(possible_ids) - return hosts[0] + :return: + ''' + return _generate_minion_id().first() or 'localhost' def get_socket(addr, type=socket.SOCK_STREAM, proto=0): @@ -309,11 +181,7 @@ def get_fqhostname(): except socket.gaierror: pass - l = _sort_hostnames(l) - if len(l) > 0: - return l[0] - - return None + return l and l[0] or None def ip_to_host(ip): diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index 9ba3819..ee5d202 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -13,7 +13,6 @@ import logging import os import shutil import tempfile -from contextlib import contextmanager # Import Salt Testing libs from salttesting import TestCase @@ -66,38 +65,6 @@ def _unhandled_mock_read(filename): raise CommandExecutionError('Unhandled mock read for {0}'.format(filename)) -@contextmanager -def _fopen_side_effect_etc_hostname(filename): - ''' - Mock reading from /etc/hostname - ''' - log.debug('Mock-reading {0}'.format(filename)) - if filename == '/etc/hostname': - mock_open = MagicMock() - mock_open.read.return_value = MOCK_ETC_HOSTNAME - yield mock_open - elif filename == '/etc/hosts': - raise IOError(2, "No such file or directory: '{0}'".format(filename)) - else: - _unhandled_mock_read(filename) - - -@contextmanager -def _fopen_side_effect_etc_hosts(filename): - ''' - Mock /etc/hostname not existing, and falling back to reading /etc/hosts - ''' - log.debug('Mock-reading {0}'.format(filename)) - if filename == '/etc/hostname': - raise IOError(2, "No such file or directory: '{0}'".format(filename)) - elif filename == '/etc/hosts': - mock_open = MagicMock() - mock_open.__iter__.return_value = MOCK_ETC_HOSTS.splitlines() - yield mock_open - else: - _unhandled_mock_read(filename) - - class ConfigTestCase(TestCase, integration.AdaptedConfigurationTestCaseMixIn): def test_proper_path_joining(self): fpath = tempfile.mktemp() @@ -373,6 +340,34 @@ class ConfigTestCase(TestCase, integration.AdaptedConfigurationTestCaseMixIn): self.assertEqual(syndic_opts['_master_conf_file'], minion_conf_path) self.assertEqual(syndic_opts['_minion_conf_file'], syndic_conf_path) + def test_issue_6714_parsing_errors_logged(self): + try: + tempdir = tempfile.mkdtemp(dir=integration.SYS_TMP_DIR) + test_config = os.path.join(tempdir, 'config') + + # Let's populate a master configuration file with some basic + # settings + salt.utils.fopen(test_config, 'w').write( + 'root_dir: {0}\n' + 'log_file: {0}/foo.log\n'.format(tempdir) + + '\n\n\n' + 'blah:false\n' + ) + + with TestsLoggingHandler() as handler: + # Let's load the configuration + config = sconfig.master_config(test_config) + for message in handler.messages: + if message.startswith('ERROR:Error parsing configuration'): + break + else: + raise AssertionError( + 'No parsing error message was logged' + ) + finally: + if os.path.isdir(tempdir): + shutil.rmtree(tempdir) + @patch('salt.utils.network.get_fqhostname', MagicMock(return_value='localhost')) def test_get_id_etc_hostname(self): ''' diff --git a/tests/unit/utils/network.py b/tests/unit/utils/network.py deleted file mode 100644 index 89db848..0000000 --- a/tests/unit/utils/network.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- -# Import Python libs -from __future__ import absolute_import - -# Import Salt Testing libs -from salttesting import skipIf -from salttesting import TestCase -from salttesting.helpers import ensure_in_syspath -from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch -ensure_in_syspath('../../') - -# Import salt libs -from salt.utils import network - -LINUX = '''\ -eth0 Link encap:Ethernet HWaddr e0:3f:49:85:6a:af - inet addr:10.10.10.56 Bcast:10.10.10.255 Mask:255.255.252.0 - inet6 addr: fe80::e23f:49ff:fe85:6aaf/64 Scope:Link - UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 - RX packets:643363 errors:0 dropped:0 overruns:0 frame:0 - TX packets:196539 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:386388355 (368.4 MiB) TX bytes:25600939 (24.4 MiB) - -lo Link encap:Local Loopback - inet addr:127.0.0.1 Mask:255.0.0.0 - inet6 addr: ::1/128 Scope:Host - UP LOOPBACK RUNNING MTU:65536 Metric:1 - RX packets:548901 errors:0 dropped:0 overruns:0 frame:0 - TX packets:548901 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:0 - RX bytes:613479895 (585.0 MiB) TX bytes:613479895 (585.0 MiB) -''' - -FREEBSD = ''' -em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 - options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO> - ether 00:30:48:ff:ff:ff - inet 10.10.10.250 netmask 0xffffffe0 broadcast 10.10.10.255 - inet 10.10.10.56 netmask 0xffffffc0 broadcast 10.10.10.63 - media: Ethernet autoselect (1000baseT <full-duplex>) - status: active -em1: flags=8c02<BROADCAST,OACTIVE,SIMPLEX,MULTICAST> metric 0 mtu 1500 - options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO> - ether 00:30:48:aa:aa:aa - media: Ethernet autoselect - status: no carrier -plip0: flags=8810<POINTOPOINT,SIMPLEX,MULTICAST> metric 0 mtu 1500 -lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384 - options=3<RXCSUM,TXCSUM> - inet6 fe80::1%lo0 prefixlen 64 scopeid 0x8 - inet6 ::1 prefixlen 128 - inet 127.0.0.1 netmask 0xff000000 - nd6 options=3<PERFORMNUD,ACCEPT_RTADV> -tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500 - options=80000<LINKSTATE> - inet 10.12.0.1 --> 10.12.0.2 netmask 0xffffffff - Opened by PID 1964 -''' - -SOLARIS = '''\ -lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1 - inet 127.0.0.1 netmask ff000000 -net0: flags=100001100943<UP,BROADCAST,RUNNING,PROMISC,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1500 index 2 - inet 10.10.10.38 netmask ffffffe0 broadcast 10.10.10.63 -ilbint0: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 3 - inet 10.6.0.11 netmask ffffff00 broadcast 10.6.0.255 -ilbext0: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 4 - inet 10.10.11.11 netmask ffffffe0 broadcast 10.10.11.31 -ilbext0:1: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 4 - inet 10.10.11.12 netmask ffffffe0 broadcast 10.10.11.31 -vpn0: flags=1000011008d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1480 index 5 - inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5 - tunnel hop limit 64 - inet 10.6.0.14 --> 10.6.0.15 netmask ff000000 -lo0: flags=2002000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL> mtu 8252 index 1 - inet6 ::1/128 -net0: flags=120002004941<UP,RUNNING,PROMISC,MULTICAST,DHCP,IPv6,PHYSRUNNING> mtu 1500 index 2 - inet6 fe80::221:9bff:fefd:2a22/10 -ilbint0: flags=120002000840<RUNNING,MULTICAST,IPv6,PHYSRUNNING> mtu 1500 index 3 - inet6 ::/0 -ilbext0: flags=120002000840<RUNNING,MULTICAST,IPv6,PHYSRUNNING> mtu 1500 index 4 - inet6 ::/0 -vpn0: flags=120002200850<POINTOPOINT,RUNNING,MULTICAST,NONUD,IPv6,PHYSRUNNING> mtu 1480 index 5 - inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5 - tunnel hop limit 64 - inet6 ::/0 --> fe80::b2d6:7c10 -''' - -FREEBSD_SOCKSTAT = '''\ -USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS -root python2.7 1294 41 tcp4 127.0.0.1:61115 127.0.0.1:4506 -''' - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -class NetworkTestCase(TestCase): - - def test_interfaces_ifconfig_linux(self): - interfaces = network._interfaces_ifconfig(LINUX) - self.assertEqual(interfaces, - {'eth0': {'hwaddr': 'e0:3f:49:85:6a:af', - 'inet': [{'address': '10.10.10.56', - 'broadcast': '10.10.10.255', - 'netmask': '255.255.252.0'}], - 'inet6': [{'address': 'fe80::e23f:49ff:fe85:6aaf', - 'prefixlen': '64', - 'scope': 'link'}], - 'up': True}, - 'lo': {'inet': [{'address': '127.0.0.1', - 'netmask': '255.0.0.0'}], - 'inet6': [{'address': '::1', - 'prefixlen': '128', - 'scope': 'host'}], - 'up': True}} - ) - - def test_interfaces_ifconfig_freebsd(self): - interfaces = network._interfaces_ifconfig(FREEBSD) - self.assertEqual(interfaces, - {'': {'up': False}, - 'em0': {'hwaddr': '00:30:48:ff:ff:ff', - 'inet': [{'address': '10.10.10.250', - 'broadcast': '10.10.10.255', - 'netmask': '255.255.255.224'}, - {'address': '10.10.10.56', - 'broadcast': '10.10.10.63', - 'netmask': '255.255.255.192'}], - 'up': True}, - 'em1': {'hwaddr': '00:30:48:aa:aa:aa', - 'up': False}, - 'lo0': {'inet': [{'address': '127.0.0.1', - 'netmask': '255.0.0.0'}], - 'inet6': [{'address': 'fe80::1', - 'prefixlen': '64', - 'scope': '0x8'}, - {'address': '::1', - 'prefixlen': '128', - 'scope': None}], - 'up': True}, - 'plip0': {'up': False}, - 'tun0': {'inet': [{'address': '10.12.0.1', - 'netmask': '255.255.255.255'}], - 'up': True}} - - ) - - def test_interfaces_ifconfig_solaris(self): - with patch('salt.utils.is_sunos', lambda: True): - interfaces = network._interfaces_ifconfig(SOLARIS) - self.assertEqual(interfaces, - {'ilbext0': {'inet': [{'address': '10.10.11.11', - 'broadcast': '10.10.11.31', - 'netmask': '255.255.255.224'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], - 'up': True}, - 'ilbint0': {'inet': [{'address': '10.6.0.11', - 'broadcast': '10.6.0.255', - 'netmask': '255.255.255.0'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], - 'up': True}, - 'lo0': {'inet': [{'address': '127.0.0.1', - 'netmask': '255.0.0.0'}], - 'inet6': [{'address': '::1', - 'prefixlen': '128'}], - 'up': True}, - 'net0': {'inet': [{'address': '10.10.10.38', - 'broadcast': '10.10.10.63', - 'netmask': '255.255.255.224'}], - 'inet6': [{'address': 'fe80::221:9bff:fefd:2a22', - 'prefixlen': '10'}], - 'up': True}, - 'vpn0': {'inet': [{'address': '10.6.0.14', - 'netmask': '255.0.0.0'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], - 'up': True}} - ) - - def test_freebsd_remotes_on(self): - with patch('salt.utils.is_sunos', lambda: False): - with patch('salt.utils.is_freebsd', lambda: True): - with patch('subprocess.check_output', - return_value=FREEBSD_SOCKSTAT): - remotes = network._freebsd_remotes_on('4506', 'remote') - self.assertEqual(remotes, set(['127.0.0.1'])) - - -if __name__ == '__main__': - from integration import run_tests - run_tests(NetworkTestCase, needs_daemon=False) diff --git a/tests/unit/utils/network_test.py b/tests/unit/utils/network_test.py new file mode 100644 index 0000000..f336588 --- /dev/null +++ b/tests/unit/utils/network_test.py @@ -0,0 +1,331 @@ +# -*- coding: utf-8 -*- +# Import Python libs +from __future__ import absolute_import + +# Import Salt Testing libs +from salttesting import skipIf +from salttesting import TestCase +from salttesting.helpers import ensure_in_syspath +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +ensure_in_syspath('../../') + +# Import salt libs +from salt.utils import network + +LINUX = '''\ +eth0 Link encap:Ethernet HWaddr e0:3f:49:85:6a:af + inet addr:10.10.10.56 Bcast:10.10.10.255 Mask:255.255.252.0 + inet6 addr: fe80::e23f:49ff:fe85:6aaf/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:643363 errors:0 dropped:0 overruns:0 frame:0 + TX packets:196539 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:386388355 (368.4 MiB) TX bytes:25600939 (24.4 MiB) + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:65536 Metric:1 + RX packets:548901 errors:0 dropped:0 overruns:0 frame:0 + TX packets:548901 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:613479895 (585.0 MiB) TX bytes:613479895 (585.0 MiB) +''' + +FREEBSD = ''' +em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 + options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO> + ether 00:30:48:ff:ff:ff + inet 10.10.10.250 netmask 0xffffffe0 broadcast 10.10.10.255 + inet 10.10.10.56 netmask 0xffffffc0 broadcast 10.10.10.63 + media: Ethernet autoselect (1000baseT <full-duplex>) + status: active +em1: flags=8c02<BROADCAST,OACTIVE,SIMPLEX,MULTICAST> metric 0 mtu 1500 + options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO> + ether 00:30:48:aa:aa:aa + media: Ethernet autoselect + status: no carrier +plip0: flags=8810<POINTOPOINT,SIMPLEX,MULTICAST> metric 0 mtu 1500 +lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384 + options=3<RXCSUM,TXCSUM> + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x8 + inet6 ::1 prefixlen 128 + inet 127.0.0.1 netmask 0xff000000 + nd6 options=3<PERFORMNUD,ACCEPT_RTADV> +tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500 + options=80000<LINKSTATE> + inet 10.12.0.1 --> 10.12.0.2 netmask 0xffffffff + Opened by PID 1964 +''' + +SOLARIS = '''\ +lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +net0: flags=100001100943<UP,BROADCAST,RUNNING,PROMISC,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1500 index 2 + inet 10.10.10.38 netmask ffffffe0 broadcast 10.10.10.63 +ilbint0: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 3 + inet 10.6.0.11 netmask ffffff00 broadcast 10.6.0.255 +ilbext0: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 4 + inet 10.10.11.11 netmask ffffffe0 broadcast 10.10.11.31 +ilbext0:1: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 4 + inet 10.10.11.12 netmask ffffffe0 broadcast 10.10.11.31 +vpn0: flags=1000011008d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1480 index 5 + inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5 + tunnel hop limit 64 + inet 10.6.0.14 --> 10.6.0.15 netmask ff000000 +lo0: flags=2002000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL> mtu 8252 index 1 + inet6 ::1/128 +net0: flags=120002004941<UP,RUNNING,PROMISC,MULTICAST,DHCP,IPv6,PHYSRUNNING> mtu 1500 index 2 + inet6 fe80::221:9bff:fefd:2a22/10 +ilbint0: flags=120002000840<RUNNING,MULTICAST,IPv6,PHYSRUNNING> mtu 1500 index 3 + inet6 ::/0 +ilbext0: flags=120002000840<RUNNING,MULTICAST,IPv6,PHYSRUNNING> mtu 1500 index 4 + inet6 ::/0 +vpn0: flags=120002200850<POINTOPOINT,RUNNING,MULTICAST,NONUD,IPv6,PHYSRUNNING> mtu 1480 index 5 + inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5 + tunnel hop limit 64 + inet6 ::/0 --> fe80::b2d6:7c10 +''' + +FREEBSD_SOCKSTAT = '''\ +USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS +root python2.7 1294 41 tcp4 127.0.0.1:61115 127.0.0.1:4506 +''' + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +class NetworkTestCase(TestCase): + + def test_interfaces_ifconfig_linux(self): + interfaces = network._interfaces_ifconfig(LINUX) + self.assertEqual(interfaces, + {'eth0': {'hwaddr': 'e0:3f:49:85:6a:af', + 'inet': [{'address': '10.10.10.56', + 'broadcast': '10.10.10.255', + 'netmask': '255.255.252.0'}], + 'inet6': [{'address': 'fe80::e23f:49ff:fe85:6aaf', + 'prefixlen': '64', + 'scope': 'link'}], + 'up': True}, + 'lo': {'inet': [{'address': '127.0.0.1', + 'netmask': '255.0.0.0'}], + 'inet6': [{'address': '::1', + 'prefixlen': '128', + 'scope': 'host'}], + 'up': True}} + ) + + def test_interfaces_ifconfig_freebsd(self): + interfaces = network._interfaces_ifconfig(FREEBSD) + self.assertEqual(interfaces, + {'': {'up': False}, + 'em0': {'hwaddr': '00:30:48:ff:ff:ff', + 'inet': [{'address': '10.10.10.250', + 'broadcast': '10.10.10.255', + 'netmask': '255.255.255.224'}, + {'address': '10.10.10.56', + 'broadcast': '10.10.10.63', + 'netmask': '255.255.255.192'}], + 'up': True}, + 'em1': {'hwaddr': '00:30:48:aa:aa:aa', + 'up': False}, + 'lo0': {'inet': [{'address': '127.0.0.1', + 'netmask': '255.0.0.0'}], + 'inet6': [{'address': 'fe80::1', + 'prefixlen': '64', + 'scope': '0x8'}, + {'address': '::1', + 'prefixlen': '128', + 'scope': None}], + 'up': True}, + 'plip0': {'up': False}, + 'tun0': {'inet': [{'address': '10.12.0.1', + 'netmask': '255.255.255.255'}], + 'up': True}} + + ) + + def test_interfaces_ifconfig_solaris(self): + with patch('salt.utils.is_sunos', lambda: True): + interfaces = network._interfaces_ifconfig(SOLARIS) + expected_interfaces = {'ilbint0': + {'inet6': [], + 'inet': [{'broadcast': '10.6.0.255', + 'netmask': '255.255.255.0', + 'address': '10.6.0.11'}], + 'up': True}, + 'lo0': + {'inet6': [{'prefixlen': '128', + 'address': '::1'}], + 'inet': [{'netmask': '255.0.0.0', + 'address': '127.0.0.1'}], + 'up': True}, + 'ilbext0': {'inet6': [], + 'inet': [{'broadcast': '10.10.11.31', + 'netmask': '255.255.255.224', + 'address': '10.10.11.11'}, + {'broadcast': '10.10.11.31', + 'netmask': '255.255.255.224', + 'address': '10.10.11.12'}], + 'up': True}, + 'vpn0': {'inet6': [], + 'inet': [{'netmask': '255.0.0.0', + 'address': '10.6.0.14'}], + 'up': True}, + 'net0': {'inet6': [{'prefixlen': '10', + 'address': 'fe80::221:9bff:fefd:2a22'}], + 'inet': [{'broadcast': '10.10.10.63', + 'netmask': '255.255.255.224', + 'address': '10.10.10.38'}], + 'up': True}} + self.assertEqual(interfaces, expected_interfaces) + + def test_freebsd_remotes_on(self): + with patch('salt.utils.is_sunos', lambda: False): + with patch('salt.utils.is_freebsd', lambda: True): + with patch('subprocess.check_output', + return_value=FREEBSD_SOCKSTAT): + remotes = network._freebsd_remotes_on('4506', 'remote') + self.assertEqual(remotes, set(['127.0.0.1'])) + + @patch('platform.node', MagicMock(return_value='nodename')) + @patch('socket.gethostname', MagicMock(return_value='hostname')) + @patch('socket.getfqdn', MagicMock(return_value='hostname.domainname.blank')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])) + def test_generate_minion_id_distinct(self): + ''' + Test if minion IDs are distinct in the pool. + + :return: + ''' + self.assertEqual(network._generate_minion_id(), + ['hostname.domainname.blank', 'nodename', 'hostname', '1.2.3.4', '5.6.7.8']) + + @patch('platform.node', MagicMock(return_value='hostname')) + @patch('socket.gethostname', MagicMock(return_value='hostname')) + @patch('socket.getfqdn', MagicMock(return_value='hostname')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])) + def test_generate_minion_id_duplicate(self): + ''' + Test if IP addresses in the minion IDs are distinct in the pool + + :return: + ''' + self.assertEqual(network._generate_minion_id(), ['hostname', '1.2.3.4']) + + @patch('platform.node', MagicMock(return_value='very.long.and.complex.domain.name')) + @patch('socket.gethostname', MagicMock(return_value='hostname')) + @patch('socket.getfqdn', MagicMock(return_value='')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])) + def test_generate_minion_id_platform_used(self): + ''' + Test if platform.node is used for the first occurrence. + The platform.node is most common hostname resolver before anything else. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), 'very.long.and.complex.domain.name') + + @patch('platform.node', MagicMock(return_value='localhost')) + @patch('socket.gethostname', MagicMock(return_value='pick.me')) + @patch('socket.getfqdn', MagicMock(return_value='hostname.domainname.blank')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])) + def test_generate_minion_id_platform_localhost_filtered(self): + ''' + Test if localhost is filtered from the first occurrence. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), 'hostname.domainname.blank') + + @patch('platform.node', MagicMock(return_value='localhost')) + @patch('socket.gethostname', MagicMock(return_value='ip6-loopback')) + @patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1', '1.2.3.4'])) + def test_generate_minion_id_platform_localhost_filtered_all(self): + ''' + Test if any of the localhost is filtered from everywhere. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), '1.2.3.4') + + @patch('platform.node', MagicMock(return_value='localhost')) + @patch('socket.gethostname', MagicMock(return_value='ip6-loopback')) + @patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])) + def test_generate_minion_id_platform_localhost_only(self): + ''' + Test if there is no other choice but localhost. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), 'localhost') + + @patch('platform.node', MagicMock(return_value='localhost')) + @patch('socket.gethostname', MagicMock(return_value='ip6-loopback')) + @patch('socket.getfqdn', MagicMock(return_value='pick.me')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])) + def test_generate_minion_id_platform_fqdn(self): + ''' + Test if fqdn is picked up. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), 'pick.me') + + @patch('platform.node', MagicMock(return_value='localhost')) + @patch('socket.gethostname', MagicMock(return_value='ip6-loopback')) + @patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'pick.me', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])) + def test_generate_minion_id_platform_localhost_addrinfo(self): + ''' + Test if addinfo is picked up. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), 'pick.me') + + @patch('platform.node', MagicMock(return_value='localhost')) + @patch('socket.gethostname', MagicMock(return_value='ip6-loopback')) + @patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')) + @patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])) + @patch('salt.utils.fopen', MagicMock(return_valute=False)) + @patch('os.path.exists', MagicMock(return_valute=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1', '1.2.3.4'])) + def test_generate_minion_id_platform_ip_addr_only(self): + ''' + Test if IP address is the only what is used as a Minion ID in case no DNS name. + + :return: + ''' + self.assertEqual(network.generate_minion_id(), '1.2.3.4') + + +if __name__ == '__main__': + from integration import run_tests + run_tests(NetworkTestCase, needs_daemon=False) -- 2.10.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor