diff --git a/plugins/module_utils/nsxt_base_resource.py b/plugins/module_utils/nsxt_base_resource.py index c219b3dc..5c9b7e5b 100644 --- a/plugins/module_utils/nsxt_base_resource.py +++ b/plugins/module_utils/nsxt_base_resource.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2018 VMware, Inc. @@ -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: @@ -872,4 +915,4 @@ def _clean_none_resource_params(self, existing_params, resource_params): resource_params.pop(key) for k, v in resource_params.items(): if type(v).__name__ == 'dict': - self._clean_none_resource_params(existing_params, v) + self._clean_none_resource_params(existing_params, v) \ No newline at end of file diff --git a/plugins/modules/nsxt_policy_tier0.py b/plugins/modules/nsxt_policy_tier0.py index 6129d125..75ad036d 100644 --- a/plugins/modules/nsxt_policy_tier0.py +++ b/plugins/modules/nsxt_policy_tier0.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2018 VMware, Inc. @@ -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 @@ -1452,23 +1462,19 @@ def update_resource_params(self, nsx_resource_params): DHCP_RELAY_CONFIG_URL + "/" + dhcp_config_id] if 'vrf_config' in nsx_resource_params: - # vrf config is attached vrf_config = nsx_resource_params['vrf_config'] - vrf_id = vrf_config.get('id') - vrf_display_name = vrf_config.get('display_name') - if not (vrf_display_name or vrf_id): - self.exit_with_failure(msg="Please specify either the ID or " - "display_name of the VRF in the " - "vrf_config using id or display_name") - + # Only perform actions related to tier0 if tier0_id or tier0_display_name is provided tier0_id = vrf_config.pop('tier0_id', None) - if not tier0_id: - tier0_id = self.get_id_using_attr_name_else_fail( - 'tier0', vrf_config, NSXTTier0.get_resource_base_url(), - 'Tier0') - vrf_config['tier0_path'] = ( - NSXTTier0.get_resource_base_url() + "/" + tier0_id) + tier0_display_name = vrf_config.pop('tier0_display_name', None) + + if tier0_id or tier0_display_name: + if not tier0_id: + tier0_id = self.get_id_using_attr_name_else_fail( + 'tier0', vrf_config, NSXTTier0.get_resource_base_url(), + 'Tier0') + vrf_config['tier0_path'] = ( + NSXTTier0.get_resource_base_url() + "/" + tier0_id) vrf_config['resource_type'] = 'Tier0VrfConfig' @@ -1690,6 +1696,12 @@ def get_resource_spec(): elements='str', required=False ), + destinations=dict( + type='list', + elements='str', + required=False, + default=["BGP"], + ), ) ) ) @@ -1905,39 +1917,38 @@ def update_resource_params(self, nsx_resource_params): ipv6_profile_paths = [] if self.do_resource_params_have_attr_with_id_or_display_name( "ipv6_ndra_profile"): - ipv6_ndra_profile_id = ( - self.get_id_using_attr_name_else_fail( - "ipv6_ndra_profile", nsx_resource_params, - IPV6_NDRA_PROFILE_URL, "Ipv6NdraProfile")) + ipv6_ndra_profile_id = self.get_id_using_attr_name_else_fail( + "ipv6_ndra_profile", nsx_resource_params, + IPV6_NDRA_PROFILE_URL, "Ipv6NdraProfile") ipv6_profile_paths.append( IPV6_NDRA_PROFILE_URL + "/" + ipv6_ndra_profile_id) if ipv6_profile_paths: - nsx_resource_params[ - "ipv6_profile_paths"] = ipv6_profile_paths + nsx_resource_params["ipv6_profile_paths"] = ipv6_profile_paths - # segment_id is a required attr + # Ensure segment_id is set correctly segment_id = self.get_id_using_attr_name_else_fail( "segment", nsx_resource_params, SEGMENT_URL, "Segment") nsx_resource_params["segment_path"] = ( SEGMENT_URL + "/" + segment_id) - # edge_node_info is a required attr - edge_node_info = nsx_resource_params.pop("edge_node_info") - site_id = edge_node_info.get("site_id", "default") - enforcementpoint_id = edge_node_info.get( - "enforcementpoint_id", "default") - edge_cluster_base_url = ( - EDGE_CLUSTER_URL.format(site_id, enforcementpoint_id)) - edge_cluster_id = self.get_id_using_attr_name_else_fail( - "edge_cluster", edge_node_info, - edge_cluster_base_url, "Edge Cluster") - edge_node_base_url = EDGE_NODE_URL.format( - site_id, enforcementpoint_id, edge_cluster_id) - edge_node_id = self.get_id_using_attr_name_else_fail( - "edge_node", edge_node_info, edge_node_base_url, - 'Edge Node') - nsx_resource_params["edge_path"] = ( - edge_node_base_url + "/" + edge_node_id) + # Check if edge_node_info exists before using it + edge_node_info = nsx_resource_params.get("edge_node_info") + if edge_node_info: + site_id = edge_node_info.get("site_id", "default") + enforcementpoint_id = edge_node_info.get( + "enforcementpoint_id", "default") + edge_cluster_base_url = ( + EDGE_CLUSTER_URL.format(site_id, enforcementpoint_id)) + edge_cluster_id = self.get_id_using_attr_name_else_fail( + "edge_cluster", edge_node_info, + edge_cluster_base_url, "Edge Cluster") + edge_node_base_url = EDGE_NODE_URL.format( + site_id, enforcementpoint_id, edge_cluster_id) + edge_node_id = self.get_id_using_attr_name_else_fail( + "edge_node", edge_node_info, edge_node_base_url, + 'Edge Node') + nsx_resource_params["edge_path"] = ( + edge_node_base_url + "/" + edge_node_id) class NSXTTier0LocaleServiceBGP(NSXTBaseRealizableResource): def __init__(self): @@ -2149,4 +2160,4 @@ def get_resource_base_url(parent_info): if __name__ == '__main__': nsxt_tier0 = NSXTTier0() - nsxt_tier0.realize() + nsxt_tier0.realize() \ No newline at end of file diff --git a/plugins/modules/nsxt_policy_tier1.py b/plugins/modules/nsxt_policy_tier1.py index 1731ed64..6a665f38 100644 --- a/plugins/modules/nsxt_policy_tier1.py +++ b/plugins/modules/nsxt_policy_tier1.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2018 VMware, Inc. @@ -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"], + ), ) ) ) @@ -1342,4 +1358,4 @@ def update_resource_params(self, nsx_resource_params): if __name__ == '__main__': nsxt_tier1 = NSXTTier1() - nsxt_tier1.realize() + nsxt_tier1.realize() \ No newline at end of file