diff --git a/lib/ansible/modules/network/nxos/nxos_bgp_af.py b/lib/ansible/modules/network/nxos/nxos_bgp_af.py index f87670e603..ab154a76c1 100644 --- a/lib/ansible/modules/network/nxos/nxos_bgp_af.py +++ b/lib/ansible/modules/network/nxos/nxos_bgp_af.py @@ -16,9 +16,11 @@ # along with Ansible. If not, see . # -ANSIBLE_METADATA = {'metadata_version': '1.0', - 'status': ['preview'], - 'supported_by': 'community'} +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community', +} DOCUMENTATION = ''' @@ -28,221 +30,221 @@ extends_documentation_fragment: nxos version_added: "2.2" short_description: Manages BGP Address-family configuration. description: - - Manages BGP Address-family configurations on NX-OS switches. + - Manages BGP Address-family configurations on NX-OS switches. author: Gabriele Gerbino (@GGabriele) notes: - - C(state=absent) removes the whole BGP ASN configuration - - Default, where supported, restores params default value. + - C(state=absent) removes the whole BGP ASN configuration + - Default, where supported, restores params default value. options: - asn: - description: - - BGP autonomous system number. Valid values are String, - Integer in ASPLAIN or ASDOT notation. - required: true - vrf: - description: - - Name of the VRF. The name 'default' is a valid VRF representing - the global bgp. - required: true - afi: - description: - - Address Family Identifier. - required: true - choices: ['ipv4','ipv6', 'vpnv4', 'vpnv6', 'l2vpn'] - safi: - description: - - Sub Address Family Identifier. - required: true - choices: ['unicast','multicast', 'evpn'] - additional_paths_install: - description: - - Install a backup path into the forwarding table and provide - prefix independent convergence (PIC) in case of a PE-CE link - failure. - required: false - choices: ['true','false'] - default: null - additional_paths_receive: - description: - - Enables the receive capability of additional paths for all of - the neighbors under this address family for which the capability - has not been disabled. - required: false - choices: ['true','false'] - default: null - additional_paths_selection: - description: - - Configures the capability of selecting additional paths for - a prefix. Valid values are a string defining the name of - the route-map. - required: false - default: null - additional_paths_send: - description: - - Enables the send capability of additional paths for all of - the neighbors under this address family for which the capability - has not been disabled. - required: false - choices: ['true','false'] - default: null - advertise_l2vpn_evpn: - description: - - Advertise evpn routes. - required: false - choices: ['true','false'] - default: null - client_to_client: - description: - - Configure client-to-client route reflection. - required: false - choices: ['true','false'] - default: null - dampen_igp_metric: - description: - - Specify dampen value for IGP metric-related changes, in seconds. - Valid values are integer and keyword 'default'. - required: false - default: null - dampening_state: - description: - - Enable/disable route-flap dampening. - required: false - choices: ['true','false'] - default: null - dampening_half_time: - description: - - Specify decay half-life in minutes for route-flap dampening. - Valid values are integer and keyword 'default'. - required: false - default: null - dampening_max_suppress_time: - description: - - Specify max suppress time for route-flap dampening stable route. - Valid values are integer and keyword 'default'. - required: false - default: null - dampening_reuse_time: - description: - - Specify route reuse time for route-flap dampening. - Valid values are integer and keyword 'default'. - required: false - dampening_routemap: - description: - - Specify route-map for route-flap dampening. Valid values are a - string defining the name of the route-map. - required: false - default: null - dampening_suppress_time: - description: - - Specify route suppress time for route-flap dampening. - Valid values are integer and keyword 'default'. - required: false - default: null - default_information_originate: - description: - - Default information originate. - required: false - choices: ['true','false'] - default: null - default_metric: - description: - - Sets default metrics for routes redistributed into BGP. - Valid values are Integer or keyword 'default' - required: false - default: null - distance_ebgp: - description: - - Sets the administrative distance for eBGP routes. - Valid values are Integer or keyword 'default'. - required: false - default: null - distance_ibgp: - description: - - Sets the administrative distance for iBGP routes. - Valid values are Integer or keyword 'default'. - required: false - default: null - distance_local: - description: - - Sets the administrative distance for local BGP routes. - Valid values are Integer or keyword 'default'. - required: false - default: null - inject_map: - description: - - An array of route-map names which will specify prefixes to - inject. Each array entry must first specify the inject-map name, - secondly an exist-map name, and optionally the copy-attributes - keyword which indicates that attributes should be copied from - the aggregate. For example [['lax_inject_map', 'lax_exist_map'], - ['nyc_inject_map', 'nyc_exist_map', 'copy-attributes'], - ['fsd_inject_map', 'fsd_exist_map']]. - required: false - default: null - maximum_paths: - description: - - Configures the maximum number of equal-cost paths for - load sharing. Valid value is an integer in the range 1-64. - default: null - maximum_paths_ibgp: - description: - - Configures the maximum number of ibgp equal-cost paths for - load sharing. Valid value is an integer in the range 1-64. - required: false - default: null - networks: - description: - - Networks to configure. Valid value is a list of network - prefixes to advertise. The list must be in the form of an array. - Each entry in the array must include a prefix address and an - optional route-map. For example [['10.0.0.0/16', 'routemap_LA'], - ['192.168.1.1', 'Chicago'], ['192.168.2.0/24], - ['192.168.3.0/24', 'routemap_NYC']]. - required: false - default: null - next_hop_route_map: - description: - - Configure a route-map for valid nexthops. Valid values are a - string defining the name of the route-map. - required: false - default: null - redistribute: - description: - - A list of redistribute directives. Multiple redistribute entries - are allowed. The list must be in the form of a nested array. - the first entry of each array defines the source-protocol to - redistribute from; the second entry defines a route-map name. - A route-map is highly advised but may be optional on some - platforms, in which case it may be omitted from the array list. - For example [['direct', 'rm_direct'], ['lisp', 'rm_lisp']]. - required: false - default: null - suppress_inactive: - description: - - Advertises only active routes to peers. - required: false - choices: ['true','false'] - default: null - table_map: - description: - - Apply table-map to filter routes downloaded into URIB. - Valid values are a string. - required: false - default: null - table_map_filter: - description: - - Filters routes rejected by the route-map and does not download - them to the RIB. - required: false - choices: ['true','false'] - default: null - state: - description: - - Determines whether the config should be present or not - on the device. - required: false - default: present - choices: ['present','absent'] + asn: + description: + - BGP autonomous system number. Valid values are String, + Integer in ASPLAIN or ASDOT notation. + required: true + vrf: + description: + - Name of the VRF. The name 'default' is a valid VRF representing + the global bgp. + required: true + afi: + description: + - Address Family Identifier. + required: true + choices: ['ipv4','ipv6', 'vpnv4', 'vpnv6', 'l2vpn'] + safi: + description: + - Sub Address Family Identifier. + required: true + choices: ['unicast','multicast', 'evpn'] + additional_paths_install: + description: + - Install a backup path into the forwarding table and provide + prefix independent convergence (PIC) in case of a PE-CE link + failure. + required: false + choices: ['true','false'] + default: null + additional_paths_receive: + description: + - Enables the receive capability of additional paths for all of + the neighbors under this address family for which the capability + has not been disabled. + required: false + choices: ['true','false'] + default: null + additional_paths_selection: + description: + - Configures the capability of selecting additional paths for + a prefix. Valid values are a string defining the name of + the route-map. + required: false + default: null + additional_paths_send: + description: + - Enables the send capability of additional paths for all of + the neighbors under this address family for which the capability + has not been disabled. + required: false + choices: ['true','false'] + default: null + advertise_l2vpn_evpn: + description: + - Advertise evpn routes. + required: false + choices: ['true','false'] + default: null + client_to_client: + description: + - Configure client-to-client route reflection. + required: false + choices: ['true','false'] + default: null + dampen_igp_metric: + description: + - Specify dampen value for IGP metric-related changes, in seconds. + Valid values are integer and keyword 'default'. + required: false + default: null + dampening_state: + description: + - Enable/disable route-flap dampening. + required: false + choices: ['true','false'] + default: null + dampening_half_time: + description: + - Specify decay half-life in minutes for route-flap dampening. + Valid values are integer and keyword 'default'. + required: false + default: null + dampening_max_suppress_time: + description: + - Specify max suppress time for route-flap dampening stable route. + Valid values are integer and keyword 'default'. + required: false + default: null + dampening_reuse_time: + description: + - Specify route reuse time for route-flap dampening. + Valid values are integer and keyword 'default'. + required: false + dampening_routemap: + description: + - Specify route-map for route-flap dampening. Valid values are a + string defining the name of the route-map. + required: false + default: null + dampening_suppress_time: + description: + - Specify route suppress time for route-flap dampening. + Valid values are integer and keyword 'default'. + required: false + default: null + default_information_originate: + description: + - Default information originate. + required: false + choices: ['true','false'] + default: null + default_metric: + description: + - Sets default metrics for routes redistributed into BGP. + Valid values are Integer or keyword 'default' + required: false + default: null + distance_ebgp: + description: + - Sets the administrative distance for eBGP routes. + Valid values are Integer or keyword 'default'. + required: false + default: null + distance_ibgp: + description: + - Sets the administrative distance for iBGP routes. + Valid values are Integer or keyword 'default'. + required: false + default: null + distance_local: + description: + - Sets the administrative distance for local BGP routes. + Valid values are Integer or keyword 'default'. + required: false + default: null + inject_map: + description: + - An array of route-map names which will specify prefixes to + inject. Each array entry must first specify the inject-map name, + secondly an exist-map name, and optionally the copy-attributes + keyword which indicates that attributes should be copied from + the aggregate. For example [['lax_inject_map', 'lax_exist_map'], + ['nyc_inject_map', 'nyc_exist_map', 'copy-attributes'], + ['fsd_inject_map', 'fsd_exist_map']]. + required: false + default: null + maximum_paths: + description: + - Configures the maximum number of equal-cost paths for + load sharing. Valid value is an integer in the range 1-64. + default: null + maximum_paths_ibgp: + description: + - Configures the maximum number of ibgp equal-cost paths for + load sharing. Valid value is an integer in the range 1-64. + required: false + default: null + networks: + description: + - Networks to configure. Valid value is a list of network + prefixes to advertise. The list must be in the form of an array. + Each entry in the array must include a prefix address and an + optional route-map. For example [['10.0.0.0/16', 'routemap_LA'], + ['192.168.1.1', 'Chicago'], ['192.168.2.0/24], + ['192.168.3.0/24', 'routemap_NYC']]. + required: false + default: null + next_hop_route_map: + description: + - Configure a route-map for valid nexthops. Valid values are a + string defining the name of the route-map. + required: false + default: null + redistribute: + description: + - A list of redistribute directives. Multiple redistribute entries + are allowed. The list must be in the form of a nested array. + the first entry of each array defines the source-protocol to + redistribute from; the second entry defines a route-map name. + A route-map is highly advised but may be optional on some + platforms, in which case it may be omitted from the array list. + For example [['direct', 'rm_direct'], ['lisp', 'rm_lisp']]. + required: false + default: null + suppress_inactive: + description: + - Advertises only active routes to peers. + required: false + choices: ['true','false'] + default: null + table_map: + description: + - Apply table-map to filter routes downloaded into URIB. + Valid values are a string. + required: false + default: null + table_map_filter: + description: + - Filters routes rejected by the route-map and does not download + them to the RIB. + required: false + choices: ['true','false'] + default: null + state: + description: + - Determines whether the config should be present or not + on the device. + required: false + default: present + choices: ['present','absent'] ''' EXAMPLES = ''' # configure a simple address-family @@ -256,58 +258,22 @@ EXAMPLES = ''' ''' RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"advertise_l2vpn_evpn": true, "afi": "ipv4", - "asn": "65535", "safi": "unicast", "vrf": "TESTING"} -existing: - description: k/v pairs of existing BGP AF configuration - returned: verbose mode - type: dict - sample: {} -end_state: - description: k/v pairs of BGP AF configuration after module execution - returned: verbose mode - type: dict - sample: {"additional_paths_install": false, - "additional_paths_receive": false, - "additional_paths_selection": "", - "additional_paths_send": false, - "advertise_l2vpn_evpn": true, "afi": "ipv4", - "asn": "65535", "client_to_client": true, - "dampen_igp_metric": "600", "dampening_half_time": "", - "dampening_max_suppress_time": "", "dampening_reuse_time": "", - "dampening_routemap": "", "dampening_state": false, - "dampening_suppress_time": "", - "default_information_originate": false, "default_metric": "", - "distance_ebgp": "20", "distance_ibgp": "200", - "distance_local": "220", "inject_map": [], "maximum_paths": "1", - "maximum_paths_ibgp": "1", "networks": [], - "next_hop_route_map": "", "redistribute": [], "safi": "unicast", - "suppress_inactive": false, "table_map": "", - "table_map_filter": false, "vrf": "TESTING"} -updates: +commands: description: commands sent to the device returned: always type: list sample: ["router bgp 65535", "vrf TESTING", "address-family ipv4 unicast", "advertise l2vpn evpn"] -changed: - description: check to see if a change was made on the device - returned: always - type: boolean - sample: true ''' import re -from ansible.module_utils.nxos import get_config, load_config, run_commands + +from ansible.module_utils.nxos import get_config, load_config from ansible.module_utils.nxos import nxos_argument_spec, check_args from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.netcfg import CustomNetworkConfig -WARNINGS = [] + BOOL_PARAMS = [ 'additional_paths_install', 'additional_paths_receive', @@ -365,24 +331,17 @@ DAMPENING_PARAMS = [ 'dampening_suppress_time', 'dampening_reuse_time', 'dampening_max_suppress_time' - ] - - -def invoke(name, *args, **kwargs): - func = globals().get(name) - if func: - return func(*args, **kwargs) +] def get_custom_list_value(config, arg, module): value_list = [] splitted_config = config.splitlines() if arg == 'inject_map': - REGEX_INJECT = ('.*inject-map\s(?P\S+)' - '\sexist-map\s(?P\S+)-*') + REGEX_INJECT = r'.*inject-map\s(?P\S+)\sexist-map\s(?P\S+)-*' for line in splitted_config: - value = [] + value = [] inject_group = {} try: match_inject = re.match(REGEX_INJECT, line, re.DOTALL) @@ -392,7 +351,7 @@ def get_custom_list_value(config, arg, module): value.append(inject_map) value.append(exist_map) except AttributeError: - value = [] + value = [] if value: copy_attributes = False @@ -401,8 +360,7 @@ def get_custom_list_value(config, arg, module): inject_group['inject_map'], inject_group['exist_map'])) - REGEX = re.compile(r'\s+{0}\s*$'.format( - inject_map_command), re.M) + REGEX = re.compile(r'\s+{0}\s*$'.format(inject_map_command), re.M) try: if REGEX.search(config): copy_attributes = True @@ -417,8 +375,7 @@ def get_custom_list_value(config, arg, module): REGEX_NETWORK = re.compile(r'(?:network\s)(?P.*)$') for line in splitted_config: - value = [] - network_group = {} + value = [] if 'network' in line: value = REGEX_NETWORK.search(line).group('value').split() @@ -431,8 +388,7 @@ def get_custom_list_value(config, arg, module): RED_REGEX = re.compile(r'(?:{0}\s)(?P.*)$'.format( PARAM_TO_COMMAND_KEYMAP[arg]), re.M) for line in splitted_config: - value = [] - redistribute_group = {} + value = [] if 'redistribute' in line: value = RED_REGEX.search(line).group('value').split() if value: @@ -472,8 +428,7 @@ def get_custom_string_value(config, arg, module): if PARAM_TO_COMMAND_KEYMAP[arg] in config: value = REGEX.search(config).group('value') else: - REGEX_DAMPENING = ('.*dampening\s(?P\w+)\s(?P\w+)' - '\s(?P\w+)\s(?P\w+)') + REGEX_DAMPENING = r'.*dampening\s(?P\w+)\s(?P\w+)\s(?P\w+)\s(?P\w+)' try: match_dampening = re.match(REGEX_DAMPENING, config, re.DOTALL) dampening_group = match_dampening.groupdict() @@ -513,49 +468,45 @@ def get_value(arg, config, module): 'redistribute' ] - if arg in BOOL_PARAMS: - REGEX = re.compile(r'\s+{0}\s*$'.format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M) - value = False - try: - if REGEX.search(config): - value = True - except TypeError: - value = False - - elif arg in custom: + if arg in custom: value = get_custom_list_value(config, arg, module) elif (arg.startswith('distance') or arg.startswith('dampening') or - arg.startswith('table_map')): + arg.startswith('table_map')): value = get_custom_string_value(config, arg, module) + elif arg in BOOL_PARAMS: + command_re = re.compile(r'\s+{0}\s*'.format(command), re.M) + value = False + + if command_re.search(config): + value = True + else: - REGEX = re.compile(r'(?:{0}\s)(?P.*)$'.format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M) + command_val_re = re.compile(r'(?:{0}\s)(?P.*)$'.format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M) value = '' - if PARAM_TO_COMMAND_KEYMAP[arg] in config: - value = REGEX.search(config).group('value') + + has_command = command_val_re.search(config) + if has_command: + value = has_command.group('value') + return value -def get_existing(module, args): +def get_existing(module, args, warnings): existing = {} netcfg = CustomNetworkConfig(indent=2, contents=get_config(module)) - try: - asn_regex = '.*router\sbgp\s(?P\d+).*' - match_asn = re.match(asn_regex, str(netcfg), re.DOTALL) - existing_asn_group = match_asn.groupdict() - existing_asn = existing_asn_group['existing_asn'] - except AttributeError: - existing_asn = '' + asn_regex = re.compile(r'.*router\sbgp\s(?P\d+).*', re.DOTALL) + match_asn = asn_regex.match(str(netcfg)) - if existing_asn: + if match_asn: + existing_asn = match_asn.group('existing_asn') parents = ["router bgp {0}".format(existing_asn)] if module.params['vrf'] != 'default': parents.append('vrf {0}'.format(module.params['vrf'])) - parents.append('address-family {0} {1}'.format(module.params['afi'], - module.params['safi'])) + parents.append('address-family {0} {1}'.format(module.params['afi'], module.params['safi'])) config = netcfg.get_section(parents) if config: @@ -568,22 +519,18 @@ def get_existing(module, args): existing['safi'] = module.params['safi'] existing['vrf'] = module.params['vrf'] else: - WARNINGS.append("The BGP process {0} didn't exist but the task" - " just created it.".format(module.params['asn'])) + warnings.append("The BGP process {0} didn't exist but the task just created it.".format(module.params['asn'])) return existing def apply_key_map(key_map, table): new_dict = {} - for key, value in table.items(): + for key in table: new_key = key_map.get(key) if new_key: - value = table.get(key) - if value: - new_dict[new_key] = value - else: - new_dict[new_key] = value + new_dict[new_key] = table.get(key) + return new_dict @@ -637,12 +584,10 @@ def default_existing(existing_value, key, value): elif key == 'inject-map': for maps in existing_value: if len(maps) == 2: - commands.append('no inject-map {0} exist-map {1}'.format( - maps[0], maps[1])) + commands.append('no inject-map {0} exist-map {1}'.format(maps[0], maps[1])) elif len(maps) == 3: commands.append('no inject-map {0} exist-map {1} ' - 'copy-attributes'.format( - maps[0], maps[1])) + 'copy-attributes'.format(maps[0], maps[1])) else: commands.append('no {0} {1}'.format(key, existing_value)) return commands @@ -658,8 +603,7 @@ def get_network_command(existing, key, value): if len(inet) == 1: command = '{0} {1}'.format(key, inet[0]) elif len(inet) == 2: - command = '{0} {1} route-map {2}'.format(key, - inet[0], inet[1]) + command = '{0} {1} route-map {2}'.format(key, inet[0], inet[1]) commands.append(command) return commands @@ -791,7 +735,7 @@ def state_present(module, existing, proposed, candidate): candidate.add(commands, parents=parents) elif len(commands) > 1: parents.append('address-family {0} {1}'.format(module.params['afi'], - module.params['safi'])) + module.params['safi'])) if addr_family_command in commands: commands.remove(addr_family_command) candidate.add(commands, parents=parents) @@ -812,8 +756,8 @@ def main(): argument_spec = dict( asn=dict(required=True, type='str'), vrf=dict(required=False, type='str', default='default'), - safi=dict(required=True, type='str', choices=['unicast','multicast', 'evpn']), - afi=dict(required=True, type='str', choices=['ipv4','ipv6', 'vpnv4', 'vpnv6', 'l2vpn']), + safi=dict(required=True, type='str', choices=['unicast', 'multicast', 'evpn']), + afi=dict(required=True, type='str', choices=['ipv4', 'ipv6', 'vpnv4', 'vpnv6', 'l2vpn']), additional_paths_install=dict(required=False, type='bool'), additional_paths_receive=dict(required=False, type='bool'), additional_paths_selection=dict(required=False, type='str'), @@ -841,27 +785,23 @@ def main(): suppress_inactive=dict(required=False, type='bool'), table_map=dict(required=False, type='str'), table_map_filter=dict(required=False, type='bool'), - state=dict(choices=['present', 'absent'], default='present', - required=False), - include_defaults=dict(default=True), - config=dict(), - save=dict(type='bool', default=False) + state=dict(choices=['present', 'absent'], default='present', required=False), ) argument_spec.update(nxos_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, - required_together=[DAMPENING_PARAMS, - ['distance_ibgp', - 'distance_ebgp', - 'distance_local']], - supports_check_mode=True) + module = AnsibleModule( + argument_spec=argument_spec, + required_together=[DAMPENING_PARAMS, ['distance_ibgp', 'distance_ebgp', 'distance_local']], + supports_check_mode=True, + ) warnings = list() check_args(module, warnings) - + result = dict(changed=False, warnings=warnings) state = module.params['state'] + if module.params['dampening_routemap']: for param in DAMPENING_PARAMS: if module.params[param]: @@ -878,52 +818,17 @@ def main(): module.fail_json(msg='table_map param is needed when using' ' table_map_filter filter.') - args = [ - "additional_paths_install", - "additional_paths_receive", - "additional_paths_selection", - "additional_paths_send", - "advertise_l2vpn_evpn", - "afi", - "asn", - "client_to_client", - "dampen_igp_metric", - "dampening_half_time", - "dampening_max_suppress_time", - "dampening_reuse_time", - "dampening_suppress_time", - "dampening_routemap", - "dampening_state", - "default_information_originate", - "default_metric", - "distance_ebgp", - "distance_ibgp", - "distance_local", - "inject_map", - "maximum_paths", - "maximum_paths_ibgp", - "networks", - "next_hop_route_map", - "redistribute", - "safi", - "suppress_inactive", - "table_map", - "table_map_filter", - "vrf" - ] + args = PARAM_TO_COMMAND_KEYMAP.keys() + existing = get_existing(module, args, warnings) - existing = invoke('get_existing', module, args) - - if existing.get('asn'): - if (existing.get('asn') != module.params['asn'] and - state == 'present'): + if existing.get('asn') and state == 'present': + if existing.get('asn') != module.params['asn']: module.fail_json(msg='Another BGP ASN already exists.', proposed_asn=module.params['asn'], existing_asn=existing.get('asn')) - end_state = existing proposed_args = dict((k, v) for k, v in module.params.items() - if v is not None and k in args) + if v is not None and k in args) if proposed_args.get('networks'): if proposed_args['networks'][0] == 'default': @@ -936,33 +841,25 @@ def main(): for key, value in proposed_args.items(): if key not in ['asn', 'vrf']: if str(value).lower() == 'default': - value = PARAM_TO_DEFAULT_KEYMAP.get(key) - if value is None: - value = 'default' - if existing.get(key) or (not existing.get(key) and value): + value = PARAM_TO_DEFAULT_KEYMAP.get(key, 'default') + if existing.get(key) != value: proposed[key] = value - result = {} - if state == 'present' or (state == 'absent' and existing): - candidate = CustomNetworkConfig(indent=3) - invoke('state_%s' % state, module, existing, proposed, candidate) - response = load_config(module, candidate) - result.update(response) + candidate = CustomNetworkConfig(indent=3) + if state == 'present': + state_present(module, existing, proposed, candidate) + elif state == 'absent' and existing: + state_absent(module, existing, proposed, candidate) + if candidate: + load_config(module, candidate) + result['changed'] = True + result['commands'] = candidate.items_text() else: - result['updates'] = [] + result['commands'] = [] - if module._verbosity > 0: - end_state = invoke('get_existing', module, args) - result['end_state'] = end_state - result['existing'] = existing - result['proposed'] = proposed_args - - if WARNINGS: - result['warnings'] = WARNINGS module.exit_json(**result) if __name__ == '__main__': main() - diff --git a/test/sanity/pep8/legacy-files.txt b/test/sanity/pep8/legacy-files.txt index 7634d4a699..a071a35d2b 100644 --- a/test/sanity/pep8/legacy-files.txt +++ b/test/sanity/pep8/legacy-files.txt @@ -489,7 +489,6 @@ lib/ansible/modules/network/nxos/_nxos_mtu.py lib/ansible/modules/network/nxos/_nxos_template.py lib/ansible/modules/network/nxos/nxos_aaa_server.py lib/ansible/modules/network/nxos/nxos_aaa_server_host.py -lib/ansible/modules/network/nxos/nxos_bgp_af.py lib/ansible/modules/network/nxos/nxos_bgp_neighbor.py lib/ansible/modules/network/nxos/nxos_bgp_neighbor_af.py lib/ansible/modules/network/nxos/nxos_command.py diff --git a/test/units/modules/network/nxos/test_nxos_bgp_af.py b/test/units/modules/network/nxos/test_nxos_bgp_af.py new file mode 100644 index 0000000000..a51edf7524 --- /dev/null +++ b/test/units/modules/network/nxos/test_nxos_bgp_af.py @@ -0,0 +1,87 @@ +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible.compat.tests.mock import patch +from ansible.modules.network.nxos import nxos_bgp_af +from .nxos_module import TestNxosModule, load_fixture, set_module_args + + +class TestNxosBgpAfModule(TestNxosModule): + + module = nxos_bgp_af + + def setUp(self): + self.mock_load_config = patch('ansible.modules.network.nxos.nxos_bgp_af.load_config') + self.load_config = self.mock_load_config.start() + + self.mock_get_config = patch('ansible.modules.network.nxos.nxos_bgp_af.get_config') + self.get_config = self.mock_get_config.start() + + def tearDown(self): + self.mock_load_config.stop() + self.mock_get_config.stop() + + def load_fixtures(self, commands=None): + self.get_config.return_value = load_fixture('nxos_bgp_config.cfg') + self.load_config.return_value = None + + def test_nxos_bgp_af(self): + set_module_args(dict(asn=65535, afi='ipv4', safi='unicast')) + self.execute_module( + changed=True, + commands=['router bgp 65535', 'address-family ipv4 unicast'] + ) + + def test_nxos_bgp_af_vrf(self): + set_module_args(dict(asn=65535, vrf='test', afi='ipv4', safi='unicast')) + self.execute_module( + changed=True, + commands=['router bgp 65535', 'vrf test', 'address-family ipv4 unicast'] + ) + + def test_nxos_bgp_af_dampening_routemap(self): + set_module_args(dict(asn=65535, afi='ipv4', safi='unicast', + dampening_routemap='route-map-a')) + self.execute_module( + changed=True, + commands=['router bgp 65535', 'address-family ipv4 unicast', + 'dampening route-map route-map-a'] + ) + + def test_nxos_bgp_af_dampening_manual(self): + set_module_args(dict(asn=65535, afi='ipv4', safi='unicast', + dampening_half_time=5, dampening_suppress_time=2000, + dampening_reuse_time=1900, dampening_max_suppress_time=10)) + self.execute_module( + changed=True, + commands=['router bgp 65535', 'address-family ipv4 unicast', + 'dampening 5 1900 2000 10'] + ) + + def test_nxos_bgp_af_dampening_mix(self): + set_module_args(dict(asn=65535, afi='ipv4', safi='unicast', + dampening_routemap='route-map-a', + dampening_half_time=5, dampening_suppress_time=2000, + dampening_reuse_time=1900, dampening_max_suppress_time=10)) + result = self.execute_module(failed=True) + self.assertEqual(result['msg'], 'dampening_routemap cannot be used with the dampening_half_time param')