mirror of https://github.com/ansible/ansible.git
Contributing lib/ansible/modules/network/cloudengine/ce_evpn_bgp_rr.py module to manage HUAWEI data center CloudEngine (#22043)
* add ce_evpn_bgp_rr add ce_evpn_bgp_rr * fix CI issuespull/24676/head
parent
4961778732
commit
35bc99156a
@ -0,0 +1,535 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'status': ['preview'],
|
||||
'supported_by': 'community',
|
||||
'metadata_version': '1.0'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ce_evpn_bgp_rr
|
||||
version_added: "2.4"
|
||||
short_description: Manages RR for the VXLAN Network on HUAWEI CloudEngine switches.
|
||||
description:
|
||||
- Configure an RR in BGP-EVPN address family view on HUAWEI CloudEngine switches.
|
||||
author: Zhijin Zhou (@CloudEngine-Ansible)
|
||||
notes:
|
||||
- Ensure that BGP view is existed.
|
||||
- The peer, peer_type, and reflect_client arguments must all exist or not exist.
|
||||
options:
|
||||
as_number:
|
||||
description:
|
||||
- Specifies the number of the AS, in integer format.
|
||||
The value is an integer that ranges from 1 to 4294967295.
|
||||
required: true
|
||||
default: null
|
||||
bgp_instance:
|
||||
description:
|
||||
- Specifies the name of a BGP instance.
|
||||
The value of instance-name can be an integer 1 or a string of 1 to 31.
|
||||
required: false
|
||||
default: null
|
||||
bgp_evpn_enable:
|
||||
description:
|
||||
- Enable or disable the BGP-EVPN address family.
|
||||
required: false
|
||||
choices: ['enable','disable']
|
||||
default: 'enable'
|
||||
peer_type:
|
||||
description:
|
||||
- Specify the peer type.
|
||||
required: false
|
||||
choices: ['group_name','ipv4_address']
|
||||
default: null
|
||||
peer:
|
||||
description:
|
||||
- Specifies the IPv4 address or the group name of a peer.
|
||||
required: false
|
||||
default: null
|
||||
reflect_client:
|
||||
description:
|
||||
- Configure the local device as the route reflector and the peer or peer group as the client of the route reflector.
|
||||
required: false
|
||||
choices: ['enable','disable']
|
||||
default: null
|
||||
policy_vpn_target:
|
||||
description:
|
||||
- Enable or disable the VPN-Target filtering.
|
||||
required: false
|
||||
choices: ['enable','disable']
|
||||
default: null
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: BGP RR test
|
||||
hosts: cloudengine
|
||||
connection: local
|
||||
gather_facts: no
|
||||
vars:
|
||||
cli:
|
||||
host: "{{ inventory_hostname }}"
|
||||
port: "{{ ansible_ssh_port }}"
|
||||
username: "{{ username }}"
|
||||
password: "{{ password }}"
|
||||
transport: cli
|
||||
|
||||
tasks:
|
||||
|
||||
- name: "Configure BGP-EVPN address family view and ensure that BGP view has existed."
|
||||
ce_evpn_bgp_rr:
|
||||
as_number: 20
|
||||
bgp_evpn_enable: enable
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: "Configure reflect client and ensure peer has existed."
|
||||
ce_evpn_bgp_rr:
|
||||
as_number: 20
|
||||
peer_type: ipv4_address
|
||||
peer: 192.8.3.3
|
||||
reflect_client: enable
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: "Configure the VPN-Target filtering."
|
||||
ce_evpn_bgp_rr:
|
||||
as_number: 20
|
||||
policy_vpn_target: enable
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: "Configure an RR in BGP-EVPN address family view."
|
||||
ce_evpn_bgp_rr:
|
||||
as_number: 20
|
||||
bgp_evpn_enable: enable
|
||||
peer_type: ipv4_address
|
||||
peer: 192.8.3.3
|
||||
reflect_client: enable
|
||||
policy_vpn_target: disable
|
||||
provider: "{{ cli }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
proposed:
|
||||
description: k/v pairs of parameters passed into module
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"as_number": "20",
|
||||
"bgp_evpn_enable": "enable",
|
||||
"bgp_instance": null,
|
||||
"peer": "192.8.3.3",
|
||||
"peer_type": "ipv4_address",
|
||||
"policy_vpn_target": "disable",
|
||||
"reflect_client": "enable"
|
||||
}
|
||||
existing:
|
||||
description: k/v pairs of existing attributes on the device
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"as_number": "20",
|
||||
"bgp_evpn_enable": "disable",
|
||||
"bgp_instance": null,
|
||||
"peer": null,
|
||||
"peer_type": null,
|
||||
"policy_vpn_target": "disable",
|
||||
"reflect_client": "disable"
|
||||
}
|
||||
end_state:
|
||||
description: k/v pairs of end attributes on the device
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"as_number": "20",
|
||||
"bgp_evpn_enable": "enable",
|
||||
"bgp_instance": null,
|
||||
"peer": "192.8.3.3",
|
||||
"peer_type": "ipv4_address",
|
||||
"policy_vpn_target": "disable",
|
||||
"reflect_client": "enable"
|
||||
}
|
||||
updates:
|
||||
description: command list sent to the device
|
||||
returned: always
|
||||
type: list
|
||||
sample: [
|
||||
"bgp 20",
|
||||
" l2vpn-family evpn",
|
||||
" peer 192.8.3.3 enable",
|
||||
" peer 192.8.3.3 reflect-client",
|
||||
" undo policy vpn-target"
|
||||
]
|
||||
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.basic import AnsibleModule
|
||||
from ansible.module_utils.ce import get_config, load_config, ce_argument_spec
|
||||
|
||||
|
||||
def is_config_exist(cmp_cfg, test_cfg):
|
||||
"""is configuration exist"""
|
||||
|
||||
if not cmp_cfg or not test_cfg:
|
||||
return False
|
||||
|
||||
return bool(test_cfg in cmp_cfg)
|
||||
|
||||
|
||||
class EvpnBgpRr(object):
|
||||
"""Manange RR in BGP-EVPN address family view"""
|
||||
|
||||
def __init__(self, argument_spec):
|
||||
self.spec = argument_spec
|
||||
self.module = None
|
||||
self.__init_module__()
|
||||
|
||||
# RR configuration parameters
|
||||
self.as_number = self.module.params['as_number']
|
||||
self.bgp_instance = self.module.params['bgp_instance']
|
||||
self.peer_type = self.module.params['peer_type']
|
||||
self.peer = self.module.params['peer']
|
||||
self.bgp_evpn_enable = self.module.params['bgp_evpn_enable']
|
||||
self.reflect_client = self.module.params['reflect_client']
|
||||
self.policy_vpn_target = self.module.params['policy_vpn_target']
|
||||
|
||||
self.commands = list()
|
||||
self.config = None
|
||||
self.bgp_evpn_config = ""
|
||||
self.cur_config = dict()
|
||||
self.conf_exist = False
|
||||
|
||||
# state
|
||||
self.changed = False
|
||||
self.updates_cmd = list()
|
||||
self.results = dict()
|
||||
self.proposed = dict()
|
||||
self.existing = dict()
|
||||
self.end_state = dict()
|
||||
|
||||
def __init_module__(self):
|
||||
"""Init module"""
|
||||
|
||||
self.module = AnsibleModule(
|
||||
argument_spec=self.spec, supports_check_mode=True)
|
||||
|
||||
def cli_load_config(self, commands):
|
||||
"""Load config by cli"""
|
||||
|
||||
if not self.module.check_mode:
|
||||
load_config(self.module, commands)
|
||||
|
||||
def is_bgp_view_exist(self):
|
||||
"""Judge whether BGP view has existed"""
|
||||
|
||||
if self.bgp_instance:
|
||||
view_cmd = "bgp %s instance %s" % (
|
||||
self.as_number, self.bgp_instance)
|
||||
else:
|
||||
view_cmd = "bgp %s" % self.as_number
|
||||
|
||||
return is_config_exist(self.config, view_cmd)
|
||||
|
||||
def is_l2vpn_family_evpn_exist(self):
|
||||
"""Judge whether BGP-EVPN address family view has existed"""
|
||||
|
||||
view_cmd = "l2vpn-family evpn"
|
||||
return is_config_exist(self.config, view_cmd)
|
||||
|
||||
def is_reflect_client_exist(self):
|
||||
"""Judge whether reflect client is configured"""
|
||||
|
||||
view_cmd = "peer %s reflect-client" % self.peer
|
||||
return is_config_exist(self.bgp_evpn_config, view_cmd)
|
||||
|
||||
def is_policy_vpn_target_exist(self):
|
||||
"""Judge whether the VPN-Target filtering is enabled"""
|
||||
|
||||
view_cmd = "undo policy vpn-target"
|
||||
if is_config_exist(self.bgp_evpn_config, view_cmd):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_config_in_bgp_view(self):
|
||||
"""Get configuration in BGP view"""
|
||||
|
||||
flags = list()
|
||||
exp = " | section include"
|
||||
if self.as_number:
|
||||
if self.bgp_instance:
|
||||
exp += " bgp %s instance %s" % (self.as_number,
|
||||
self.bgp_instance)
|
||||
else:
|
||||
exp += " bgp %s" % self.as_number
|
||||
|
||||
flags.append(exp)
|
||||
config = get_config(self.module, flags)
|
||||
|
||||
return config
|
||||
|
||||
def get_config_in_bgp_evpn_view(self):
|
||||
"""Get configuration in BGP_EVPN view"""
|
||||
|
||||
self.bgp_evpn_config = ""
|
||||
if not self.config:
|
||||
return ""
|
||||
|
||||
index = self.config.find("l2vpn-family evpn")
|
||||
if index == -1:
|
||||
return ""
|
||||
|
||||
return self.config[index:]
|
||||
|
||||
def get_current_config(self):
|
||||
"""Get current configuration"""
|
||||
|
||||
if not self.as_number:
|
||||
self.module.fail_json(msg='Error: The value of as-number cannot be empty.')
|
||||
|
||||
self.cur_config['bgp_exist'] = False
|
||||
self.cur_config['bgp_evpn_enable'] = 'disable'
|
||||
self.cur_config['reflect_client'] = 'disable'
|
||||
self.cur_config['policy_vpn_target'] = 'disable'
|
||||
self.cur_config['peer_type'] = None
|
||||
self.cur_config['peer'] = None
|
||||
|
||||
self.config = self.get_config_in_bgp_view()
|
||||
|
||||
if not self.is_bgp_view_exist():
|
||||
return
|
||||
self.cur_config['bgp_exist'] = True
|
||||
|
||||
if not self.is_l2vpn_family_evpn_exist():
|
||||
return
|
||||
self.cur_config['bgp_evpn_enable'] = 'enable'
|
||||
|
||||
self.bgp_evpn_config = self.get_config_in_bgp_evpn_view()
|
||||
if self.is_reflect_client_exist():
|
||||
self.cur_config['reflect_client'] = 'enable'
|
||||
self.cur_config['peer_type'] = self.peer_type
|
||||
self.cur_config['peer'] = self.peer
|
||||
|
||||
if self.is_policy_vpn_target_exist():
|
||||
self.cur_config['policy_vpn_target'] = 'enable'
|
||||
|
||||
def get_existing(self):
|
||||
"""Get existing config"""
|
||||
|
||||
self.existing = dict(as_number=self.as_number,
|
||||
bgp_instance=self.bgp_instance,
|
||||
peer_type=self.cur_config['peer_type'],
|
||||
peer=self.cur_config['peer'],
|
||||
bgp_evpn_enable=self.cur_config[
|
||||
'bgp_evpn_enable'],
|
||||
reflect_client=self.cur_config['reflect_client'],
|
||||
policy_vpn_target=self.cur_config[
|
||||
'policy_vpn_target'])
|
||||
|
||||
def get_proposed(self):
|
||||
"""Get proposed config"""
|
||||
|
||||
self.proposed = dict(as_number=self.as_number,
|
||||
bgp_instance=self.bgp_instance,
|
||||
peer_type=self.peer_type,
|
||||
peer=self.peer,
|
||||
bgp_evpn_enable=self.bgp_evpn_enable,
|
||||
reflect_client=self.reflect_client,
|
||||
policy_vpn_target=self.policy_vpn_target)
|
||||
|
||||
def get_end_state(self):
|
||||
"""Get end config"""
|
||||
|
||||
self.get_current_config()
|
||||
self.end_state = dict(as_number=self.as_number,
|
||||
bgp_instance=self.bgp_instance,
|
||||
peer_type=self.cur_config['peer_type'],
|
||||
peer=self.cur_config['peer'],
|
||||
bgp_evpn_enable=self.cur_config[
|
||||
'bgp_evpn_enable'],
|
||||
reflect_client=self.cur_config['reflect_client'],
|
||||
policy_vpn_target=self.cur_config['policy_vpn_target'])
|
||||
|
||||
def show_result(self):
|
||||
"""Show result"""
|
||||
|
||||
self.results['changed'] = self.changed
|
||||
self.results['proposed'] = self.proposed
|
||||
self.results['existing'] = self.existing
|
||||
self.results['end_state'] = self.end_state
|
||||
if self.changed:
|
||||
self.results['updates'] = self.updates_cmd
|
||||
else:
|
||||
self.results['updates'] = list()
|
||||
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def judge_if_config_exist(self):
|
||||
"""Judge whether configuration has existed"""
|
||||
|
||||
if self.bgp_evpn_enable and self.bgp_evpn_enable != self.cur_config['bgp_evpn_enable']:
|
||||
return False
|
||||
|
||||
if self.bgp_evpn_enable == 'disable' and self.cur_config['bgp_evpn_enable'] == 'disable':
|
||||
return True
|
||||
|
||||
if self.reflect_client and self.reflect_client == 'enable':
|
||||
if self.peer_type and self.peer_type != self.cur_config['peer_type']:
|
||||
return False
|
||||
if self.peer and self.peer != self.cur_config['peer']:
|
||||
return False
|
||||
if self.reflect_client and self.reflect_client != self.cur_config['reflect_client']:
|
||||
return False
|
||||
|
||||
if self.policy_vpn_target and self.policy_vpn_target != self.cur_config['policy_vpn_target']:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def cli_add_command(self, command, undo=False):
|
||||
"""Add command to self.update_cmd and self.commands"""
|
||||
|
||||
if undo and command.lower() not in ["quit", "return"]:
|
||||
cmd = "undo " + command
|
||||
else:
|
||||
cmd = command
|
||||
|
||||
self.commands.append(cmd) # set to device
|
||||
if command.lower() not in ["quit", "return"]:
|
||||
self.updates_cmd.append(cmd) # show updates result
|
||||
|
||||
def config_rr(self):
|
||||
"""Configure RR"""
|
||||
|
||||
if self.conf_exist:
|
||||
return
|
||||
|
||||
if self.bgp_instance:
|
||||
view_cmd = "bgp %s instance %s" % (
|
||||
self.as_number, self.bgp_instance)
|
||||
else:
|
||||
view_cmd = "bgp %s" % self.as_number
|
||||
self.cli_add_command(view_cmd)
|
||||
|
||||
if self.bgp_evpn_enable == 'disable':
|
||||
self.cli_add_command(" undo l2vpn-family evpn")
|
||||
else:
|
||||
self.cli_add_command(" l2vpn-family evpn")
|
||||
if self.reflect_client and self.reflect_client != self.cur_config['reflect_client']:
|
||||
if self.reflect_client == 'enable':
|
||||
self.cli_add_command(" peer %s enable" % self.peer)
|
||||
self.cli_add_command(
|
||||
" peer %s reflect-client" % self.peer)
|
||||
else:
|
||||
self.cli_add_command(
|
||||
" undo peer %s reflect-client" % self.peer)
|
||||
self.cli_add_command(" undo peer %s enable" % self.peer)
|
||||
if self.cur_config['bgp_evpn_enable'] == 'enable':
|
||||
if self.policy_vpn_target and self.policy_vpn_target != self.cur_config['policy_vpn_target']:
|
||||
if self.policy_vpn_target == 'enable':
|
||||
self.cli_add_command(" policy vpn-target")
|
||||
else:
|
||||
self.cli_add_command(" undo policy vpn-target")
|
||||
else:
|
||||
if self.policy_vpn_target and self.policy_vpn_target == 'disable':
|
||||
self.cli_add_command(" undo policy vpn-target")
|
||||
|
||||
if self.commands:
|
||||
self.cli_load_config(self.commands)
|
||||
self.changed = True
|
||||
|
||||
def check_is_ipv4_addr(self):
|
||||
"""Check ipaddress validate"""
|
||||
|
||||
rule1 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
|
||||
rule2 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
|
||||
ipv4_regex = '%s%s%s%s%s%s' % ('^', rule1, rule1, rule1, rule2, '$')
|
||||
|
||||
return bool(re.match(ipv4_regex, self.peer))
|
||||
|
||||
def check_params(self):
|
||||
"""Check all input params"""
|
||||
|
||||
if self.cur_config['bgp_exist'] == 'false':
|
||||
self.module.fail_json(msg="Error: BGP view doesnot exist.")
|
||||
|
||||
if self.bgp_instance:
|
||||
if len(self.bgp_instance) < 1 or len(self.bgp_instance) > 31:
|
||||
self.module.fail_json(
|
||||
msg="Error: The length of BGP instance-name must be between 1 or a string of 1 to and 31.")
|
||||
|
||||
if self.as_number:
|
||||
if len(self.as_number) > 11 or len(self.as_number) == 0:
|
||||
self.module.fail_json(
|
||||
msg='Error: The len of as_number %s is out of [1 - 11].' % self.as_number)
|
||||
|
||||
tmp_dict1 = dict(peer_type=self.peer_type,
|
||||
peer=self.peer,
|
||||
reflect_client=self.reflect_client)
|
||||
tmp_dict2 = dict((k, v)
|
||||
for k, v in tmp_dict1.items() if v is not None)
|
||||
if len(tmp_dict2) != 0 and len(tmp_dict2) != 3:
|
||||
self.module.fail_json(
|
||||
msg='Error: The peer, peer_type, and reflect_client arguments must all exist or not exist.')
|
||||
|
||||
if self.peer_type:
|
||||
if self.peer_type == 'ipv4_address' and not self.check_is_ipv4_addr():
|
||||
self.module.fail_json(msg='Error: Illegal IPv4 address.')
|
||||
elif self.peer_type == 'group_name' and self.check_is_ipv4_addr():
|
||||
self.module.fail_json(
|
||||
msg='Error: Ip address cannot be configured as group-name.')
|
||||
|
||||
def work(self):
|
||||
"""Excute task"""
|
||||
|
||||
self.get_current_config()
|
||||
self.check_params()
|
||||
self.get_existing()
|
||||
self.get_proposed()
|
||||
self.conf_exist = self.judge_if_config_exist()
|
||||
|
||||
self.config_rr()
|
||||
|
||||
self.get_end_state()
|
||||
self.show_result()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function entry"""
|
||||
|
||||
argument_spec = dict(
|
||||
as_number=dict(required=True, type='str'),
|
||||
bgp_instance=dict(required=False, type='str'),
|
||||
bgp_evpn_enable=dict(required=False, type='str',
|
||||
default='enable', choices=['enable', 'disable']),
|
||||
peer_type=dict(required=False, type='str', choices=[
|
||||
'group_name', 'ipv4_address']),
|
||||
peer=dict(required=False, type='str'),
|
||||
reflect_client=dict(required=False, type='str',
|
||||
choices=['enable', 'disable']),
|
||||
policy_vpn_target=dict(required=False, choices=['enable', 'disable']),
|
||||
)
|
||||
argument_spec.update(ce_argument_spec)
|
||||
evpn_bgp_rr = EvpnBgpRr(argument_spec)
|
||||
evpn_bgp_rr.work()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue