#!/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 .
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: ce_vrf
short_description: Manages VPN instance on HUAWEI CloudEngine switches.
description:
- Manages VPN instance of HUAWEI CloudEngine switches.
author: Yang yang (@QijunPan)
notes:
- If I(state=absent), the route will be removed, regardless of the non-required options.
- This module requires the netconf system service be enabled on the remote device being managed.
- Recommended connection is C(netconf).
- This module also works with C(local) connections for legacy playbooks.
options:
vrf:
description:
- VPN instance, the length of vrf name is 1 - 31, i.e. "test", but can not be C(_public_).
required: true
description:
description:
- Description of the vrf, the string length is 1 - 242 .
state:
description:
- Manage the state of the resource.
choices: ['present','absent']
default: present
'''
EXAMPLES = '''
- name: vrf module 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: Config a vpn install named vpna, description is test
ce_vrf:
vrf: vpna
description: test
state: present
provider: "{{ cli }}"
- name: Delete a vpn install named vpna
ce_vrf:
vrf: vpna
state: absent
provider: "{{ cli }}"
'''
RETURN = '''
proposed:
description: k/v pairs of parameters passed into module
returned: always
type: dict
sample: {"vrf": "vpna",
"description": "test",
"state": "present"}
existing:
description: k/v pairs of existing switchport
returned: always
type: dict
sample: {}
end_state:
description: k/v pairs of switchport after module execution
returned: always
type: dict
sample: {"vrf": "vpna",
"description": "test",
"present": "present"}
updates:
description: command list sent to the device
returned: always
type: list
sample: ["ip vpn-instance vpna",
"description test"]
changed:
description: check to see if a change was made on the device
returned: always
type: bool
sample: true
'''
from xml.etree import ElementTree
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec
CE_NC_GET_VRF = """
"""
CE_NC_CREATE_VRF = """
%s
%s
"""
CE_NC_DELETE_VRF = """
%s
%s
"""
def build_config_xml(xmlstr):
"""build_config_xml"""
return ' ' + xmlstr + ' '
class Vrf(object):
"""Manage vpn instance"""
def __init__(self, argument_spec, ):
self.spec = argument_spec
self.module = None
self.init_module()
# vpn instance info
self.vrf = self.module.params['vrf']
self.description = self.module.params['description']
self.state = self.module.params['state']
# 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 check_response(self, xml_str, xml_name):
"""Check if response message is already succeed."""
if "" not in xml_str:
self.module.fail_json(msg='Error: %s failed.' % xml_name)
def set_update_cmd(self):
""" set update command"""
if not self.changed:
return
if self.state == "present":
self.updates_cmd.append('ip vpn-instance %s' % (self.vrf))
if self.description:
self.updates_cmd.append('description %s' % (self.description))
else:
self.updates_cmd.append('undo ip vpn-instance %s' % (self.vrf))
def get_vrf(self):
""" check if vrf is need to change"""
getxmlstr = CE_NC_GET_VRF
xml_str = get_nc_config(self.module, getxmlstr)
xml_str = xml_str.replace('\r', '').replace('\n', '').\
replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
root = ElementTree.fromstring(xml_str)
vpn_instances = root.findall(
"l3vpn/l3vpncomm/l3vpnInstances/l3vpnInstance")
if vpn_instances:
for vpn_instance in vpn_instances:
if vpn_instance.find('vrfName').text == self.vrf:
if vpn_instance.find('vrfDescription').text == self.description:
if self.state == "present":
return False
else:
return True
else:
return True
return self.state == "present"
else:
return self.state == "present"
def check_params(self):
"""Check all input params"""
# vrf and description check
if self.vrf == '_public_':
self.module.fail_json(
msg='Error: The vrf name _public_ is reserved.')
if len(self.vrf) < 1 or len(self.vrf) > 31:
self.module.fail_json(
msg='Error: The vrf name length must between 1 and 242.')
if self.description:
if len(self.description) < 1 or len(self.description) > 242:
self.module.fail_json(
msg='Error: The vrf description length must between 1 and 242.')
def operate_vrf(self):
"""config/delete vrf"""
if not self.changed:
return
if self.state == "present":
if self.description is None:
configxmlstr = CE_NC_CREATE_VRF % (self.vrf, '')
else:
configxmlstr = CE_NC_CREATE_VRF % (self.vrf, self.description)
else:
configxmlstr = CE_NC_DELETE_VRF % (self.vrf, self.description)
conf_str = build_config_xml(configxmlstr)
recv_xml = set_nc_config(self.module, conf_str)
self.check_response(recv_xml, "OPERATE_VRF")
def get_proposed(self):
"""get_proposed"""
if self.state == 'present':
self.proposed['vrf'] = self.vrf
if self.description:
self.proposed['description'] = self.description
else:
self.proposed = dict()
self.proposed['state'] = self.state
def get_existing(self):
"""get_existing"""
change = self.get_vrf()
if change:
if self.state == 'present':
self.existing = dict()
else:
self.existing['vrf'] = self.vrf
if self.description:
self.existing['description'] = self.description
self.changed = True
else:
if self.state == 'absent':
self.existing = dict()
else:
self.existing['vrf'] = self.vrf
if self.description:
self.existing['description'] = self.description
self.changed = False
def get_end_state(self):
"""get_end_state"""
change = self.get_vrf()
if not change:
if self.state == 'present':
self.end_state['vrf'] = self.vrf
if self.description:
self.end_state['description'] = self.description
else:
self.end_state = dict()
else:
if self.state == 'present':
self.end_state = dict()
else:
self.end_state['vrf'] = self.vrf
if self.description:
self.end_state['description'] = self.description
def work(self):
"""worker"""
self.check_params()
self.get_existing()
self.get_proposed()
self.operate_vrf()
self.set_update_cmd()
self.get_end_state()
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 main():
"""main"""
argument_spec = dict(
vrf=dict(required=True, type='str'),
description=dict(required=False, type='str'),
state=dict(choices=['absent', 'present'],
default='present', required=False),
)
argument_spec.update(ce_argument_spec)
interface = Vrf(argument_spec)
interface.work()
if __name__ == '__main__':
main()