1# -*- coding: utf-8 -*-
2
3#
4# Dell EMC OpenManage Ansible Modules
5# Version 3.1.0
6# Copyright (C) 2019-2021 Dell Inc. or its subsidiaries. All Rights Reserved.
7
8# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
9#
10
11from __future__ import (absolute_import, division, print_function)
12
13__metaclass__ = type
14
15import json
16
17import pytest
18from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
19from ansible.module_utils.urls import ConnectionError, SSLValidationError
20from io import StringIO
21from ansible.module_utils._text import to_text
22from ssl import SSLError
23from ansible_collections.dellemc.openmanage.plugins.modules import ome_application_network_proxy
24from ansible_collections.dellemc.openmanage.tests.unit.plugins.modules.common import FakeAnsibleModule, Constants
25
26MODULE_PATH = 'ansible_collections.dellemc.openmanage.plugins.modules.'
27CHECK_MODE_CHANGE_FOUND_MSG = "Changes found to be applied."
28CHECK_MODE_CHANGE_NOT_FOUND_MSG = "No Changes found to be applied."
29
30
31@pytest.fixture
32def ome_connection_mock_for_application_network_proxy(mocker, ome_response_mock):
33    connection_class_mock = mocker.patch(MODULE_PATH + 'ome_application_network_proxy.RestOME')
34    ome_connection_mock_obj = connection_class_mock.return_value.__enter__.return_value
35    ome_connection_mock_obj.invoke_request.return_value = ome_response_mock
36    ome_connection_mock_obj.get_all_report_details.return_value = {"report_list": []}
37    return ome_connection_mock_obj
38
39
40class TestOmeTemplate(FakeAnsibleModule):
41    module = ome_application_network_proxy
42
43    sub_param1 = {"enable_proxy": True, "ip_address": "255.0.0.0", "proxy_port": 443, "proxy_username": "username",
44                  "proxy_password": "password",
45                  "enable_authentication": True}
46    sub_param2 = {"enable_proxy": False}
47
48    @pytest.mark.parametrize("sub_param", [sub_param1, sub_param2])
49    def test_ome_application_network_proxy_main_success_case_01(self, mocker, ome_default_args, sub_param,
50                                                                ome_connection_mock_for_application_network_proxy,
51                                                                ome_response_mock):
52        ome_default_args.update(sub_param)
53        mocker.patch(MODULE_PATH + "ome_application_network_proxy.get_payload", return_value={"key": "val"})
54        mocker.patch(MODULE_PATH + "ome_application_network_proxy.get_updated_payload", return_value={"key": "val"})
55        ome_response_mock.json_data = {"EnableProxy": True, "IpAddress": "255.0.0.0", "PortNumber": 443,
56                                       "Username": "username", "Password": "password", "EnableAuthentication": True}
57        result = self.execute_module(ome_default_args)
58        assert result['changed'] is True
59        assert "msg" in result
60        assert "proxy_configuration" in result and result["proxy_configuration"] == {"EnableProxy": True,
61                                                                                     "IpAddress": "255.0.0.0",
62                                                                                     "PortNumber": 443,
63                                                                                     "Username": "username",
64                                                                                     "Password": "password",
65                                                                                     "EnableAuthentication": True}
66        assert result["msg"] == "Successfully updated network proxy configuration."
67
68    sub_param1 = {"param": {"enable_proxy": True, "ip_address": "255.0.0.0"},
69                  "msg": 'enable_proxy is True but all of the following are missing: proxy_port'}
70    sub_param2 = {"param": {"enable_proxy": True, "proxy_port": 443},
71                  "msg": 'enable_proxy is True but all of the following are missing: ip_address'}
72    sub_param3 = {"param": {"enable_proxy": True},
73                  "msg": 'enable_proxy is True but all of the following are missing: ip_address, proxy_port'}
74    sub_param4 = {"param": {}, "msg": 'missing required arguments: enable_proxy'}
75
76    @pytest.mark.parametrize("param", [sub_param1, sub_param2, sub_param3, sub_param4])
77    def test_ome_application_network_proxy_main_failure_case_01(self, mocker, ome_default_args, param,
78                                                                ome_connection_mock_for_application_network_proxy,
79                                                                ome_response_mock):
80        sub_param = param["param"]
81        msg = param["msg"]
82        ome_default_args.update(sub_param)
83        result = self._run_module_with_fail_json(ome_default_args)
84        assert result["msg"] == msg
85        assert "proxy_configuration" not in result
86        assert result["failed"] is True
87
88    sub_param1 = {
89        "param": {"enable_proxy": True, "proxy_port": 443, "ip_address": "255.0.0.0", "enable_authentication": True,
90                  "proxy_username": "255.0.0.0"},
91        "msg": 'enable_authentication is True but all of the following are missing: proxy_password'}
92    sub_param2 = {
93        "param": {"enable_proxy": True, "proxy_port": 443, "ip_address": "255.0.0.0", "enable_authentication": True,
94                  "proxy_password": 443},
95        "msg": 'enable_authentication is True but all of the following are missing: proxy_username'}
96    sub_param3 = {
97        "param": {"enable_proxy": True, "proxy_port": 443, "ip_address": "255.0.0.0", "enable_authentication": True},
98        "msg": 'enable_authentication is True but all of the following are missing: proxy_username, proxy_password'}
99
100    @pytest.mark.parametrize("param", [sub_param1, sub_param2, sub_param3])
101    def test_ome_application_network_proxy_main_failure_case_02(self, mocker, ome_default_args, param,
102                                                                ome_connection_mock_for_application_network_proxy,
103                                                                ome_response_mock):
104        sub_param = param["param"]
105        msg = param["msg"]
106        ome_default_args.update(sub_param)
107        result = self._run_module_with_fail_json(ome_default_args)
108        assert result["msg"] == msg
109        assert "proxy_configuration" not in result
110        assert result["failed"] is True
111
112    @pytest.mark.parametrize("exc_type",
113                             [IOError, ValueError, SSLError, TypeError, ConnectionError, HTTPError, URLError])
114    def test_ome_application_network_proxy_main_success_failure_case3(self, exc_type, mocker, ome_default_args,
115                                                                      ome_connection_mock_for_application_network_proxy,
116                                                                      ome_response_mock):
117        ome_default_args.update({"enable_proxy": False})
118        json_str = to_text(json.dumps({"info": "error_details"}))
119        if exc_type == URLError:
120            mocker.patch(MODULE_PATH + 'ome_application_network_proxy.get_payload',
121                         side_effect=exc_type("TEST"))
122            result = self._run_module(ome_default_args)
123            assert result["unreachable"] is True
124        elif exc_type not in [HTTPError, SSLValidationError]:
125            mocker.patch(MODULE_PATH + 'ome_application_network_proxy.get_payload',
126                         side_effect=exc_type("exception message"))
127            result = self._run_module_with_fail_json(ome_default_args)
128            assert result['failed'] is True
129        else:
130            mocker.patch(MODULE_PATH + 'ome_application_network_proxy.get_payload',
131                         side_effect=exc_type('http://testhost.com', 400,
132                                              'http error message',
133                                              {"accept-type": "application/json"},
134                                              StringIO(json_str)))
135            result = self._run_module_with_fail_json(ome_default_args)
136            assert result['failed'] is True
137        assert 'proxy_configuration' not in result
138        assert 'msg' in result
139
140    def test_remove_unwanted_keys(self, ome_default_args):
141        removable_keys = list(ome_default_args.keys())
142        new_param = {
143            "ip_address": "IpAddress",
144            "proxy_port": "PortNumber",
145            "enable_proxy": "EnableProxy",
146            "proxy_username": "Username",
147            "proxy_password": "Password",
148            "enable_authentication": "EnableAuthentication"
149        }
150        ome_default_args.update(new_param)
151        self.module.remove_unwanted_keys(removable_keys, ome_default_args)
152        assert len(set(new_param.keys()) - set(ome_default_args.keys())) == 0
153
154    def test_remove_unwanted_keys_case2(self):
155        """when key not exists should not throw error"""
156        current_setting = {"@odata.context": "context", "@odata.type": "data_type", "@odata.id": "@odata.id"}
157        removable_keys = ["@odata.context", "@odata.type", "@odata.id", "Password"]
158        self.module.remove_unwanted_keys(removable_keys, current_setting)
159        assert len(current_setting) == 0
160
161    def test_get_payload(self, ome_default_args):
162        new_param = {
163            "ip_address": "192.168.0.2",
164            "proxy_port": 443,
165            "enable_proxy": True,
166            "proxy_username": "username",
167            "proxy_password": "password",
168            "enable_authentication": False,
169            "port": 443
170        }
171        ome_default_args.update(new_param)
172        f_module = self.get_module_mock(params=ome_default_args)
173        payload = self.module.get_payload(f_module)
174        assert ome_default_args == {"ip_address": "192.168.0.2",
175                                    "proxy_port": 443,
176                                    "enable_proxy": True,
177                                    "proxy_username": "username",
178                                    "proxy_password": "password",
179                                    "enable_authentication": False,
180                                    "hostname": "192.168.0.1",
181                                    "username": "username",
182                                    "password": "password",
183                                    "port": 443}
184        assert payload == {"EnableProxy": True, "IpAddress": "192.168.0.2", "PortNumber": 443, "Username": "username",
185                           "Password": "password", "EnableAuthentication": False}
186
187    def test_get_updated_payload_success_case(self, mocker, ome_default_args, ome_connection_mock_for_application_network_proxy,
188                                              ome_response_mock):
189        current_setting = {"@odata.context": "/api/$metadata#Network.ProxyConfiguration",
190                           "@odata.type": "#Network.ProxyConfiguration",
191                           "@odata.id": "/api/ApplicationService/Network/ProxyConfiguration", "IpAddress": "255.0.0.0",
192                           "PortNumber": 443, "EnableAuthentication": False, "EnableProxy": True,
193                           "Username": "username1", "Password": "password1"}
194        payload = {"EnableAuthentication": True, "IpAddress": "192.168.0.1", "PortNumber": 443, 'EnableProxy': True,
195                   'Username': 'username2', "Password": "password2"}
196        f_module = self.get_module_mock(params=ome_default_args)
197        ome_response_mock.json_data = current_setting
198        mocker.patch(MODULE_PATH + "ome_application_network_proxy.validate_check_mode_for_network_proxy",
199                     return_value=None)
200        setting = self.module.get_updated_payload(ome_connection_mock_for_application_network_proxy, f_module, payload)
201        assert setting == payload
202
203    def test_get_updated_payload_enable_auth_disable_success_case(self, mocker, ome_default_args,
204                                                                  ome_connection_mock_for_application_network_proxy,
205                                                                  ome_response_mock):
206        """when EnableAuthentication is False setting will not have Password and UserName even if its passed"""
207        ome_default_args.update(
208            {"enable_authentication": False, "proxy_username": 'username2', "proxy_password": "password2"})
209        current_setting = {"@odata.context": "/api/$metadata#Network.ProxyConfiguration",
210                           "@odata.type": "#Network.ProxyConfiguration",
211                           "@odata.id": "/api/ApplicationService/Network/ProxyConfiguration", "IpAddress": "255.0.0.0",
212                           "PortNumber": 443, "EnableAuthentication": True, "EnableProxy": True,
213                           "Username": "username1", "Password": "password1"}
214        payload = {"EnableAuthentication": False, "IpAddress": "192.168.0.1", "PortNumber": 443, 'EnableProxy': True,
215                   'Username': 'username2', "Password": "password2"}
216        f_module = self.get_module_mock(params=ome_default_args)
217        ome_response_mock.json_data = current_setting
218        mocker.patch(MODULE_PATH + "ome_application_network_proxy.validate_check_mode_for_network_proxy",
219                     return_value=None)
220        setting = self.module.get_updated_payload(ome_connection_mock_for_application_network_proxy, f_module, payload)
221        assert setting == {"EnableAuthentication": False, "IpAddress": "192.168.0.1", "PortNumber": 443,
222                           'EnableProxy': True}
223
224    def test_get_updated_payload_when_same_setting_failure_case1(self, mocker, ome_default_args,
225                                                                 ome_connection_mock_for_application_network_proxy,
226                                                                 ome_response_mock):
227        current_setting = {"@odata.context": "/api/$metadata#Network.ProxyConfiguration",
228                           "@odata.type": "#Network.ProxyConfiguration",
229                           "@odata.id": "/api/ApplicationService/Network/ProxyConfiguration", "IpAddress": "255.0.0.0",
230                           "PortNumber": 443, "EnableAuthentication": False, "EnableProxy": True,
231                           "Username": "username", "Password": "password"}
232        payload = {"IpAddress": "255.0.0.0", "PortNumber": 443, "EnableAuthentication": False, "EnableProxy": True,
233                   "Username": "username", "Password": "password"}
234        f_module = self.get_module_mock(params=ome_default_args)
235        ome_response_mock.json_data = current_setting
236        error_message = "No changes made to proxy configuration as entered values are the same as current " \
237                        "configuration values."
238        mocker.patch(MODULE_PATH + "ome_application_network_proxy.validate_check_mode_for_network_proxy",
239                     return_value=None)
240        with pytest.raises(Exception, match=error_message) as err:
241            self.module.get_updated_payload(ome_connection_mock_for_application_network_proxy, f_module, payload)
242
243    def test_get_updated_payload_when_same_setting_failure_case2(self, mocker, ome_default_args,
244                                                                 ome_connection_mock_for_application_network_proxy,
245                                                                 ome_response_mock):
246        """Password are ignored for difference check in payload"""
247        current_setting = {"@odata.context": "/api/$metadata#Network.ProxyConfiguration",
248                           "@odata.type": "#Network.ProxyConfiguration",
249                           "@odata.id": "/api/ApplicationService/Network/ProxyConfiguration", "IpAddress": "255.0.0.0",
250                           "PortNumber": 443, "EnableAuthentication": False, "EnableProxy": True,
251                           "Username": "username", "Password": "password1"}
252        payload = {"IpAddress": "255.0.0.0", "PortNumber": 443, "EnableAuthentication": False, "EnableProxy": True,
253                   "Username": "username", "Password": "password2"}
254        f_module = self.get_module_mock(params=ome_default_args)
255        ome_response_mock.json_data = current_setting
256        error_message = "No changes made to proxy configuration as entered values are the same as current " \
257                        "configuration values."
258        mocker.patch(MODULE_PATH + "ome_application_network_proxy.validate_check_mode_for_network_proxy",
259                     return_value=None)
260        with pytest.raises(Exception, match=error_message) as err:
261            self.module.get_updated_payload(ome_connection_mock_for_application_network_proxy, f_module, payload)
262
263    def test_get_updated_payload_when_no_diff_failure_case(self, mocker, ome_default_args,
264                                                           ome_connection_mock_for_application_network_proxy,
265                                                           ome_response_mock):
266        current_setting = {"@odata.context": "/api/$metadata#Network.ProxyConfiguration",
267                           "@odata.type": "#Network.ProxyConfiguration",
268                           "@odata.id": "/api/ApplicationService/Network/ProxyConfiguration", "IpAddress": "255.0.0.0",
269                           "PortNumber": 443, "EnableAuthentication": False, "EnableProxy": True,
270                           "Username": "username", "Password": "password"}
271        payload = {}
272        f_module = self.get_module_mock(params=ome_default_args)
273        ome_response_mock.json_data = current_setting
274        error_message = "Unable to configure the proxy because proxy configuration settings are not provided."
275        mocker.patch(MODULE_PATH + "ome_application_network_proxy.validate_check_mode_for_network_proxy",
276                     return_value=None)
277        with pytest.raises(Exception, match=error_message) as err:
278            self.module.get_updated_payload(ome_connection_mock_for_application_network_proxy, f_module, payload)
279
280    def test_validate_check_mode_for_network_proxy_case01(self, ome_default_args):
281        f_module = self.get_module_mock(params={}, check_mode=True)
282        with pytest.raises(Exception, match=CHECK_MODE_CHANGE_FOUND_MSG):
283            self.module.validate_check_mode_for_network_proxy(True, f_module)
284
285    def test_validate_check_mode_for_network_proxy_case02(self, ome_default_args):
286        f_module = self.get_module_mock(params={}, check_mode=True)
287        with pytest.raises(Exception, match=CHECK_MODE_CHANGE_NOT_FOUND_MSG):
288            self.module.validate_check_mode_for_network_proxy(False, f_module)
289
290    def test_validate_check_mode_for_network_proxy_case03(self, ome_default_args):
291        f_module = self.get_module_mock(params={}, check_mode=False)
292        self.module.validate_check_mode_for_network_proxy(True, f_module)
293
294    def test_validate_check_mode_for_network_proxy_case04(self, ome_default_args):
295        f_module = self.get_module_mock(params={}, check_mode=False)
296        self.module.validate_check_mode_for_network_proxy(False, f_module)
297