From 6a1290230b4add6a07568e247e7c85c4f4ed016a Mon Sep 17 00:00:00 2001 From: amitsi Date: Fri, 16 Sep 2016 07:04:29 -0700 Subject: [PATCH] pn_bgp (New Module) (#4738) * added Py2.4 and YAML Documentation fixes * added no_log for password * incorporated additional review comments * remove type for options block * fix type for pn_multiprotocol --- .../modules/network/netvisor/pn_vrouterbgp.py | 470 ++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 lib/ansible/modules/network/netvisor/pn_vrouterbgp.py diff --git a/lib/ansible/modules/network/netvisor/pn_vrouterbgp.py b/lib/ansible/modules/network/netvisor/pn_vrouterbgp.py new file mode 100644 index 00000000000..1a535a221f5 --- /dev/null +++ b/lib/ansible/modules/network/netvisor/pn_vrouterbgp.py @@ -0,0 +1,470 @@ +#!/usr/bin/python +""" PN-CLI vrouter-bgp-add/vrouter-bgp-remove/vrouter-bgp-modify """ + +# +# 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 . +# + + +import shlex + +DOCUMENTATION = """ +--- +module: pn_vrouterbgp +author: "Pluribus Networks (@amitsi)" +version_added: "2.2" +version: 1.0 +short_description: CLI command to add/remove/modify vrouter-bgp. +description: + - Execute vrouter-bgp-add, vrouter-bgp-remove, vrouter-bgp-modify command. + - Each fabric, cluster, standalone switch, or virtual network (VNET) can + provide its tenants with a vRouter service that forwards traffic between + networks and implements Layer 4 protocols. +options: + pn_cliusername: + description: + - Provide login username if user is not root. + required: False + pn_clipassword: + description: + - Provide login password if user is not root. + required: False + pn_cliswitch: + description: + - Target switch(es) to run the cli on. + required: False + state: + description: + - State the action to perform. Use 'present' to add bgp, + 'absent' to remove bgp and 'update' to modify bgp. + required: True + choices: ['present', 'absent', 'update'] + pn_vrouter_name: + description: + - Specify a name for the vRouter service. + required: True + pn_neighbor: + description: + - Specify a neighbor IP address to use for BGP. + - Required for vrouter-bgp-add. + pn_remote_as: + description: + - Specify the remote Autonomous System(AS) number. This value is between + 1 and 4294967295. + - Required for vrouter-bgp-add. + pn_next_hop_self: + description: + - Specify if the next-hop is the same router or not. + pn_password: + description: + - Specify a password, if desired. + pn_ebgp: + description: + - Specify a value for external BGP to accept or attempt BGP connections + to external peers, not directly connected, on the network. This is a + value between 1 and 255. + pn_prefix_listin: + description: + - Specify the prefix list to filter traffic inbound. + pn_prefix_listout: + description: + - Specify the prefix list to filter traffic outbound. + pn_route_reflector: + description: + - Specify if a route reflector client is used. + pn_override_capability: + description: + - Specify if you want to override capability. + pn_soft_reconfig: + description: + - Specify if you want a soft reconfiguration of inbound traffic. + pn_max_prefix: + description: + - Specify the maximum number of prefixes. + pn_max_prefix_warn: + description: + - Specify if you want a warning message when the maximum number of + prefixes is exceeded. + pn_bfd: + description: + - Specify if you want BFD protocol support for fault detection. + pn_multiprotocol: + description: + - Specify a multi-protocol for BGP. + choices: ['ipv4-unicast', 'ipv6-unicast'] + pn_weight: + description: + - Specify a default weight value between 0 and 65535 for the neighbor + routes. + pn_default_originate: + description: + - Specify if you want announce default routes to the neighbor or not. + pn_keepalive: + description: + - Specify BGP neighbor keepalive interval in seconds. + pn_holdtime: + description: + - Specify BGP neighbor holdtime in seconds. + pn_route_mapin: + description: + - Specify inbound route map for neighbor. + pn_route_mapout: + description: + - Specify outbound route map for neighbor. +""" + +EXAMPLES = """ +- name: add vrouter-bgp + pn_vrouterbgp: + state: 'present' + pn_vrouter_name: 'ansible-vrouter' + pn_neighbor: 104.104.104.1 + pn_remote_as: 1800 + +- name: remove vrouter-bgp + pn_vrouterbgp: + state: 'absent' + pn_name: 'ansible-vrouter' +""" + +RETURN = """ +command: + description: The CLI command run on the target node(s). +stdout: + description: The set of responses from the vrouterbpg command. + returned: always + type: list +stderr: + description: The set of error responses from the vrouterbgp command. + returned: on error + type: list +changed: + description: Indicates whether the CLI caused changes on the target. + returned: always + type: bool +""" + + +VROUTER_EXISTS = None +NEIGHBOR_EXISTS = None + + +def pn_cli(module): + """ + This method is to generate the cli portion to launch the Netvisor cli. + It parses the username, password, switch parameters from module. + :param module: The Ansible module to fetch username, password and switch + :return: returns the cli string for further processing + """ + username = module.params['pn_cliusername'] + password = module.params['pn_clipassword'] + cliswitch = module.params['pn_cliswitch'] + + if username and password: + cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) + else: + cli = '/usr/bin/cli --quiet ' + + if cliswitch == 'local': + cli += ' switch-local ' + else: + cli += ' switch ' + cliswitch + return cli + + +def check_cli(module, cli): + """ + This method checks if vRouter exists on the target node. + This method also checks for idempotency using the vrouter-bgp-show command. + If the given vRouter exists, return VROUTER_EXISTS as True else False. + If a BGP neighbor with the given ip exists on the given vRouter, + return NEIGHBOR_EXISTS as True else False. + + :param module: The Ansible module to fetch input parameters + :param cli: The CLI string + :return Global Booleans: VROUTER_EXISTS, NEIGHBOR_EXISTS + """ + vrouter_name = module.params['pn_vrouter_name'] + neighbor = module.params['pn_neighbor'] + # Global flags + global VROUTER_EXISTS, NEIGHBOR_EXISTS + + # Check for vRouter + check_vrouter = cli + ' vrouter-show format name no-show-headers ' + check_vrouter = shlex.split(check_vrouter) + out = module.run_command(check_vrouter)[1] + out = out.split() + + if vrouter_name in out: + VROUTER_EXISTS = True + else: + VROUTER_EXISTS = False + + # Check for BGP neighbors + show = cli + ' vrouter-bgp-show vrouter-name %s ' % vrouter_name + show += 'format neighbor no-show-headers' + show = shlex.split(show) + out = module.run_command(show)[1] + out = out.split() + + if neighbor in out: + NEIGHBOR_EXISTS = True + else: + NEIGHBOR_EXISTS = False + + +def run_cli(module, cli): + """ + This method executes the cli command on the target node(s) and returns the + output. The module then exits based on the output. + :param cli: the complete cli string to be executed on the target node(s). + :param module: The Ansible module to fetch command + """ + cliswitch = module.params['pn_cliswitch'] + state = module.params['state'] + command = get_command_from_state(state) + + cmd = shlex.split(cli) + + # 'out' contains the output + # 'err' contains the error messages + result, out, err = module.run_command(cmd) + + print_cli = cli.split(cliswitch)[1] + + # Response in JSON format + if result != 0: + module.exit_json( + command=print_cli, + stderr=err.strip(), + msg="%s operation failed" % command, + changed=False + ) + + if out: + module.exit_json( + command=print_cli, + stdout=out.strip(), + msg="%s operation completed" % command, + changed=True + ) + + else: + module.exit_json( + command=print_cli, + msg="%s operation completed" % command, + changed=True + ) + + +def get_command_from_state(state): + """ + This method gets appropriate command name for the state specified. It + returns the command name for the specified state. + :param state: The state for which the respective command name is required. + """ + command = None + if state == 'present': + command = 'vrouter-bgp-add' + if state == 'absent': + command = 'vrouter-bgp-remove' + if state == 'update': + command = 'vrouter-bgp-modify' + return command + + +def main(): + """ This portion is for arguments parsing """ + module = AnsibleModule( + argument_spec=dict( + pn_cliusername=dict(required=False, type='str'), + pn_clipassword=dict(required=False, type='str', no_log=True), + pn_cliswitch=dict(required=False, type='str', default='local'), + state=dict(required=True, type='str', + choices=['present', 'absent', 'update']), + pn_vrouter_name=dict(required=True, type='str'), + pn_neighbor=dict(type='str'), + pn_remote_as=dict(type='str'), + pn_next_hop_self=dict(type='bool'), + pn_password=dict(type='str', no_log=True), + pn_ebgp=dict(type='int'), + pn_prefix_listin=dict(type='str'), + pn_prefix_listout=dict(type='str'), + pn_route_reflector=dict(type='bool'), + pn_override_capability=dict(type='bool'), + pn_soft_reconfig=dict(type='bool'), + pn_max_prefix=dict(type='int'), + pn_max_prefix_warn=dict(type='bool'), + pn_bfd=dict(type='bool'), + pn_multiprotocol=dict(type='str', + choices=['ipv4-unicast', 'ipv6-unicast']), + pn_weight=dict(type='int'), + pn_default_originate=dict(type='bool'), + pn_keepalive=dict(type='str'), + pn_holdtime=dict(type='str'), + pn_route_mapin=dict(type='str'), + pn_route_mapout=dict(type='str') + ), + required_if=( + ["state", "present", + ["pn_vrouter_name", "pn_neighbor", "pn_remote_as"]], + ["state", "absent", + ["pn_vrouter_name", "pn_neighbor"]], + ["state", "update", + ["pn_vrouter_name", "pn_neighbor"]] + ) + ) + + # Accessing the arguments + state= module.params['state'] + vrouter_name = module.params['pn_vrouter_name'] + neighbor = module.params['pn_neighbor'] + remote_as = module.params['pn_remote_as'] + next_hop_self = module.params['pn_next_hop_self'] + password = module.params['pn_password'] + ebgp = module.params['pn_ebgp'] + prefix_listin = module.params['pn_prefix_listin'] + prefix_listout = module.params['pn_prefix_listout'] + route_reflector = module.params['pn_route_reflector'] + override_capability = module.params['pn_override_capability'] + soft_reconfig = module.params['pn_soft_reconfig'] + max_prefix = module.params['pn_max_prefix'] + max_prefix_warn = module.params['pn_max_prefix_warn'] + bfd = module.params['pn_bfd'] + multiprotocol = module.params['pn_multiprotocol'] + weight = module.params['pn_weight'] + default_originate = module.params['pn_default_originate'] + keepalive = module.params['pn_keepalive'] + holdtime = module.params['pn_holdtime'] + route_mapin = module.params['pn_route_mapin'] + route_mapout = module.params['pn_route_mapout'] + + # Building the CLI command string + cli = pn_cli(module) + + command = get_command_from_state(state) + if command == 'vrouter-bgp-remove': + check_cli(module, cli) + if VROUTER_EXISTS is False: + module.exit_json( + skipped=True, + msg='vRouter %s does not exist' % vrouter_name + ) + if NEIGHBOR_EXISTS is False: + module.exit_json( + skipped=True, + msg=('BGP neighbor with IP %s does not exist on %s' + % (neighbor, vrouter_name)) + ) + cli += (' %s vrouter-name %s neighbor %s ' + % (command, vrouter_name, neighbor)) + + else: + + if command == 'vrouter-bgp-add': + check_cli(module, cli) + if VROUTER_EXISTS is False: + module.exit_json( + skipped=True, + msg='vRouter %s does not exist' % vrouter_name + ) + if NEIGHBOR_EXISTS is True: + module.exit_json( + skipped=True, + msg=('BGP neighbor with IP %s already exists on %s' + % (neighbor, vrouter_name)) + ) + + cli += (' %s vrouter-name %s neighbor %s ' + % (command, vrouter_name, neighbor)) + + if remote_as: + cli += ' remote-as ' + str(remote_as) + + if next_hop_self is True: + cli += ' next-hop-self ' + if next_hop_self is False: + cli += ' no-next-hop-self ' + + if password: + cli += ' password ' + password + + if ebgp: + cli += ' ebgp-multihop ' + str(ebgp) + + if prefix_listin: + cli += ' prefix-list-in ' + prefix_listin + + if prefix_listout: + cli += ' prefix-list-out ' + prefix_listout + + if route_reflector is True: + cli += ' route-reflector-client ' + if route_reflector is False: + cli += ' no-route-reflector-client ' + + if override_capability is True: + cli += ' override-capability ' + if override_capability is False: + cli += ' no-override-capability ' + + if soft_reconfig is True: + cli += ' soft-reconfig-inbound ' + if soft_reconfig is False: + cli += ' no-soft-reconfig-inbound ' + + if max_prefix: + cli += ' max-prefix ' + str(max_prefix) + + if max_prefix_warn is True: + cli += ' max-prefix-warn-only ' + if max_prefix_warn is False: + cli += ' no-max-prefix-warn-only ' + + if bfd is True: + cli += ' bfd ' + if bfd is False: + cli += ' no-bfd ' + + if multiprotocol: + cli += ' multi-protocol ' + multiprotocol + + if weight: + cli += ' weight ' + str(weight) + + if default_originate is True: + cli += ' default-originate ' + if default_originate is False: + cli += ' no-default-originate ' + + if keepalive: + cli += ' neighbor-keepalive-interval ' + keepalive + + if holdtime: + cli += ' neighbor-holdtime ' + holdtime + + if route_mapin: + cli += ' route-map-in ' + route_mapin + + if route_mapout: + cli += ' route-map-out ' + route_mapout + + run_cli(module, cli) +# Ansible boiler-plate +from ansible.module_utils.basic import AnsibleModule + +if __name__ == '__main__': + main()