#!/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()