salt/tests/unit/modules/test_network.py

405 lines
16 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
'''
:codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
'''
# Import Python Libs
from __future__ import absolute_import, unicode_literals, print_function
2018-10-21 15:39:29 +00:00
import logging
import socket
import os.path
# Import Salt Testing Libs
2017-03-21 17:58:19 +00:00
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
mock_open,
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
# Import Salt Libs
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
import salt.utils.network
import salt.utils.path
import salt.modules.network as network
from salt.exceptions import CommandExecutionError
2018-09-28 13:34:05 +00:00
from salt._compat import ipaddress
log = logging.getLogger(__name__)
@skipIf(NO_MOCK, NO_MOCK_REASON)
2017-03-21 17:58:19 +00:00
class NetworkTestCase(TestCase, LoaderModuleMockMixin):
'''
Test cases for salt.modules.network
'''
def setup_loader_modules(self):
return {network: {}}
2017-03-21 17:58:19 +00:00
def test_wol_bad_mac(self):
'''
tests network.wol with bad mac
'''
bad_mac = '31337'
self.assertRaises(ValueError, network.wol, bad_mac)
def test_wol_success(self):
'''
tests network.wol success
'''
mac = '080027136977'
bcast = '255.255.255.255 7'
class MockSocket(object):
def __init__(self, *args, **kwargs):
pass
def __call__(self, *args, **kwargs):
pass
def setsockopt(self, *args, **kwargs):
pass
def sendto(self, *args, **kwargs):
pass
with patch('socket.socket', MockSocket):
self.assertTrue(network.wol(mac, bcast))
def test_ping(self):
'''
Test for Performs a ping to a host
'''
with patch.object(salt.utils.network, 'sanitize_host',
return_value='A'):
mock_all = MagicMock(side_effect=[{'retcode': 1}, {'retcode': 0}])
with patch.dict(network.__salt__, {'cmd.run_all': mock_all}):
self.assertFalse(network.ping('host', return_boolean=True))
self.assertTrue(network.ping('host', return_boolean=True))
with patch.dict(network.__salt__, {'cmd.run':
MagicMock(return_value='A')}):
self.assertEqual(network.ping('host'), 'A')
def test_netstat(self):
'''
Test for return information on open ports and states
'''
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.object(network, '_netstat_linux', return_value='A'):
with patch.object(network, '_ss_linux', return_value='A'):
self.assertEqual(network.netstat(), 'A')
with patch.dict(network.__grains__, {'kernel': 'OpenBSD'}):
with patch.object(network, '_netstat_bsd', return_value='A'):
self.assertEqual(network.netstat(), 'A')
with patch.dict(network.__grains__, {'kernel': 'A'}):
self.assertRaises(CommandExecutionError, network.netstat)
def test_active_tcp(self):
'''
Test for return a dict containing information on all
of the running TCP connections
'''
with patch.object(salt.utils.network, 'active_tcp', return_value='A'):
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
self.assertEqual(network.active_tcp(), 'A')
def test_traceroute(self):
'''
Test for Performs a traceroute to a 3rd party host
'''
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
with patch.object(salt.utils.path, 'which', side_effect=[False, True]):
self.assertListEqual(network.traceroute('host'), [])
with patch.object(salt.utils.network, 'sanitize_host',
return_value='A'):
with patch.dict(network.__salt__, {'cmd.run':
MagicMock(return_value="")}):
self.assertListEqual(network.traceroute('host'), [])
def test_dig(self):
'''
Test for Performs a DNS lookup with dig
'''
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
with patch('salt.utils.path.which', MagicMock(return_value='dig')), \
2017-04-10 13:00:57 +00:00
patch.object(salt.utils.network, 'sanitize_host',
return_value='A'), \
patch.dict(network.__salt__, {'cmd.run':
MagicMock(return_value='A')}):
self.assertEqual(network.dig('host'), 'A')
def test_arp(self):
'''
Test for return the arp table from the minion
'''
with patch.dict(network.__salt__,
{'cmd.run':
2017-04-10 13:00:57 +00:00
MagicMock(return_value='A,B,C,D\nE,F,G,H\n')}), \
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
patch('salt.utils.path.which', MagicMock(return_value='')):
self.assertDictEqual(network.arp(), {})
def test_interfaces(self):
'''
Test for return a dictionary of information about
all the interfaces on the minion
'''
with patch.object(salt.utils.network, 'interfaces', return_value={}):
self.assertDictEqual(network.interfaces(), {})
def test_hw_addr(self):
'''
Test for return the hardware address (a.k.a. MAC address)
for a given interface
'''
with patch.object(salt.utils.network, 'hw_addr', return_value={}):
self.assertDictEqual(network.hw_addr('iface'), {})
def test_interface(self):
'''
Test for return the inet address for a given interface
'''
with patch.object(salt.utils.network, 'interface', return_value={}):
self.assertDictEqual(network.interface('iface'), {})
def test_interface_ip(self):
'''
Test for return the inet address for a given interface
'''
with patch.object(salt.utils.network, 'interface_ip', return_value={}):
self.assertDictEqual(network.interface_ip('iface'), {})
def test_subnets(self):
'''
Test for returns a list of subnets to which the host belongs
'''
with patch.object(salt.utils.network, 'subnets', return_value={}):
self.assertDictEqual(network.subnets(), {})
def test_in_subnet(self):
'''
Test for returns True if host is within specified
subnet, otherwise False.
'''
with patch.object(salt.utils.network, 'in_subnet', return_value={}):
self.assertDictEqual(network.in_subnet('iface'), {})
def test_ip_addrs(self):
'''
Test for returns a list of IPv4 addresses assigned to the host.
'''
with patch.object(salt.utils.network, 'ip_addrs',
return_value=['0.0.0.0']):
with patch.object(salt.utils.network, 'in_subnet',
return_value=True):
self.assertListEqual(network.ip_addrs('interface',
'include_loopback',
'cidr'), ['0.0.0.0'])
self.assertListEqual(network.ip_addrs('interface',
'include_loopback'),
['0.0.0.0'])
def test_ip_addrs6(self):
'''
Test for returns a list of IPv6 addresses assigned to the host.
'''
with patch.object(salt.utils.network, 'ip_addrs6',
return_value=['A']):
self.assertListEqual(network.ip_addrs6('int', 'include'), ['A'])
def test_get_hostname(self):
'''
Test for Get hostname
'''
with patch.object(network.socket, 'gethostname', return_value='A'):
self.assertEqual(network.get_hostname(), 'A')
def test_mod_hostname(self):
'''
Test for Modify hostname
'''
self.assertFalse(network.mod_hostname(None))
file_d = '\n'.join(['#', 'A B C D,E,F G H'])
with patch.object(salt.utils.path, 'which', return_value='hostname'), \
patch.dict(network.__salt__,
{'cmd.run': MagicMock(return_value=None)}), \
patch.dict(network.__grains__, {'os_family': 'A'}), \
patch('salt.utils.files.fopen', mock_open(read_data=file_d)):
self.assertTrue(network.mod_hostname('hostname'))
2017-04-10 13:00:57 +00:00
def test_connect(self):
'''
Test for Test connectivity to a host using a particular
port from the minion.
'''
2017-04-10 13:00:57 +00:00
with patch('socket.socket') as mock_socket:
self.assertDictEqual(network.connect(False, 'port'),
{'comment': 'Required argument, host, is missing.',
'result': False})
2017-04-10 13:00:57 +00:00
self.assertDictEqual(network.connect('host', False),
{'comment': 'Required argument, port, is missing.',
'result': False})
2017-04-10 13:00:57 +00:00
ret = 'Unable to connect to host (0) on tcp port port'
mock_socket.side_effect = Exception('foo')
with patch.object(salt.utils.network, 'sanitize_host',
return_value='A'):
with patch.object(socket, 'getaddrinfo',
return_value=[['ipv4', 'A', 6, 'B', '0.0.0.0']]):
self.assertDictEqual(network.connect('host', 'port'),
{'comment': ret, 'result': False})
ret = 'Successfully connected to host (0) on tcp port port'
mock_socket.side_effect = MagicMock()
mock_socket.settimeout().return_value = None
mock_socket.connect().return_value = None
mock_socket.shutdown().return_value = None
with patch.object(salt.utils.network, 'sanitize_host',
return_value='A'):
with patch.object(socket,
'getaddrinfo',
return_value=[['ipv4',
'A', 6, 'B', '0.0.0.0']]):
self.assertDictEqual(network.connect('host', 'port'),
{'comment': ret, 'result': True})
@skipIf(not bool(ipaddress), 'unable to import \'ipaddress\'')
def test_is_private(self):
'''
Test for Check if the given IP address is a private address
'''
2017-03-21 17:58:19 +00:00
with patch.object(ipaddress.IPv4Address, 'is_private',
return_value=True):
self.assertTrue(network.is_private('0.0.0.0'))
2017-03-21 17:58:19 +00:00
with patch.object(ipaddress.IPv6Address, 'is_private',
return_value=True):
self.assertTrue(network.is_private('::1'))
@skipIf(not bool(ipaddress), 'unable to import \'ipaddress\'')
def test_is_loopback(self):
'''
Test for Check if the given IP address is a loopback address
'''
2017-03-21 17:58:19 +00:00
with patch.object(ipaddress.IPv4Address, 'is_loopback',
return_value=True):
self.assertTrue(network.is_loopback('127.0.0.1'))
2017-03-21 17:58:19 +00:00
with patch.object(ipaddress.IPv6Address, 'is_loopback',
return_value=True):
self.assertTrue(network.is_loopback('::1'))
def test_get_bufsize(self):
'''
Test for return network buffer sizes as a dict
'''
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.object(os.path, 'exists', return_value=True):
with patch.object(network, '_get_bufsize_linux',
return_value={'size': 1}):
self.assertDictEqual(network.get_bufsize('iface'),
{'size': 1})
with patch.dict(network.__grains__, {'kernel': 'A'}):
self.assertDictEqual(network.get_bufsize('iface'), {})
def test_mod_bufsize(self):
'''
Test for Modify network interface buffers (currently linux only)
'''
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.object(os.path, 'exists', return_value=True):
with patch.object(network, '_mod_bufsize_linux',
return_value={'size': 1}):
self.assertDictEqual(network.mod_bufsize('iface'),
{'size': 1})
with patch.dict(network.__grains__, {'kernel': 'A'}):
self.assertFalse(network.mod_bufsize('iface'))
def test_routes(self):
'''
Test for return currently configured routes from routing table
'''
self.assertRaises(CommandExecutionError, network.routes, 'family')
with patch.dict(network.__grains__, {'kernel': 'A', 'os': 'B'}):
self.assertRaises(CommandExecutionError, network.routes, 'inet')
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.object(network, '_netstat_route_linux',
side_effect=['A', [{'addr_family': 'inet'}]]):
with patch.object(network, '_ip_route_linux',
side_effect=['A', [{'addr_family': 'inet'}]]):
self.assertEqual(network.routes(None), 'A')
self.assertListEqual(network.routes('inet'),
[{'addr_family': 'inet'}])
def test_default_route(self):
'''
Test for return default route(s) from routing table
'''
self.assertRaises(CommandExecutionError, network.default_route,
'family')
with patch.object(network, 'routes',
side_effect=[[{'addr_family': 'inet'},
{'destination': 'A'}], []]):
with patch.dict(network.__grains__, {'kernel': 'A',
'os': 'B'}):
self.assertRaises(CommandExecutionError,
network.default_route, 'inet')
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
self.assertListEqual(network.default_route('inet'), [])
def test_get_route(self):
'''
Test for return output from get_route
'''
mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev eth0 src 10.10.10.10 uid 0\ncache')
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.dict(network.__salt__, {'cmd.run': mock_iproute}):
expected = {'interface': 'eth0',
'source': '10.10.10.10',
'destination': '8.8.8.8',
'gateway': '10.10.10.1'}
ret = network.get_route('8.8.8.8')
self.assertEqual(ret, expected)
mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev eth0.1 src 10.10.10.10 uid 0\ncache')
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.dict(network.__salt__, {'cmd.run': mock_iproute}):
expected = {'interface': 'eth0.1',
'source': '10.10.10.10',
'destination': '8.8.8.8',
'gateway': '10.10.10.1'}
ret = network.get_route('8.8.8.8')
self.assertEqual(ret, expected)
mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev eth0:1 src 10.10.10.10 uid 0\ncache')
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.dict(network.__salt__, {'cmd.run': mock_iproute}):
expected = {'interface': 'eth0:1',
'source': '10.10.10.10',
'destination': '8.8.8.8',
'gateway': '10.10.10.1'}
ret = network.get_route('8.8.8.8')
self.assertEqual(ret, expected)
mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev lan-br0 src 10.10.10.10 uid 0\ncache')
with patch.dict(network.__grains__, {'kernel': 'Linux'}):
with patch.dict(network.__salt__, {'cmd.run': mock_iproute}):
expected = {'interface': 'lan-br0',
'source': '10.10.10.10',
'destination': '8.8.8.8',
'gateway': '10.10.10.1'}
ret = network.get_route('8.8.8.8')
self.assertEqual(ret, expected)