From a0c71199746db09dfcc14958c243b4f742597315 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 5 Oct 2023 18:28:44 -0400 Subject: [PATCH 1/6] [minor_change] Add aci_l3out_bgp_protocol_profile as a new module. --- .../modules/aci_l3out_bgp_protocol_profile.py | 333 ++++++++++++++++++ .../modules/aci_l3out_logical_node_profile.py | 2 +- 2 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 plugins/modules/aci_l3out_bgp_protocol_profile.py diff --git a/plugins/modules/aci_l3out_bgp_protocol_profile.py b/plugins/modules/aci_l3out_bgp_protocol_profile.py new file mode 100644 index 000000000..987a2ca43 --- /dev/null +++ b/plugins/modules/aci_l3out_bgp_protocol_profile.py @@ -0,0 +1,333 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} + +DOCUMENTATION = r""" +--- +module: aci_l3out_route_tag_policy +short_description: Manage BGP Protocol Profile (bgp:ProtP) +description: +- Manage BGP Protocol Profile for The Logical Node Profiles on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + l3out: + description: + - The name of an existing L3Out. + type: str + aliases: [ l3out_name ] + node_profile: + description: + - The name of an existing logical node profile. + type: str + aliases: [ node_profile_name, logical_node ] + bgp_protocol_profile: + description: + - The name of the bgp protocol profile. + type: str + aliases: [ name, bgp_protocol_profile_name ] + bgp_timers_policy: + description: + - The name of an existing bgp timers policy. + type: str + aliases: [ bgp_timers_policy_name ] + bgp_best_path_policy: + description: + - The name of the bgp best path control policy. + type: str + aliases: [ bgp_best_path_policy_name ] + description: + description: + - Description for the bgp protocol profile. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(bgp:ProtP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +""" + +EXAMPLES = r""" +- name: Create a l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + tag: 1000 + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: present + delegate_to: localhost + +- name: Delete a l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: absent + delegate_to: localhost + +- name: Query all l3out route tag policies + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: query + delegate_to: localhost + register: query_result +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects + l3out=dict(type="str", aliases=["l3out_name"]), # Not required for querying all objects + node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]), # Not required for querying all objects + bgp_protocol_profile=dict(type="str", aliases=["name", "bgp_protocol_profile_name"]), # Not required for querying all objects + bgp_timers_policy=dict(type="str", aliases=["bgp_timers_policy_name"]), + bgp_best_path_policy=dict(type="str", aliases=["bgp_best_path_policy_name"]), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["tenant", "l3out", "node_profile"]], + ["state", "present", ["tenant", "l3out", "node_profile"]], + ], + ) + + bgp_protocol_profile = module.params.get("bgp_protocol_profile") + bgp_timers_policy = module.params.get("bgp_timers_policy") + bgp_best_path_policy = module.params.get("bgp_best_path_policy") + description = module.params.get("description") + state = module.params.get("state") + tenant = module.params.get("tenant") + l3out = module.params.get("l3out") + node_profile = module.params.get("node_profile") + name_alias = module.params.get("name_alias") + + aci = ACIModule(module) + + child_classes = ["bgpRsBgpNodeCtxPol", "bgpRsBestPathCtrlPol"] + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="l3extOut", + aci_rn="out-{0}".format(l3out), + module_object=l3out, + target_filter={"name": l3out}, + ), + subclass_2=dict( + aci_class="l3extLNodeP", + aci_rn="lnodep-{0}".format(node_profile), + module_object=node_profile, + target_filter={"name": node_profile}, + ), + subclass_3=dict( + aci_class="bgpProtP", + aci_rn="protp", + module_object="", + target_filter={"name": bgp_protocol_profile}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + if state == "present": + child_configs=[] + if bgp_timers_policy is not None: + child_configs.append(dict(bgpRsBgpNodeCtxPol=dict(attributes=dict(tnBgpCtxPolName=bgp_timers_policy)))) + if bgp_best_path_policy is not None: + child_configs.append( + dict(bgpRsBestPathCtrlPo=dict(attributes=dict(tnBgpBestPathCtrlPolName=bgp_timers_policy))) + ) + + aci.payload( + aci_class="bgpProtP", + class_config=dict( + name=bgp_protocol_profile, + descr=description, + nameAlias=name_alias, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class="bgpProtP") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/plugins/modules/aci_l3out_logical_node_profile.py b/plugins/modules/aci_l3out_logical_node_profile.py index d3aa5e8f7..10eefd018 100644 --- a/plugins/modules/aci_l3out_logical_node_profile.py +++ b/plugins/modules/aci_l3out_logical_node_profile.py @@ -12,7 +12,7 @@ DOCUMENTATION = r""" --- module: aci_l3out_logical_node_profile -short_description: Manage Layer 3 Outside (L3Out) logical node profiles (l3extLNodeP:lnodep) +short_description: Manage Layer 3 Outside (L3Out) logical node profiles (l3ext:LNodeP) description: - Manage Layer 3 Outside (L3Out) logical node profiles on Cisco ACI fabrics. options: From cbf12f7dbbb4ae21518199212c38f3c2bf4d5f40 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 6 Oct 2023 10:36:36 -0400 Subject: [PATCH 2/6] [minor_change] Add aci_bgp_timers_policy and aci_bgp_best_path_policy as new modules. --- plugins/modules/aci_bgp_best_path_policy.py | 294 ++++++++++++++++ plugins/modules/aci_bgp_timers_policy.py | 322 ++++++++++++++++++ .../modules/aci_l3out_bgp_protocol_profile.py | 6 +- 3 files changed, 620 insertions(+), 2 deletions(-) create mode 100644 plugins/modules/aci_bgp_best_path_policy.py create mode 100644 plugins/modules/aci_bgp_timers_policy.py diff --git a/plugins/modules/aci_bgp_best_path_policy.py b/plugins/modules/aci_bgp_best_path_policy.py new file mode 100644 index 000000000..4831aee7f --- /dev/null +++ b/plugins/modules/aci_bgp_best_path_policy.py @@ -0,0 +1,294 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} + +DOCUMENTATION = r""" +--- +module: aci_l3out_logical_node_profile +short_description: Manage BGP Best Path policy (bgp:BestPathCtrlPol) +description: +- Manage BGP Best Path policies for Tenants on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + bgp_best_path_policy: + description: + - The name of the best path policy. + type: str + aliases: [ bgp_best_path_policy_name, name ] + best_path_control: + description: + - The option to enable/disable to relax AS-Path restriction when choosing multipaths. + - When enabled, allow load sharing across providers with different AS paths. + type: str + choices: [enable, disable] + aliases: [as_path_control] + description: + description: + - Description for the bgp protocol profile. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(bgp:BestPathCtrlPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +""" + +EXAMPLES = r""" +- name: Create a l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + tag: 1000 + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: present + delegate_to: localhost + +- name: Delete a l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: absent + delegate_to: localhost + +- name: Query all l3out route tag policies + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: query + delegate_to: localhost + register: query_result +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec + +BEST_PATH_CONTROL_MAPPING = dict(enable="asPathMultipathRelax", disable="defaultValue") + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects + bgp_best_path_policy=dict(type="str", aliases=["bgp_best_path_policy_name", "name"]), # Not required for querying all objects + best_path_control=dict(type="str", choices=["enable", "disable"], aliases=["as_path_control"]), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["bgp_best_path_policy", "tenant"]], + ["state", "present", ["bgp_best_path_policy", "tenant"]], + ], + ) + + bgp_best_path_policy = module.params.get("bgp_best_path_policy") + best_path_control = BEST_PATH_CONTROL_MAPPING.get(module.params.get("best_path_control")) + description = module.params.get("description") + state = module.params.get("state") + tenant = module.params.get("tenant") + name_alias = module.params.get("name_alias") + + aci = ACIModule(module) + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="bgpBestPathCtrlPol", + aci_rn="bestpath-{0}".format(bgp_best_path_policy), + module_object=bgp_best_path_policy, + target_filter={"name": bgp_best_path_policy}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="bgpBestPathCtrlPol", + class_config=dict( + name=bgp_best_path_policy, + ctrl=best_path_control, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class="bgpBestPathCtrlPol") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_bgp_timers_policy.py b/plugins/modules/aci_bgp_timers_policy.py new file mode 100644 index 000000000..0f5ee7684 --- /dev/null +++ b/plugins/modules/aci_bgp_timers_policy.py @@ -0,0 +1,322 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} + +DOCUMENTATION = r""" +--- +module: aci_l3out_logical_node_profile +short_description: Manage BGP timers policy (bgp:CtxPol) +description: +- Manage BGP timers policies for Tenants on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + bgp_timers_policy: + description: + - The name of the bgp timers policy. + type: str + aliases: [ bgp_timers_policy_name, name ] + graceful_restart_controls: + description: + - The property to determine whether the entity functions only as a graceful restart helper or whether graceful restart is enabled completely. + - The graceful restart helper option configures the local BGP router to support the graceful restart of a remote BGP peer. + - The complete graceful restart option allows BGP graceful restart to be enabled or disable for an individual neighbor. + type: str + choices: [ helper, complete ] + hold_interval: + description: + - The time period to wait before declaring the neighbor device down. + type: int + keepalive_interval: + description: + - The interval time between sending keepalive messages. + type: int + max_as_limit: + description: + - The maximum AS limit, to discard routes that have excessive AS numbers. + type: int + stale_interval: + description: + - The maximum time that BGP keeps stale routes from the restarting BGP peer. + type: int + description: + description: + - Description for the bgp protocol profile. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(bgp:CtxPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +""" + +EXAMPLES = r""" +- name: Create a l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + tag: 1000 + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: present + delegate_to: localhost + +- name: Delete a l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: absent + delegate_to: localhost + +- name: Query all l3out route tag policies + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific l3out route tag policy + cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + bgp_protocol_profile: my_route_tag_policy + tenant: production + state: query + delegate_to: localhost + register: query_result +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec + +GRACEFUL_RESTART_CONTROLS_MAPPING = dict(helper="helper", complete="") + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects + bgp_timers_policy=dict(type="str", aliases=["bgp_timers_policy_name", "name"]), # Not required for querying all objects + graceful_restart_controls=dict(type="str", choices=["helper", "complete"]), + hold_interval=dict(type="int"), + keepalive_interval=dict(type="int"), + max_as_limit=dict(type="int"), + stale_interval=dict(type="int"), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["bgp_timers_policy", "tenant"]], + ["state", "present", ["bgp_timers_policy", "tenant"]], + ], + ) + + bgp_timers_policy = module.params.get("bgp_timers_policy") + graceful_restart_controls = GRACEFUL_RESTART_CONTROLS_MAPPING.get(module.params.get("graceful_restart_controls")) + hold_interval = module.params.get("hold_interval") + keepalive_interval = module.params.get("keepalive_interval") + max_as_limit = module.params.get("max_as_limit") + stale_interval = module.params.get("stale_interval") + description = module.params.get("description") + state = module.params.get("state") + tenant = module.params.get("tenant") + name_alias = module.params.get("name_alias") + + aci = ACIModule(module) + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="bgpCtxPol", + aci_rn="bgpCtxP-{0}".format(bgp_timers_policy), + module_object=bgp_timers_policy, + target_filter={"name": bgp_timers_policy}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="bgpCtxPol", + class_config=dict( + name=bgp_timers_policy, + grCtrl=graceful_restart_controls, + holdIntvl=hold_interval, + kaIntvl=keepalive_interval, + maxAsLimit=max_as_limit, + staleIntvl=stale_interval, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class="bgpCtxPol") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_l3out_bgp_protocol_profile.py b/plugins/modules/aci_l3out_bgp_protocol_profile.py index 987a2ca43..e2dcfb4dc 100644 --- a/plugins/modules/aci_l3out_bgp_protocol_profile.py +++ b/plugins/modules/aci_l3out_bgp_protocol_profile.py @@ -1,6 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +# Copyright: (c) 2023, Gaspard Micol (@gmicol) # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function @@ -11,7 +12,7 @@ DOCUMENTATION = r""" --- -module: aci_l3out_route_tag_policy +module: aci_l3out_logical_node_profile short_description: Manage BGP Protocol Profile (bgp:ProtP) description: - Manage BGP Protocol Profile for The Logical Node Profiles on Cisco ACI fabrics. @@ -301,6 +302,7 @@ def main(): aci.get_existing() if state == "present": + child_configs=[] if bgp_timers_policy is not None: child_configs.append(dict(bgpRsBgpNodeCtxPol=dict(attributes=dict(tnBgpCtxPolName=bgp_timers_policy)))) @@ -330,4 +332,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From 2391eaa193d0928a5ce54e5b4f2e402397ca08a5 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 6 Oct 2023 13:06:11 -0400 Subject: [PATCH 3/6] [ignore] Add test cases for all modules related to aci_l3out_bgp_protocol_profile. --- plugins/modules/aci_bgp_best_path_policy.py | 30 +-- plugins/modules/aci_bgp_timers_policy.py | 36 ++-- .../modules/aci_l3out_bgp_protocol_profile.py | 64 +++--- .../targets/aci_bgp_best_path_policy/aliases | 2 + .../aci_bgp_best_path_policy/tasks/main.yml | 140 +++++++++++++ .../targets/aci_bgp_timers_policy/aliases | 2 + .../aci_bgp_timers_policy/tasks/main.yml | 155 +++++++++++++++ .../aci_l3out_bgp_protocol_profile/aliases | 2 + .../tasks/main.yml | 184 ++++++++++++++++++ 9 files changed, 549 insertions(+), 66 deletions(-) create mode 100644 tests/integration/targets/aci_bgp_best_path_policy/aliases create mode 100644 tests/integration/targets/aci_bgp_best_path_policy/tasks/main.yml create mode 100644 tests/integration/targets/aci_bgp_timers_policy/aliases create mode 100644 tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml create mode 100644 tests/integration/targets/aci_l3out_bgp_protocol_profile/aliases create mode 100644 tests/integration/targets/aci_l3out_bgp_protocol_profile/tasks/main.yml diff --git a/plugins/modules/aci_bgp_best_path_policy.py b/plugins/modules/aci_bgp_best_path_policy.py index 4831aee7f..a037ae4c1 100644 --- a/plugins/modules/aci_bgp_best_path_policy.py +++ b/plugins/modules/aci_bgp_best_path_policy.py @@ -12,7 +12,7 @@ DOCUMENTATION = r""" --- -module: aci_l3out_logical_node_profile +module: aci_bgp_best_path_policy short_description: Manage BGP Best Path policy (bgp:BestPathCtrlPol) description: - Manage BGP Best Path policies for Tenants on Cisco ACI fabrics. @@ -64,33 +64,33 @@ description: More information about the internal APIC class B(bgp:BestPathCtrlPol). link: https://developer.cisco.com/docs/apic-mim-ref/ author: -- Dag Wieers (@dagwieers) +- Gaspard Micol (@gmicol) """ EXAMPLES = r""" -- name: Create a l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Create a BGP best path policy + cisco.aci.aci_bgp_best_path_policy: host: apic username: admin password: SomeSecretPassword - tag: 1000 - bgp_protocol_profile: my_route_tag_policy + bgp_protocol_profile: my_bgp_best_path_policy + best_path_control: enable tenant: production state: present delegate_to: localhost -- name: Delete a l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Delete a BGP best path policy + cisco.aci.aci_bgp_best_path_policy: host: apic username: admin password: SomeSecretPassword - bgp_protocol_profile: my_route_tag_policy + bgp_protocol_profile: my_bgp_best_path_policy tenant: production state: absent delegate_to: localhost -- name: Query all l3out route tag policies - cisco.aci.aci_l3out_route_tag_policy: +- name: Query all BGP best path policies + cisco.aci.aci_bgp_best_path_policy: host: apic username: admin password: SomeSecretPassword @@ -98,12 +98,12 @@ delegate_to: localhost register: query_result -- name: Query a specific l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Query a specific BGP best path policy + cisco.aci.aci_bgp_best_path_policy: host: apic username: admin password: SomeSecretPassword - bgp_protocol_profile: my_route_tag_policy + bgp_protocol_profile: my_bgp_best_path_policy tenant: production state: query delegate_to: localhost @@ -218,7 +218,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec -BEST_PATH_CONTROL_MAPPING = dict(enable="asPathMultipathRelax", disable="defaultValue") +BEST_PATH_CONTROL_MAPPING = dict(enable="asPathMultipathRelax", disable="") def main(): diff --git a/plugins/modules/aci_bgp_timers_policy.py b/plugins/modules/aci_bgp_timers_policy.py index 0f5ee7684..4dbe434a0 100644 --- a/plugins/modules/aci_bgp_timers_policy.py +++ b/plugins/modules/aci_bgp_timers_policy.py @@ -12,7 +12,7 @@ DOCUMENTATION = r""" --- -module: aci_l3out_logical_node_profile +module: aci_bgp_timers_policy short_description: Manage BGP timers policy (bgp:CtxPol) description: - Manage BGP timers policies for Tenants on Cisco ACI fabrics. @@ -33,7 +33,7 @@ - The graceful restart helper option configures the local BGP router to support the graceful restart of a remote BGP peer. - The complete graceful restart option allows BGP graceful restart to be enabled or disable for an individual neighbor. type: str - choices: [ helper, complete ] + choices: [ helper, complete ] hold_interval: description: - The time period to wait before declaring the neighbor device down. @@ -49,7 +49,7 @@ stale_interval: description: - The maximum time that BGP keeps stale routes from the restarting BGP peer. - type: int + type: int description: description: - Description for the bgp protocol profile. @@ -80,33 +80,37 @@ description: More information about the internal APIC class B(bgp:CtxPol). link: https://developer.cisco.com/docs/apic-mim-ref/ author: -- Dag Wieers (@dagwieers) +- Gaspard Micol (@gmicol) """ EXAMPLES = r""" -- name: Create a l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Create a BGP timers policy + cisco.aci.aci_bgp_timers_policy: host: apic username: admin password: SomeSecretPassword - tag: 1000 - bgp_protocol_profile: my_route_tag_policy + bgp_protocol_profile: my_bgp_timers_policy + graceful_restart_controls: complete + hold_interval: 360 + keepalive_interval: 120 + max_as_limit: 1 + stale_interval: 600 tenant: production state: present delegate_to: localhost -- name: Delete a l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Delete a BGP timers policy + cisco.aci.aci_bgp_timers_policy: host: apic username: admin password: SomeSecretPassword - bgp_protocol_profile: my_route_tag_policy + bgp_protocol_profile: my_bgp_timers_policy tenant: production state: absent delegate_to: localhost -- name: Query all l3out route tag policies - cisco.aci.aci_l3out_route_tag_policy: +- name: Query all BGP timers policies + cisco.aci.aci_bgp_timers_policy: host: apic username: admin password: SomeSecretPassword @@ -114,12 +118,12 @@ delegate_to: localhost register: query_result -- name: Query a specific l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Query a specific BGP timers policy + cisco.aci.aci_bgp_timers_policy: host: apic username: admin password: SomeSecretPassword - bgp_protocol_profile: my_route_tag_policy + bgp_protocol_profile: my_bgp_timers_policy tenant: production state: query delegate_to: localhost diff --git a/plugins/modules/aci_l3out_bgp_protocol_profile.py b/plugins/modules/aci_l3out_bgp_protocol_profile.py index e2dcfb4dc..96a53368a 100644 --- a/plugins/modules/aci_l3out_bgp_protocol_profile.py +++ b/plugins/modules/aci_l3out_bgp_protocol_profile.py @@ -12,7 +12,7 @@ DOCUMENTATION = r""" --- -module: aci_l3out_logical_node_profile +module: aci_l3out_bgp_protocol_profile short_description: Manage BGP Protocol Profile (bgp:ProtP) description: - Manage BGP Protocol Profile for The Logical Node Profiles on Cisco ACI fabrics. @@ -32,11 +32,6 @@ - The name of an existing logical node profile. type: str aliases: [ node_profile_name, logical_node ] - bgp_protocol_profile: - description: - - The name of the bgp protocol profile. - type: str - aliases: [ name, bgp_protocol_profile_name ] bgp_timers_policy: description: - The name of an existing bgp timers policy. @@ -47,11 +42,6 @@ - The name of the bgp best path control policy. type: str aliases: [ bgp_best_path_policy_name ] - description: - description: - - Description for the bgp protocol profile. - type: str - aliases: [ descr ] state: description: - Use C(present) or C(absent) for adding or removing. @@ -69,41 +59,50 @@ - cisco.aci.owner notes: -- The C(tenant) used must exist before using this module in your playbook. - The M(cisco.aci.aci_tenant) module can be used for this. +- The C(tenant), C(l3out) and C(node_profile) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l3out) and M(cisco.aci.aci_l3out_logical_node_profile) modules can be used for this. +- If C(bgp_timers_policy) and/or C(bgp_best_path_policy) are used, they must exist before using this module in your playbook. + The M(cisco.aci.aci_bgp_timers_policy) and M(cisco.aci.aci_bgp_best_path_policy) modules can be used for this. seealso: - module: cisco.aci.aci_tenant +- module: cisco.aci.aci_l3out +- module: cisco.aci.aci_l3out_logical_node_profile +- module: cisco.aci.aci_bgp_timers_policy +- module: cisco.aci.aci_bgp_best_path_policy - name: APIC Management Information Model reference description: More information about the internal APIC class B(bgp:ProtP). link: https://developer.cisco.com/docs/apic-mim-ref/ author: -- Dag Wieers (@dagwieers) +- Gaspard Micol (@gmicol) """ EXAMPLES = r""" -- name: Create a l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Create a l3out BGP protocol profile + cisco.aci.l3out_bgp_protocol_profile: host: apic username: admin password: SomeSecretPassword - tag: 1000 - bgp_protocol_profile: my_route_tag_policy tenant: production + l3out: prod_l3out + node_profile: prod_node_profile + bgp_timers_policy: prod_bgp_timers_policy + bgp_best_path_policy: prod_bgp_best_path_policy state: present delegate_to: localhost -- name: Delete a l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Delete a l3out BGP protocol profile + cisco.aci.l3out_bgp_protocol_profile: host: apic username: admin password: SomeSecretPassword - bgp_protocol_profile: my_route_tag_policy tenant: production + l3out: prod_l3out + node_profile: prod_node_profile state: absent delegate_to: localhost -- name: Query all l3out route tag policies - cisco.aci.aci_l3out_route_tag_policy: +- name: Query all l3out BGP protocol profiles + cisco.aci.l3out_bgp_protocol_profile: host: apic username: admin password: SomeSecretPassword @@ -111,13 +110,14 @@ delegate_to: localhost register: query_result -- name: Query a specific l3out route tag policy - cisco.aci.aci_l3out_route_tag_policy: +- name: Query a specific l3out BGP protocol profile + cisco.aci.l3out_bgp_protocol_profile: host: apic username: admin password: SomeSecretPassword - bgp_protocol_profile: my_route_tag_policy tenant: production + l3out: prod_l3out + node_profile: prod_node_profile state: query delegate_to: localhost register: query_result @@ -240,10 +240,8 @@ def main(): tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects l3out=dict(type="str", aliases=["l3out_name"]), # Not required for querying all objects node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]), # Not required for querying all objects - bgp_protocol_profile=dict(type="str", aliases=["name", "bgp_protocol_profile_name"]), # Not required for querying all objects bgp_timers_policy=dict(type="str", aliases=["bgp_timers_policy_name"]), bgp_best_path_policy=dict(type="str", aliases=["bgp_best_path_policy_name"]), - description=dict(type="str", aliases=["descr"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), name_alias=dict(type="str"), ) @@ -257,10 +255,8 @@ def main(): ], ) - bgp_protocol_profile = module.params.get("bgp_protocol_profile") bgp_timers_policy = module.params.get("bgp_timers_policy") bgp_best_path_policy = module.params.get("bgp_best_path_policy") - description = module.params.get("description") state = module.params.get("state") tenant = module.params.get("tenant") l3out = module.params.get("l3out") @@ -294,7 +290,7 @@ def main(): aci_class="bgpProtP", aci_rn="protp", module_object="", - target_filter={"name": bgp_protocol_profile}, + target_filter={"name": ""}, ), child_classes=child_classes, ) @@ -303,19 +299,17 @@ def main(): if state == "present": - child_configs=[] + child_configs = [] if bgp_timers_policy is not None: child_configs.append(dict(bgpRsBgpNodeCtxPol=dict(attributes=dict(tnBgpCtxPolName=bgp_timers_policy)))) if bgp_best_path_policy is not None: child_configs.append( - dict(bgpRsBestPathCtrlPo=dict(attributes=dict(tnBgpBestPathCtrlPolName=bgp_timers_policy))) + dict(bgpRsBestPathCtrlPol=dict(attributes=dict(tnBgpBestPathCtrlPolName=bgp_best_path_policy))) ) aci.payload( aci_class="bgpProtP", class_config=dict( - name=bgp_protocol_profile, - descr=description, nameAlias=name_alias, ), child_configs=child_configs, diff --git a/tests/integration/targets/aci_bgp_best_path_policy/aliases b/tests/integration/targets/aci_bgp_best_path_policy/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_bgp_best_path_policy/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_bgp_best_path_policy/tasks/main.yml b/tests/integration/targets/aci_bgp_best_path_policy/tasks/main.yml new file mode 100644 index 000000000..d0eae2666 --- /dev/null +++ b/tests/integration/targets/aci_bgp_best_path_policy/tasks/main.yml @@ -0,0 +1,140 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add a BGP best path policy (check_mode) + aci_bgp_best_path_policy: &aci_bgp_best_path_policy_present + <<: *aci_info + tenant: ansible_tenant + bgp_best_path_policy: ansible_bgp_best_path_policy_1 + best_path_control: enable + description: BGP best path policy 1 for ansible_tenant tenant + state: present + check_mode: true + register: cm_add_bgp_best_path_policy + + - name: Add a BGP best path policy (normal_mode) + aci_bgp_best_path_policy: + <<: *aci_bgp_best_path_policy_present + register: nm_add_bgp_best_path_policy + + - name: Add the first BGP best path policy again - testing idempotency + aci_bgp_best_path_policy: + <<: *aci_bgp_best_path_policy_present + register: nm_add_bgp_best_path_policy_idempotency + + - name: Add a second BGP best path policy (normal_mode) + aci_bgp_best_path_policy: + <<: *aci_info + tenant: ansible_tenant + bgp_best_path_policy: ansible_bgp_best_path_policy_2 + best_path_control: disable + description: BGP best path policy 2 for ansible_tenant tenant + state: present + register: nm_add_bgp_best_path_policy_2 + + - name: Asserts for BGP best path policys creation tasks + assert: + that: + - cm_add_bgp_best_path_policy is changed + - cm_add_bgp_best_path_policy.previous == [] + - cm_add_bgp_best_path_policy.current == [] + - nm_add_bgp_best_path_policy is changed + - nm_add_bgp_best_path_policy.current.0.bgpBestPathCtrlPol.attributes.name == "ansible_bgp_best_path_policy_1" + - nm_add_bgp_best_path_policy.current.0.bgpBestPathCtrlPol.attributes.ctrl == "asPathMultipathRelax" + - nm_add_bgp_best_path_policy_idempotency is not changed + - nm_add_bgp_best_path_policy_2 is changed + - nm_add_bgp_best_path_policy_2.previous == [] + - nm_add_bgp_best_path_policy_2.current.0.bgpBestPathCtrlPol.attributes.name == "ansible_bgp_best_path_policy_2" + - nm_add_bgp_best_path_policy_2.current.0.bgpBestPathCtrlPol.attributes.ctrl == "" + + - name: Query all BGP best path policies + aci_bgp_best_path_policy: + <<: *aci_info + state: query + register: query_all_bgp_best_path_policy + + - name: Query ansible_bgp_best_path_policy_1 + aci_bgp_best_path_policy: + <<: *aci_bgp_best_path_policy_present + state: query + register: query_ansible_bgp_best_path_policy_1 + + - name: Asserts query tasks + assert: + that: + - query_all_bgp_best_path_policy is not changed + - query_all_bgp_best_path_policy.current|length >= 2 + - query_ansible_bgp_best_path_policy_1 is not changed + - query_ansible_bgp_best_path_policy_1.current.0.bgpBestPathCtrlPol.attributes.name == "ansible_bgp_best_path_policy_1" + - query_ansible_bgp_best_path_policy_1.current.0.bgpBestPathCtrlPol.attributes.ctrl == "asPathMultipathRelax" + + - name: Remove BGP best path policy (check_mode) + aci_bgp_best_path_policy: &bgp_best_path_policy_absent + <<: *aci_bgp_best_path_policy_present + state: absent + check_mode: true + register: cm_remove_bgp_best_path_policy + + - name: Remove BGP best path policy (normal_mode) + aci_bgp_best_path_policy: + <<: *bgp_best_path_policy_absent + register: nm_remove_bgp_best_path_policy + + - name: Remove BGP best path policy - testing idempotency + aci_bgp_best_path_policy: + <<: *bgp_best_path_policy_absent + register: nm_remove_bgp_best_path_policy_idempotency + + - name: Asserts deletion tasks + assert: + that: + - cm_remove_bgp_best_path_policy is changed + - cm_remove_bgp_best_path_policy.proposed == {} + - nm_remove_bgp_best_path_policy is changed + - nm_remove_bgp_best_path_policy.previous != [] + - nm_remove_bgp_best_path_policy.method == "DELETE" + - nm_remove_bgp_best_path_policy_idempotency is not changed + - nm_remove_bgp_best_path_policy_idempotency.previous == [] + + - name: Remove the ansible_tenant - cleanup before ending tests + aci_tenant: + <<: *aci_tenant_present + state: absent \ No newline at end of file diff --git a/tests/integration/targets/aci_bgp_timers_policy/aliases b/tests/integration/targets/aci_bgp_timers_policy/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_bgp_timers_policy/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml b/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml new file mode 100644 index 000000000..6dc04d60f --- /dev/null +++ b/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml @@ -0,0 +1,155 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add a BGP timers policy (check_mode) + aci_bgp_timers_policy: &aci_bgp_timers_policy_present + <<: *aci_info + tenant: ansible_tenant + bgp_timers_policy: ansible_bgp_timers_policy_1 + graceful_restart_controls: complete + hold_interval: 360 + keepalive_interval: 120 + max_as_limit: 1 + stale_interval: 600 + description: BGP timers policy 1 for ansible_tenant tenant + state: present + check_mode: true + register: cm_add_bgp_timers_policy + + - name: Add a BGP timers policy (normal_mode) + aci_bgp_timers_policy: + <<: *aci_bgp_timers_policy_present + register: nm_add_bgp_timers_policy + + - name: Add the first BGP timers policy again - testing idempotency + aci_bgp_timers_policy: + <<: *aci_bgp_timers_policy_present + register: nm_add_bgp_timers_policy_idempotency + + - name: Add a second BGP timers policy (normal_mode) + aci_bgp_timers_policy: + <<: *aci_info + tenant: ansible_tenant + bgp_timers_policy: ansible_bgp_timers_policy_2 + description: BGP timers policy 2 for ansible_tenant tenant + state: present + register: nm_add_bgp_timers_policy_2 + + - name: Asserts for BGP timers policys creation tasks + assert: + that: + - cm_add_bgp_timers_policy is changed + - cm_add_bgp_timers_policy.previous == [] + - cm_add_bgp_timers_policy.current == [] + - nm_add_bgp_timers_policy is changed + - nm_add_bgp_timers_policy.current.0.bgpCtxPol.attributes.name == "ansible_bgp_timers_policy_1" + - nm_add_bgp_timers_policy.current.0.bgpCtxPol.attributes.grCtrl == "" + - nm_add_bgp_timers_policy.current.0.bgpCtxPol.attributes.holdIntvl == "360" + - nm_add_bgp_timers_policy.current.0.bgpCtxPol.attributes.kaIntvl == "120" + - nm_add_bgp_timers_policy.current.0.bgpCtxPol.attributes.maxAsLimit == "1" + - nm_add_bgp_timers_policy.current.0.bgpCtxPol.attributes.staleIntvl == "600" + - nm_add_bgp_timers_policy_idempotency is not changed + - nm_add_bgp_timers_policy_2 is changed + - nm_add_bgp_timers_policy_2.previous == [] + - nm_add_bgp_timers_policy_2.current.0.bgpCtxPol.attributes.name == "ansible_bgp_timers_policy_2" + - nm_add_bgp_timers_policy_2.current.0.bgpCtxPol.attributes.grCtrl == "helper" + - nm_add_bgp_timers_policy_2.current.0.bgpCtxPol.attributes.holdIntvl == "180" + - nm_add_bgp_timers_policy_2.current.0.bgpCtxPol.attributes.kaIntvl == "60" + - nm_add_bgp_timers_policy_2.current.0.bgpCtxPol.attributes.maxAsLimit == "0" + - nm_add_bgp_timers_policy_2.current.0.bgpCtxPol.attributes.staleIntvl == "default" + + - name: Query all BGP timers policies + aci_bgp_timers_policy: + <<: *aci_info + state: query + register: query_all_bgp_timers_policy + + - name: Query ansible_bgp_timers_policy_1 + aci_bgp_timers_policy: + <<: *aci_bgp_timers_policy_present + state: query + register: query_ansible_bgp_timers_policy_1 + + - name: Asserts query tasks + assert: + that: + - query_all_bgp_timers_policy is not changed + - query_all_bgp_timers_policy.current|length >= 2 + - query_ansible_bgp_timers_policy_1 is not changed + - query_ansible_bgp_timers_policy_1.current.0.bgpCtxPol.attributes.name == "ansible_bgp_timers_policy_1" + - query_ansible_bgp_timers_policy_1.current.0.bgpCtxPol.attributes.grCtrl == "" + - query_ansible_bgp_timers_policy_1.current.0.bgpCtxPol.attributes.holdIntvl == "360" + - query_ansible_bgp_timers_policy_1.current.0.bgpCtxPol.attributes.kaIntvl == "120" + - query_ansible_bgp_timers_policy_1.current.0.bgpCtxPol.attributes.maxAsLimit == "1" + - query_ansible_bgp_timers_policy_1.current.0.bgpCtxPol.attributes.staleIntvl == "600" + + - name: Remove BGP timers policy (check_mode) + aci_bgp_timers_policy: &bgp_timers_policy_absent + <<: *aci_bgp_timers_policy_present + state: absent + check_mode: true + register: cm_remove_bgp_timers_policy + + - name: Remove BGP timers policy (normal_mode) + aci_bgp_timers_policy: + <<: *bgp_timers_policy_absent + register: nm_remove_bgp_timers_policy + + - name: Remove BGP timers policy - testing idempotency + aci_bgp_timers_policy: + <<: *bgp_timers_policy_absent + register: nm_remove_bgp_timers_policy_idempotency + + - name: Asserts deletion tasks + assert: + that: + - cm_remove_bgp_timers_policy is changed + - cm_remove_bgp_timers_policy.proposed == {} + - nm_remove_bgp_timers_policy is changed + - nm_remove_bgp_timers_policy.previous != [] + - nm_remove_bgp_timers_policy.method == "DELETE" + - nm_remove_bgp_timers_policy_idempotency is not changed + - nm_remove_bgp_timers_policy_idempotency.previous == [] + + - name: Remove the ansible_tenant - cleanup before ending tests + aci_tenant: + <<: *aci_tenant_present + state: absent \ No newline at end of file diff --git a/tests/integration/targets/aci_l3out_bgp_protocol_profile/aliases b/tests/integration/targets/aci_l3out_bgp_protocol_profile/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_l3out_bgp_protocol_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_l3out_bgp_protocol_profile/tasks/main.yml b/tests/integration/targets/aci_l3out_bgp_protocol_profile/tasks/main.yml new file mode 100644 index 000000000..85b43726c --- /dev/null +++ b/tests/integration/targets/aci_l3out_bgp_protocol_profile/tasks/main.yml @@ -0,0 +1,184 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add a new l3out + aci_l3out: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + description: Ansible L3Out for ansible_tenant tenant + domain: ansible_dom + vrf: ansible_vrf + state: present + + - name: Add a new logical node profile + aci_l3out_logical_node_profile: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + node_profile: ansible_node_profile_1 + description: Ansible Logical Node Profile 1 for ansible_l3out L3Out + state: present + + - name: Add a second logical node profile + aci_l3out_logical_node_profile: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + node_profile: ansible_node_profile_2 + description: Ansible Logical Node Profile 2 for ansible_l3out L3Out + state: present + + - name: Add a new BGP timers policy + aci_bgp_timers_policy: + <<: *aci_info + tenant: ansible_tenant + bgp_timers_policy: ansible_bgp_timers_policy + description: Ansible BGP timers policy for ansible_tenant tenant + state: present + + - name: Add a new BGP protocol profile + aci_bgp_best_path_policy: + <<: *aci_info + tenant: ansible_tenant + bgp_best_path_policy: ansible_bgp_best_path_policy + description: Ansible BGP protocol profile for ansible_tenant tenant + state: present + + - name: Add a BGP protocol profile (check_mode) + aci_l3out_bgp_protocol_profile: &aci_l3out_bgp_protocol_profile_present + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + node_profile: ansible_node_profile_1 + bgp_timers_policy: ansible_bgp_timers_policy + bgp_best_path_policy: ansible_bgp_best_path_policy + state: present + check_mode: true + register: cm_add_bgp_protocol_profile + + - name: Add a BGP protocol profile (normal_mode) + aci_l3out_bgp_protocol_profile: + <<: *aci_l3out_bgp_protocol_profile_present + register: nm_add_bgp_protocol_profile + + - name: Add the first BGP protocol profile again - testing idempotency + aci_l3out_bgp_protocol_profile: + <<: *aci_l3out_bgp_protocol_profile_present + register: nm_add_bgp_protocol_profile_idempotency + + - name: Add a second BGP protocol profile (normal_mode) + aci_l3out_bgp_protocol_profile: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + node_profile: ansible_node_profile_2 + state: present + register: nm_add_bgp_protocol_profile_2 + + - name: Asserts for BGP protocol profiles creation tasks + assert: + that: + - cm_add_bgp_protocol_profile is changed + - cm_add_bgp_protocol_profile.previous == [] + - cm_add_bgp_protocol_profile.current == [] + - nm_add_bgp_protocol_profile is changed + - nm_add_bgp_protocol_profile.current.0.bgpProtP.children.0.bgpRsBestPathCtrlPol.attributes.tnBgpBestPathCtrlPolName == "ansible_bgp_best_path_policy" + - nm_add_bgp_protocol_profile.current.0.bgpProtP.children.1.bgpRsBgpNodeCtxPol.attributes.tnBgpCtxPolName == "ansible_bgp_timers_policy" + - nm_add_bgp_protocol_profile_idempotency is not changed + - nm_add_bgp_protocol_profile_2 is changed + - nm_add_bgp_protocol_profile_2.previous == [] + + - name: Query all BGP best path policies + aci_l3out_bgp_protocol_profile: + <<: *aci_info + state: query + register: query_all_bgp_protocol_profile + + - name: Query ansible_bgp_protocol_profile_1 + aci_l3out_bgp_protocol_profile: + <<: *aci_l3out_bgp_protocol_profile_present + state: query + register: query_ansible_bgp_protocol_profile_1 + + - name: Asserts query tasks + assert: + that: + - query_all_bgp_protocol_profile is not changed + - query_all_bgp_protocol_profile.current|length >= 2 + - query_ansible_bgp_protocol_profile_1 is not changed + - query_ansible_bgp_protocol_profile_1.current.0.bgpProtP.children.0.bgpRsBestPathCtrlPol.attributes.tDn == "uni/tn-ansible_tenant/bestpath-ansible_bgp_best_path_policy" + - query_ansible_bgp_protocol_profile_1.current.0.bgpProtP.children.0.bgpRsBestPathCtrlPol.attributes.state == "formed" + - query_ansible_bgp_protocol_profile_1.current.0.bgpProtP.children.1.bgpRsBgpNodeCtxPol.attributes.tDn == "uni/tn-ansible_tenant/bgpCtxP-ansible_bgp_timers_policy" + - query_ansible_bgp_protocol_profile_1.current.0.bgpProtP.children.1.bgpRsBgpNodeCtxPol.attributes.state == "formed" + + - name: Remove BGP protocol profile (check_mode) + aci_l3out_bgp_protocol_profile: &bgp_protocol_profile_absent + <<: *aci_l3out_bgp_protocol_profile_present + state: absent + check_mode: true + register: cm_remove_bgp_protocol_profile + + - name: Remove BGP protocol profile (normal_mode) + aci_l3out_bgp_protocol_profile: + <<: *bgp_protocol_profile_absent + register: nm_remove_bgp_protocol_profile + + - name: Remove BGP protocol profile - testing idempotency + aci_l3out_bgp_protocol_profile: + <<: *bgp_protocol_profile_absent + register: nm_remove_bgp_protocol_profile_idempotency + + - name: Asserts deletion tasks + assert: + that: + - cm_remove_bgp_protocol_profile is changed + - cm_remove_bgp_protocol_profile.proposed == {} + - nm_remove_bgp_protocol_profile is changed + - nm_remove_bgp_protocol_profile.previous != [] + - nm_remove_bgp_protocol_profile.method == "DELETE" + - nm_remove_bgp_protocol_profile_idempotency is not changed + - nm_remove_bgp_protocol_profile_idempotency.previous == [] + + - name: Remove the ansible_tenant - cleanup before ending tests + aci_tenant: + <<: *aci_tenant_present + state: absent \ No newline at end of file From 3c06769d416229d64456348826c4067f830eb5a5 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 6 Oct 2023 13:08:38 -0400 Subject: [PATCH 4/6] [ignore] Apply Black for formating python files. --- plugins/modules/aci_l3out_bgp_protocol_profile.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/modules/aci_l3out_bgp_protocol_profile.py b/plugins/modules/aci_l3out_bgp_protocol_profile.py index 96a53368a..d4e0f5358 100644 --- a/plugins/modules/aci_l3out_bgp_protocol_profile.py +++ b/plugins/modules/aci_l3out_bgp_protocol_profile.py @@ -303,9 +303,7 @@ def main(): if bgp_timers_policy is not None: child_configs.append(dict(bgpRsBgpNodeCtxPol=dict(attributes=dict(tnBgpCtxPolName=bgp_timers_policy)))) if bgp_best_path_policy is not None: - child_configs.append( - dict(bgpRsBestPathCtrlPol=dict(attributes=dict(tnBgpBestPathCtrlPolName=bgp_best_path_policy))) - ) + child_configs.append(dict(bgpRsBestPathCtrlPol=dict(attributes=dict(tnBgpBestPathCtrlPolName=bgp_best_path_policy)))) aci.payload( aci_class="bgpProtP", From f56338c0bb7ee1a105ba1869eb28cd5f95ac97a1 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 6 Oct 2023 13:10:23 -0400 Subject: [PATCH 5/6] [ignore] Apply Black again. --- plugins/modules/aci_l3out_bgp_protocol_profile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/modules/aci_l3out_bgp_protocol_profile.py b/plugins/modules/aci_l3out_bgp_protocol_profile.py index d4e0f5358..aacf22d3d 100644 --- a/plugins/modules/aci_l3out_bgp_protocol_profile.py +++ b/plugins/modules/aci_l3out_bgp_protocol_profile.py @@ -298,7 +298,6 @@ def main(): aci.get_existing() if state == "present": - child_configs = [] if bgp_timers_policy is not None: child_configs.append(dict(bgpRsBgpNodeCtxPol=dict(attributes=dict(tnBgpCtxPolName=bgp_timers_policy)))) From 826336a5865acf6d9ca887aaaa22701d19ef8b99 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 12 Oct 2023 13:34:29 -0400 Subject: [PATCH 6/6] [ignore] Moved Mapping dict to constants.py file and Added APIC defaults to documentations. --- plugins/module_utils/constants.py | 4 ++++ plugins/modules/aci_bgp_best_path_policy.py | 6 +++--- plugins/modules/aci_bgp_timers_policy.py | 10 +++++++--- .../targets/aci_bgp_timers_policy/tasks/main.yml | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index 2a6a3e883..aa4c13ae6 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -88,3 +88,7 @@ MATCH_SMU_OPERATION_MAPPING = dict(smu_install="smuInstall", smu_uninstall="smuUninstall") MATCH_SMU_OPERATION_FLAGS_MAPPING = dict(smu_reload_immediate="smuReloadImmediate", smu_reload_skip="smuReloadSkip") + +MATCH_BEST_PATH_CONTROL_MAPPING = dict(enable="asPathMultipathRelax", disable="") + +MATCH_GRACEFUL_RESTART_CONTROLS_MAPPING = dict(helper="helper", complete="") diff --git a/plugins/modules/aci_bgp_best_path_policy.py b/plugins/modules/aci_bgp_best_path_policy.py index a037ae4c1..a8b5c0a6a 100644 --- a/plugins/modules/aci_bgp_best_path_policy.py +++ b/plugins/modules/aci_bgp_best_path_policy.py @@ -31,6 +31,7 @@ description: - The option to enable/disable to relax AS-Path restriction when choosing multipaths. - When enabled, allow load sharing across providers with different AS paths. + - The APIC defaults to C(enable) when unset during creation. type: str choices: [enable, disable] aliases: [as_path_control] @@ -217,8 +218,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec - -BEST_PATH_CONTROL_MAPPING = dict(enable="asPathMultipathRelax", disable="") +from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_BEST_PATH_CONTROL_MAPPING def main(): @@ -244,7 +244,7 @@ def main(): ) bgp_best_path_policy = module.params.get("bgp_best_path_policy") - best_path_control = BEST_PATH_CONTROL_MAPPING.get(module.params.get("best_path_control")) + best_path_control = MATCH_BEST_PATH_CONTROL_MAPPING.get(module.params.get("best_path_control")) description = module.params.get("description") state = module.params.get("state") tenant = module.params.get("tenant") diff --git a/plugins/modules/aci_bgp_timers_policy.py b/plugins/modules/aci_bgp_timers_policy.py index 4dbe434a0..c8d6c54b0 100644 --- a/plugins/modules/aci_bgp_timers_policy.py +++ b/plugins/modules/aci_bgp_timers_policy.py @@ -32,23 +32,28 @@ - The property to determine whether the entity functions only as a graceful restart helper or whether graceful restart is enabled completely. - The graceful restart helper option configures the local BGP router to support the graceful restart of a remote BGP peer. - The complete graceful restart option allows BGP graceful restart to be enabled or disable for an individual neighbor. + - The APIC defaults to C(helper) when unset during creation. type: str choices: [ helper, complete ] hold_interval: description: - The time period to wait before declaring the neighbor device down. + - The APIC defaults to C(180) when unset during creation. type: int keepalive_interval: description: - The interval time between sending keepalive messages. + - The APIC defaults to C(60) when unset during creation. type: int max_as_limit: description: - The maximum AS limit, to discard routes that have excessive AS numbers. + - The APIC defaults to C(0) when unset during creation. type: int stale_interval: description: - The maximum time that BGP keeps stale routes from the restarting BGP peer. + - The APIC defaults to C(default) which is equal to 300 sec when unset during creation. type: int description: description: @@ -237,8 +242,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec - -GRACEFUL_RESTART_CONTROLS_MAPPING = dict(helper="helper", complete="") +from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_GRACEFUL_RESTART_CONTROLS_MAPPING def main(): @@ -268,7 +272,7 @@ def main(): ) bgp_timers_policy = module.params.get("bgp_timers_policy") - graceful_restart_controls = GRACEFUL_RESTART_CONTROLS_MAPPING.get(module.params.get("graceful_restart_controls")) + graceful_restart_controls = MATCH_GRACEFUL_RESTART_CONTROLS_MAPPING.get(module.params.get("graceful_restart_controls")) hold_interval = module.params.get("hold_interval") keepalive_interval = module.params.get("keepalive_interval") max_as_limit = module.params.get("max_as_limit") diff --git a/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml b/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml index 6dc04d60f..0c7cdd77d 100644 --- a/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml +++ b/tests/integration/targets/aci_bgp_timers_policy/tasks/main.yml @@ -152,4 +152,4 @@ - name: Remove the ansible_tenant - cleanup before ending tests aci_tenant: <<: *aci_tenant_present - state: absent \ No newline at end of file + state: absent