2018-01-17 15:31:51 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
|
|
|
:codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
|
|
|
|
'''
|
|
|
|
|
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
from tests.support.unit import TestCase, skipIf
|
|
|
|
from tests.support.mock import (
|
|
|
|
NO_MOCK,
|
|
|
|
NO_MOCK_REASON,
|
|
|
|
MagicMock,
|
|
|
|
patch)
|
|
|
|
|
|
|
|
# Import Salt libs
|
|
|
|
import salt.exceptions
|
|
|
|
import salt.state
|
2018-01-17 15:38:54 +00:00
|
|
|
from salt.utils import ssdp
|
2018-01-17 15:31:51 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
import pytest
|
|
|
|
except ImportError as err:
|
|
|
|
pytest = None
|
|
|
|
|
|
|
|
|
2018-01-18 16:44:48 +00:00
|
|
|
class Mocks(object):
|
|
|
|
def get_socket_mock(self, expected_ip, expected_hostname):
|
|
|
|
'''
|
|
|
|
Get a mock of a socket
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
sck = MagicMock()
|
|
|
|
sck.getsockname = MagicMock(return_value=(expected_ip, 123456))
|
|
|
|
|
|
|
|
sock_mock = MagicMock()
|
|
|
|
sock_mock.socket = MagicMock(return_value=sck)
|
|
|
|
sock_mock.gethostbyname = MagicMock(return_value=expected_hostname)
|
|
|
|
|
|
|
|
return sock_mock
|
|
|
|
|
|
|
|
|
2018-01-17 15:31:51 +00:00
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
|
|
@skipIf(pytest is None, 'PyTest is missing')
|
2018-01-18 16:44:48 +00:00
|
|
|
class SSDPBaseTestCase(TestCase, Mocks):
|
2018-01-17 15:31:51 +00:00
|
|
|
'''
|
|
|
|
TestCase for SSDP-related parts.
|
|
|
|
'''
|
|
|
|
|
2018-01-19 14:55:41 +00:00
|
|
|
@staticmethod
|
|
|
|
def exception_generic(*args, **kwargs):
|
|
|
|
'''
|
|
|
|
Side effect
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
raise Exception('some network error')
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def exception_attr_error(*args, **kwargs):
|
|
|
|
'''
|
|
|
|
Side effect
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
raise AttributeError('attribute error: {0}. {1}'.format(args, kwargs))
|
|
|
|
|
2018-01-17 15:38:54 +00:00
|
|
|
@patch('salt.utils.ssdp._json', None)
|
|
|
|
@patch('salt.utils.ssdp.asyncio', None)
|
2018-01-17 15:41:56 +00:00
|
|
|
def test_base_avail(self):
|
2018-01-17 15:31:51 +00:00
|
|
|
'''
|
2018-01-17 15:38:54 +00:00
|
|
|
Test SSDP base class availability method.
|
2018-01-17 15:31:51 +00:00
|
|
|
:return:
|
|
|
|
'''
|
2018-01-17 15:38:54 +00:00
|
|
|
base = ssdp.SSDPBase()
|
2018-01-17 15:41:56 +00:00
|
|
|
assert not base._is_available()
|
|
|
|
|
|
|
|
with patch('salt.utils.ssdp._json', True):
|
|
|
|
assert not base._is_available()
|
|
|
|
|
|
|
|
with patch('salt.utils.ssdp.asyncio', True):
|
|
|
|
assert not base._is_available()
|
|
|
|
|
|
|
|
with patch('salt.utils.ssdp._json', True), patch('salt.utils.ssdp.asyncio', True):
|
|
|
|
assert base._is_available()
|
2018-01-17 15:53:29 +00:00
|
|
|
|
|
|
|
def test_base_protocol_settings(self):
|
|
|
|
'''
|
|
|
|
Tests default constants data.
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
base = ssdp.SSDPBase()
|
|
|
|
v_keys = ['signature', 'answer', 'port', 'listen_ip', 'timeout']
|
|
|
|
v_vals = ['__salt_master_service', {}, 4520, '0.0.0.0', 3]
|
|
|
|
for key in v_keys:
|
|
|
|
assert key in base.DEFAULTS
|
|
|
|
|
|
|
|
for key in base.DEFAULTS.keys():
|
|
|
|
assert key in v_keys
|
|
|
|
|
|
|
|
for key, value in zip(v_keys, v_vals):
|
|
|
|
assert base.DEFAULTS[key] == value
|
2018-01-17 16:21:52 +00:00
|
|
|
|
|
|
|
def test_base_self_ip(self):
|
|
|
|
'''
|
|
|
|
Test getting self IP method.
|
|
|
|
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
base = ssdp.SSDPBase()
|
|
|
|
expected_ip = '192.168.1.10'
|
|
|
|
expected_host = 'oxygen'
|
2018-01-18 16:44:48 +00:00
|
|
|
sock_mock = self.get_socket_mock(expected_ip, expected_host)
|
2018-01-17 16:21:52 +00:00
|
|
|
|
|
|
|
with patch('salt.utils.ssdp.socket', sock_mock):
|
|
|
|
assert base.get_self_ip() == expected_ip
|
|
|
|
|
2018-01-19 14:55:41 +00:00
|
|
|
sock_mock.socket().getsockname.side_effect = SSDPBaseTestCase.exception_generic
|
2018-01-17 16:21:52 +00:00
|
|
|
with patch('salt.utils.ssdp.socket', sock_mock):
|
|
|
|
assert base.get_self_ip() == expected_host
|
2018-01-19 13:49:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
|
|
@skipIf(pytest is None, 'PyTest is missing')
|
|
|
|
class SSDPFactoryTestCase(TestCase):
|
|
|
|
'''
|
|
|
|
Test socket protocol
|
|
|
|
'''
|
|
|
|
@patch('salt.utils.ssdp.socket.gethostbyname', MagicMock(return_value='10.10.10.10'))
|
|
|
|
def test_attr_check(self):
|
|
|
|
'''
|
|
|
|
Tests attributes are set to the base class
|
|
|
|
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
config = {
|
|
|
|
ssdp.SSDPBase.SIGNATURE: '-signature-',
|
|
|
|
ssdp.SSDPBase.ANSWER: {'this-is': 'the-answer'}
|
|
|
|
}
|
|
|
|
factory = ssdp.SSDPFactory(**config)
|
|
|
|
for attr in [ssdp.SSDPBase.SIGNATURE, ssdp.SSDPBase.ANSWER]:
|
|
|
|
assert hasattr(factory, attr)
|
|
|
|
assert getattr(factory, attr) == config[attr]
|
|
|
|
assert not factory.disable_hidden
|
|
|
|
assert factory.my_ip == '10.10.10.10'
|
2018-01-19 14:29:02 +00:00
|
|
|
|
|
|
|
def test_transport_sendto_success(self):
|
|
|
|
'''
|
|
|
|
Test transport send_to.
|
|
|
|
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
transport = MagicMock()
|
|
|
|
log = MagicMock()
|
|
|
|
factory = ssdp.SSDPFactory()
|
|
|
|
with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log):
|
|
|
|
data = {'some': 'data'}
|
|
|
|
addr = '10.10.10.10'
|
|
|
|
factory._sendto(data=data, addr=addr)
|
|
|
|
assert factory.transport.sendto.called
|
|
|
|
assert factory.transport.sendto.mock_calls[0][1][0]['some'] == 'data'
|
|
|
|
assert factory.transport.sendto.mock_calls[0][2]['addr'] == '10.10.10.10'
|
|
|
|
assert factory.log.debug.called
|
|
|
|
assert factory.log.debug.mock_calls[0][1][0] == 'Sent successfully'
|
2018-01-19 14:56:02 +00:00
|
|
|
|
|
|
|
@patch('salt.utils.ssdp.time.sleep', MagicMock())
|
|
|
|
def test_transport_sendto_retry(self):
|
|
|
|
'''
|
|
|
|
Test transport send_to.
|
|
|
|
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
transport = MagicMock()
|
|
|
|
transport.sendto = MagicMock(side_effect=SSDPBaseTestCase.exception_attr_error)
|
|
|
|
log = MagicMock()
|
|
|
|
factory = ssdp.SSDPFactory()
|
|
|
|
with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log):
|
|
|
|
data = {'some': 'data'}
|
|
|
|
addr = '10.10.10.10'
|
|
|
|
factory._sendto(data=data, addr=addr)
|
|
|
|
assert factory.transport.sendto.called
|
|
|
|
assert ssdp.time.sleep.called
|
|
|
|
assert ssdp.time.sleep.call_args[0][0] > 0 and ssdp.time.sleep.call_args[0][0] < 0.5
|
|
|
|
assert factory.log.debug.called
|
|
|
|
assert 'Permission error' in factory.log.debug.mock_calls[0][1][0]
|
2018-01-19 17:22:15 +00:00
|
|
|
|
|
|
|
def test_datagram_received_bad_signature(self):
|
|
|
|
'''
|
|
|
|
Test datagram_received on bad signature
|
|
|
|
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
factory = ssdp.SSDPFactory()
|
|
|
|
data = 'nonsense'
|
|
|
|
addr = '10.10.10.10', 'foo.suse.de'
|
|
|
|
|
|
|
|
with patch.object(factory, 'log', MagicMock()):
|
|
|
|
factory.datagram_received(data=data, addr=addr)
|
|
|
|
assert factory.log.debug.called
|
|
|
|
assert 'Received bad signature from' in factory.log.debug.call_args[0][0]
|
|
|
|
assert factory.log.debug.call_args[0][1] == addr[0]
|
|
|
|
assert factory.log.debug.call_args[0][2] == addr[1]
|