diff --git a/plugins/module_utils/nsxt_base_resource.py b/plugins/module_utils/nsxt_base_resource.py index c219b3dc..899ca038 100644 --- a/plugins/module_utils/nsxt_base_resource.py +++ b/plugins/module_utils/nsxt_base_resource.py @@ -25,6 +25,8 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +import collections + import sys if sys.version_info[0] < 3: raise Exception("Must be using Python 3") @@ -257,6 +259,18 @@ def update_resource_params(self, nsx_resource_params): # Should be overridden in the subclass if needed pass + def convert_to_ordered_data(self, data): + if isinstance(data, list): + data = sorted([self.convert_to_ordered_data(d) for d in data], + key=str) + elif isinstance(data, dict): + # sort dict with keys + data = collections.OrderedDict( + sorted(data.items(), key=lambda d: d[0])) + for key in data: + data[key] = self.convert_to_ordered_data(data[key]) + return data + def check_for_update(self, existing_params, resource_params): """ resource_params: dict @@ -274,22 +288,23 @@ def check_for_update(self, existing_params, resource_params): for k, v in resource_params.items(): if k not in existing_params: return True - elif type(v).__name__ == 'dict': + elif isinstance(v, collections.OrderedDict): if self.check_for_update(existing_params[k], v): return True elif v != existing_params[k]: - def compare_lists(list1, list2): + def do_lists_differ(list1, list2): # Returns True if list1 and list2 differ - try: - # If the lists can be converted into sets, do so and - # compare lists as sets. - set1 = set(list1) - set2 = set(list2) - return set1 != set2 - except Exception: + if len(list1) != len(list2): return True - if type(v).__name__ == 'list': - if compare_lists(v, existing_params[k]): + for e1, e2 in zip(list1, list2): + if isinstance(e1, collections.OrderedDict): + return self.check_for_update(e1, e2) + elif isinstance(e1, list): + return do_lists_differ(e1, e2) + else: + return e1 != e2 + if isinstance(v, list): + if do_lists_differ(existing_params[k], v): return True continue return True @@ -549,19 +564,47 @@ def _get_base_arg_spec_of_resource(self): def _extract_nsx_resource_params(self, resource_params): # extract the params belonging to this resource only. filtered_params = {} - - def filter_with_spec(spec): + def filter_with_spec(spec, filtered_params, resource_params): for key in spec.keys(): if (key in resource_params and resource_params[key] is not None): - filtered_params[key] = resource_params[key] + v = resource_params[key] + current_spec = spec[key] + current_spec_type = current_spec.get("type") + if current_spec_type == 'dict': + params = filter_with_spec( + current_spec, {}, v) + if params: + filtered_params[key] = params + elif current_spec_type == 'list' and v: + if current_spec.get("elements") == "dict": + all_params = [] + for element in v: + params = filter_with_spec( + current_spec['options'], {}, element) + if params: + all_params.append(params) + if all_params: + filtered_params[key] = all_params + else: + filtered_params[key] = v + else: + filtered_params[key] = v + return filtered_params - filter_with_spec(self.get_resource_spec()) - filter_with_spec(self._get_base_arg_spec_of_nsx_resource()) + filtered_params = filter_with_spec( + self.get_resource_spec(), filtered_params, resource_params) + filtered_params = filter_with_spec( + self._get_base_arg_spec_of_nsx_resource(), filtered_params, + resource_params) return filtered_params def _achieve_present_state(self, successful_resource_exec_logs): self.update_resource_params(self.nsx_resource_params) + self.nsx_resource_params = self.convert_to_ordered_data( + self.nsx_resource_params) + self.existing_resource = self.convert_to_ordered_data( + self.existing_resource) is_resource_updated = self.check_for_update( self.existing_resource, self.nsx_resource_params) if not is_resource_updated: diff --git a/plugins/modules/nsxt_policy_tier0.py b/plugins/modules/nsxt_policy_tier0.py index 6129d125..298fe682 100644 --- a/plugins/modules/nsxt_policy_tier0.py +++ b/plugins/modules/nsxt_policy_tier0.py @@ -683,6 +683,16 @@ IPSec VPN local-endpoint subnets advertised by TIER1. type: list + destinations: + description: + - List of destinations for a given + redistribution rule + - Each rule can have more than one + destination. If destinations not + specified for a given rule, default + destination will be BGP + default: ["BGP"] + choices: ["BGP", "OSPF"] ha_vip_configs: type: list elements: dict @@ -1690,6 +1700,12 @@ def get_resource_spec(): elements='str', required=False ), + destinations=dict( + type='list', + elements='str', + required=False, + default=["BGP"], + ), ) ) ) diff --git a/plugins/modules/nsxt_policy_tier1.py b/plugins/modules/nsxt_policy_tier1.py index 1731ed64..8cf3aa99 100644 --- a/plugins/modules/nsxt_policy_tier1.py +++ b/plugins/modules/nsxt_policy_tier1.py @@ -579,6 +579,16 @@ IPSec VPN local-endpoint subnets advertised by TIER1. type: list + destinations: + description: + - List of destinations for a given + redistribution rule + - Each rule can have more than one + destination. If destinations not + specified for a given rule, default + destination will be BGP + default: ["BGP"] + choices: ["BGP", "OSPF"] ha_vip_configs: type: list elements: dict @@ -1130,6 +1140,12 @@ def get_resource_spec(): elements='str', required=False ), + destinations=dict( + type='list', + elements='str', + required=False, + default=["BGP"], + ), ) ) ) diff --git a/tests/unit/plugins/module_utils/test_nsxt_base_resource.py b/tests/unit/plugins/module_utils/test_nsxt_base_resource.py index 72a8fc5c..53668097 100644 --- a/tests/unit/plugins/module_utils/test_nsxt_base_resource.py +++ b/tests/unit/plugins/module_utils/test_nsxt_base_resource.py @@ -18,6 +18,7 @@ # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import collections import unittest import json @@ -624,6 +625,24 @@ def test_detached_delete_child(): nsxt_base_resource.BASE_RESOURCES = init_base_resources + def test_convert_to_ordered_data(self): + simple_dummy_resource = SimpleDummyNSXTResource() + resource_params = {"dummy": ["dummy2", "dummy1"]} + observed = simple_dummy_resource.convert_to_ordered_data( + resource_params) + expected = collections.OrderedDict({"dummy": ["dummy1", "dummy2"]}) + self.assertEqual(observed, expected) + + resource_params = {"dummy2": {"dummy1": "dummy", "dummy2": "dummy"}, + "dummy1": {"dummy4": "dummy", "dummy2": "dummy"}} + observed = simple_dummy_resource.convert_to_ordered_data( + resource_params) + expected = collections.OrderedDict( + {"dummy1": collections.OrderedDict( + {"dummy2": "dummy", "dummy4": "dummy"}), + "dummy2": {"dummy1": "dummy", "dummy2": "dummy"}}) + self.assertEqual(observed, expected) + def test_check_for_update(self): simple_dummy_resource = SimpleDummyNSXTResource() @@ -634,6 +653,10 @@ def test_with_no_existing_resource(): def test_with_same_params(): existing_params = {"dummy": "dummy"} resource_params = {"dummy": "dummy"} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertFalse(simple_dummy_resource.check_for_update( existing_params, resource_params)) @@ -641,6 +664,10 @@ def test_with_same_params(): def test_with_diff_params_simple(): existing_params = {"dummy": "dummy"} resource_params = {"dummy1": "dummy"} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) @@ -648,13 +675,21 @@ def test_with_diff_params_simple(): def test_with_same_params_list_same_order(): existing_params = {"dummy": ["dummy1", "dummy2"]} resource_params = {"dummy": ["dummy1", "dummy2"]} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertFalse(simple_dummy_resource.check_for_update( existing_params, resource_params)) def test_with_same_params_list_different_order(): - existing_params = {"dummy": ["dummy1", "dummy2"]} - resource_params = {"dummy": ["dummy2", "dummy1"]} + existing_params = {"dummy": ["dummy1", "dummy2", "dummy3"]} + resource_params = {"dummy": ["dummy2", "dummy1", "dummy3"]} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertFalse(simple_dummy_resource.check_for_update( existing_params, resource_params)) @@ -662,6 +697,10 @@ def test_with_same_params_list_different_order(): def test_with_same_params_single_dict(): existing_params = {"dummy": {"dummy": "dummy"}} resource_params = {"dummy": {"dummy": "dummy"}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertFalse(simple_dummy_resource.check_for_update( existing_params, resource_params)) @@ -669,18 +708,30 @@ def test_with_same_params_single_dict(): def test_with_diff_params_single_dict(): existing_params = {"dummy": {"dummy": "dummy"}} resource_params = {"dummy": {"dummy1": "dummy"}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) existing_params = {"dummy": {"dummy": "dummy"}} resource_params = {"dummy": {"dummy": "dummy1"}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) existing_params = {"dummy": {"dummy": "dummy"}} resource_params = {"dummy1": {"dummy": "dummy"}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) @@ -688,6 +739,10 @@ def test_with_diff_params_single_dict(): def test_with_same_params_multilevel_dict(): existing_params = {"dummy": {"dummy": {"dummy": "dummy"}}} resource_params = {"dummy": {"dummy": {"dummy": "dummy"}}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertFalse(simple_dummy_resource.check_for_update( existing_params, resource_params)) @@ -695,24 +750,40 @@ def test_with_same_params_multilevel_dict(): def test_with_diff_params_multilevel_dict(): existing_params = {"dummy": {"dummy": {"dummy": "dummy"}}} resource_params = {"dummy1": {"dummy": {"dummy": "dummy"}}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) existing_params = {"dummy": {"dummy": {"dummy": "dummy"}}} resource_params = {"dummy": {"dummy1": {"dummy": "dummy"}}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) existing_params = {"dummy": {"dummy": {"dummy": "dummy"}}} resource_params = {"dummy": {"dummy": {"dummy1": "dummy"}}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params)) existing_params = {"dummy": {"dummy": {"dummy": "dummy"}}} resource_params = {"dummy": {"dummy": {"dummy": "dummy1"}}} + existing_params = simple_dummy_resource.convert_to_ordered_data( + existing_params) + resource_params = simple_dummy_resource.convert_to_ordered_data( + resource_params) self.assertTrue(simple_dummy_resource.check_for_update( existing_params, resource_params))