File implement-network.fqdns-module-function-bsc-1134860-.patch of Package salt

From a11587a1209cd198f421fafdb43510b6d651f4b2 Mon Sep 17 00:00:00 2001
From: EricS <54029547+ESiebigteroth@users.noreply.github.com>
Date: Tue, 3 Sep 2019 11:22:53 +0200
Subject: [PATCH] Implement network.fqdns module function (bsc#1134860)
 (#172)

* Duplicate fqdns logic in module.network
* Move _get_interfaces to utils.network
* Reuse network.fqdns in grains.core.fqdns
* Return empty list when fqdns grains is disabled


Co-authored-by: Eric Siebigteroth <eric.siebigteroth@suse.de>
---
 salt/grains/core.py            | 66 +++++-------------------------------------
 salt/modules/network.py        | 60 ++++++++++++++++++++++++++++++++++++++
 salt/utils/network.py          | 12 ++++++++
 tests/unit/grains/test_core.py | 63 +++++++++++++++++++++++++++++++---------
 4 files changed, 130 insertions(+), 71 deletions(-)

diff --git a/salt/grains/core.py b/salt/grains/core.py
index 0f3ccd9b92..77ae99590f 100644
--- a/salt/grains/core.py
+++ b/salt/grains/core.py
@@ -26,8 +26,9 @@ from errno import EACCES, EPERM
 import datetime
 import warnings
 import time
+import salt.modules.network
 
-from multiprocessing.pool import ThreadPool
+from salt.utils.network import _get_interfaces
 
 # pylint: disable=import-error
 try:
@@ -84,6 +85,7 @@ __salt__ = {
     'cmd.run_all': salt.modules.cmdmod._run_all_quiet,
     'smbios.records': salt.modules.smbios.records,
     'smbios.get': salt.modules.smbios.get,
+    'network.fqdns': salt.modules.network.fqdns,
 }
 log = logging.getLogger(__name__)
 
@@ -107,7 +109,6 @@ HAS_UNAME = True
 if not hasattr(os, 'uname'):
     HAS_UNAME = False
 
-_INTERFACES = {}
 
 # Possible value for h_errno defined in netdb.h
 HOST_NOT_FOUND = 1
@@ -1553,17 +1554,6 @@ def _linux_bin_exists(binary):
         return False
 
 
-def _get_interfaces():
-    '''
-    Provide a dict of the connected interfaces and their ip addresses
-    '''
-
-    global _INTERFACES
-    if not _INTERFACES:
-        _INTERFACES = salt.utils.network.interfaces()
-    return _INTERFACES
-
-
 def _parse_lsb_release():
     ret = {}
     try:
@@ -2271,52 +2261,12 @@ def fqdns():
     '''
     Return all known FQDNs for the system by enumerating all interfaces and
     then trying to reverse resolve them (excluding 'lo' interface).
+    To disable the fqdns grain, set enable_fqdns_grains: False in the minion configuration file.
     '''
-    # Provides:
-    # fqdns
-
-    grains = {}
-    fqdns = set()
-
-    def _lookup_fqdn(ip):
-        try:
-            name, aliaslist, addresslist = socket.gethostbyaddr(ip)
-            return [socket.getfqdn(name)] + [als for als in aliaslist if salt.utils.network.is_fqdn(als)]
-        except socket.herror as err:
-            if err.errno in (0, HOST_NOT_FOUND, NO_DATA):
-                # No FQDN for this IP address, so we don't need to know this all the time.
-                log.debug("Unable to resolve address %s: %s", ip, err)
-            else:
-                log.error(err_message, ip, err)
-        except (socket.error, socket.gaierror, socket.timeout) as err:
-            log.error(err_message, ip, err)
-
-    start = time.time()
-
-    addresses = salt.utils.network.ip_addrs(include_loopback=False, interface_data=_get_interfaces())
-    addresses.extend(salt.utils.network.ip_addrs6(include_loopback=False, interface_data=_get_interfaces()))
-    err_message = 'Exception during resolving address: %s'
-
-    # Create a ThreadPool to process the underlying calls to 'socket.gethostbyaddr' in parallel.
-    # This avoid blocking the execution when the "fqdn" is not defined for certains IP addresses, which was causing
-    # that "socket.timeout" was reached multiple times secuencially, blocking execution for several seconds.
-
-    try:
-       pool = ThreadPool(8)
-       results = pool.map(_lookup_fqdn, addresses)
-       pool.close()
-       pool.join()
-    except Exception as exc:
-       log.error("Exception while creating a ThreadPool for resolving FQDNs: %s", exc)
-
-    for item in results:
-        if item:
-            fqdns.update(item)
-
-    elapsed = time.time() - start
-    log.debug('Elapsed time getting FQDNs: {} seconds'.format(elapsed))
-
-    return {"fqdns": sorted(list(fqdns))}
+    opt = {"fqdns": []}
+    if __opts__.get('enable_fqdns_grains', True) == True:
+        opt = __salt__['network.fqdns']()
+    return opt
 
 
 def ip_fqdn():
diff --git a/salt/modules/network.py b/salt/modules/network.py
index 38e2bc326e..880f4f8d5f 100644
--- a/salt/modules/network.py
+++ b/salt/modules/network.py
@@ -11,6 +11,10 @@ import logging
 import re
 import os
 import socket
+import time
+
+from multiprocessing.pool import ThreadPool
+
 
 # Import salt libs
 import salt.utils.decorators.path
@@ -1887,3 +1891,59 @@ def iphexval(ip):
     a = ip.split('.')
     hexval = ['%02X' % int(x) for x in a]  # pylint: disable=E1321
     return ''.join(hexval)
+
+
+def fqdns():
+    '''
+    Return all known FQDNs for the system by enumerating all interfaces and
+    then trying to reverse resolve them (excluding 'lo' interface).
+    '''
+    # Provides:
+    # fqdns
+
+    # Possible value for h_errno defined in netdb.h
+    HOST_NOT_FOUND = 1
+    NO_DATA = 4
+
+    grains = {}
+    fqdns = set()
+
+    def _lookup_fqdn(ip):
+        try:
+            name, aliaslist, addresslist = socket.gethostbyaddr(ip)
+            return [socket.getfqdn(name)] + [als for als in aliaslist if salt.utils.network.is_fqdn(als)]
+        except socket.herror as err:
+            if err.errno in (0, HOST_NOT_FOUND, NO_DATA):
+                # No FQDN for this IP address, so we don't need to know this all the time.
+                log.debug("Unable to resolve address %s: %s", ip, err)
+            else:
+                log.error(err_message, err)
+        except (socket.error, socket.gaierror, socket.timeout) as err:
+            log.error(err_message, err)
+
+    start = time.time()
+
+    addresses = salt.utils.network.ip_addrs(include_loopback=False, interface_data=salt.utils.network._get_interfaces())
+    addresses.extend(salt.utils.network.ip_addrs6(include_loopback=False, interface_data=salt.utils.network._get_interfaces()))
+    err_message = 'Exception during resolving address: %s'
+
+    # Create a ThreadPool to process the underlying calls to 'socket.gethostbyaddr' in parallel.
+    # This avoid blocking the execution when the "fqdn" is not defined for certains IP addresses, which was causing
+    # that "socket.timeout" was reached multiple times secuencially, blocking execution for several seconds.
+
+    try:
+       pool = ThreadPool(8)
+       results = pool.map(_lookup_fqdn, addresses)
+       pool.close()
+       pool.join()
+    except Exception as exc:
+       log.error("Exception while creating a ThreadPool for resolving FQDNs: %s", exc)
+
+    for item in results:
+        if item:
+            fqdns.update(item)
+
+    elapsed = time.time() - start
+    log.debug('Elapsed time getting FQDNs: {} seconds'.format(elapsed))
+
+    return {"fqdns": sorted(list(fqdns))}
\ No newline at end of file
diff --git a/salt/utils/network.py b/salt/utils/network.py
index 74536cc143..4cc8a05c4a 100644
--- a/salt/utils/network.py
+++ b/salt/utils/network.py
@@ -50,6 +50,18 @@ except (ImportError, OSError, AttributeError, TypeError):
     pass
 
 
+_INTERFACES = {}
+def _get_interfaces(): #! function
+    '''
+    Provide a dict of the connected interfaces and their ip addresses
+    '''
+
+    global _INTERFACES
+    if not _INTERFACES:
+        _INTERFACES = interfaces()
+    return _INTERFACES
+
+
 def sanitize_host(host):
     '''
     Sanitize host string.
diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py
index ac03b57226..60914204b0 100644
--- a/tests/unit/grains/test_core.py
+++ b/tests/unit/grains/test_core.py
@@ -35,6 +35,7 @@ import salt.utils.path
 import salt.modules.cmdmod
 import salt.modules.smbios
 import salt.grains.core as core
+import salt.modules.network
 
 # Import 3rd-party libs
 from salt.ext import six
@@ -1029,6 +1030,40 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
         with patch.object(salt.utils.dns, 'parse_resolv', MagicMock(return_value=resolv_mock)):
             assert core.dns() == ret
 
+
+    def test_enablefqdnsFalse(self):
+        '''
+        tests enable_fqdns_grains is set to False
+        '''
+        with patch.dict('salt.grains.core.__opts__', {'enable_fqdns_grains':False}):
+            assert core.fqdns() == {"fqdns": []}
+
+
+    def test_enablefqdnsTrue(self):
+        '''
+        testing that grains uses network.fqdns module
+        '''
+        with patch.dict('salt.grains.core.__salt__', {'network.fqdns': MagicMock(return_value="my.fake.domain")}):
+            with patch.dict('salt.grains.core.__opts__', {'enable_fqdns_grains':True}):
+                assert core.fqdns() == 'my.fake.domain'
+
+
+    def test_enablefqdnsNone(self):
+        '''
+        testing default fqdns grains is returned when enable_fqdns_grains is None
+        '''
+        with patch.dict('salt.grains.core.__opts__', {'enable_fqdns_grains':None}):
+             assert core.fqdns() == {"fqdns": []}
+
+
+    def test_enablefqdnswithoutpaching(self):
+        '''
+        testing fqdns grains is enabled by default
+        '''
+        with patch.dict('salt.grains.core.__salt__', {'network.fqdns': MagicMock(return_value="my.fake.domain")}):
+            assert core.fqdns() == 'my.fake.domain'
+
+
     @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux')
     @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8']))
     @patch('salt.utils.network.ip_addrs6',
@@ -1044,11 +1079,12 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
                                ('foo.bar.baz', [], ['fe80::a8b2:93ff:fe00:0']),
                                ('bluesniff.foo.bar', [], ['fe80::a8b2:93ff:dead:beef'])]
         ret = {'fqdns': ['bluesniff.foo.bar', 'foo.bar.baz', 'rinzler.evil-corp.com']}
-        with patch.object(socket, 'gethostbyaddr', side_effect=reverse_resolv_mock):
-            fqdns = core.fqdns()
-            assert "fqdns" in fqdns
-            assert len(fqdns['fqdns']) == len(ret['fqdns'])
-            assert set(fqdns['fqdns']) == set(ret['fqdns'])
+        with patch.dict(core.__salt__, {'network.fqdns': salt.modules.network.fqdns}):
+            with patch.object(socket, 'gethostbyaddr', side_effect=reverse_resolv_mock):
+                fqdns = core.fqdns()
+                assert "fqdns" in fqdns
+                assert len(fqdns['fqdns']) == len(ret['fqdns'])
+                assert set(fqdns['fqdns']) == set(ret['fqdns'])
 
     @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux')
     @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4']))
@@ -1094,14 +1130,15 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
                                ('rinzler.evil-corp.com', ["false-hostname", "badaliass"], ['5.6.7.8']),
                                ('foo.bar.baz', [], ['fe80::a8b2:93ff:fe00:0']),
                                ('bluesniff.foo.bar', ["alias.bluesniff.foo.bar"], ['fe80::a8b2:93ff:dead:beef'])]
-        with patch.object(socket, 'gethostbyaddr', side_effect=reverse_resolv_mock):
-            fqdns = core.fqdns()
-            assert "fqdns" in fqdns
-            for alias in ["this.is.valid.alias", "alias.bluesniff.foo.bar"]:
-                assert alias in fqdns["fqdns"]
-
-            for alias in ["throwmeaway", "false-hostname", "badaliass"]:
-                assert alias not in fqdns["fqdns"]
+        with patch.dict(core.__salt__, {'network.fqdns': salt.modules.network.fqdns}):
+            with patch.object(socket, 'gethostbyaddr', side_effect=reverse_resolv_mock):
+                fqdns = core.fqdns()
+                assert "fqdns" in fqdns
+                for alias in ["this.is.valid.alias", "alias.bluesniff.foo.bar"]:
+                    assert alias in fqdns["fqdns"]
+
+                for alias in ["throwmeaway", "false-hostname", "badaliass"]:
+                    assert alias not in fqdns["fqdns"]
 
     def test_core_virtual(self):
         '''
-- 
2.16.4