#!/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_bfd_view short_description: Manages BFD session view configuration on HUAWEI CloudEngine devices. description: - Manages BFD session view configuration on HUAWEI CloudEngine devices. author: QijunPan (@QijunPan) notes: - 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: session_name: description: - Specifies the name of a BFD session. The value is a string of 1 to 15 case-sensitive characters without spaces. required: true local_discr: description: - Specifies the local discriminator of a BFD session. The value is an integer that ranges from 1 to 16384. remote_discr: description: - Specifies the remote discriminator of a BFD session. The value is an integer that ranges from 1 to 4294967295. min_tx_interval: description: - Specifies the minimum interval for receiving BFD packets. The value is an integer that ranges from 50 to 1000, in milliseconds. min_rx_interval: description: - Specifies the minimum interval for sending BFD packets. The value is an integer that ranges from 50 to 1000, in milliseconds. detect_multi: description: - Specifies the local detection multiplier of a BFD session. The value is an integer that ranges from 3 to 50. wtr_interval: description: - Specifies the WTR time of a BFD session. The value is an integer that ranges from 1 to 60, in minutes. The default value is 0. tos_exp: description: - Specifies a priority for BFD control packets. The value is an integer ranging from 0 to 7. The default value is 7, which is the highest priority. admin_down: description: - Enables the BFD session to enter the AdminDown state. By default, a BFD session is enabled. The default value is bool type. type: bool default: 'no' description: description: - Specifies the description of a BFD session. The value is a string of 1 to 51 case-sensitive characters with spaces. state: description: - Determines whether the config should be present or not on the device. default: present choices: ['present', 'absent'] extends_documentation_fragment: - community.general.ce ''' EXAMPLES = ''' - name: bfd view 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: Set the local discriminator of a BFD session to 80 and the remote discriminator to 800 ce_bfd_view: session_name: atob local_discr: 80 remote_discr: 800 state: present provider: '{{ cli }}' - name: Set the minimum interval for receiving BFD packets to 500 ms ce_bfd_view: session_name: atob min_rx_interval: 500 state: present provider: '{{ cli }}' ''' RETURN = ''' proposed: description: k/v pairs of parameters passed into module returned: always type: dict sample: { "admin_down": false, "description": null, "detect_multi": null, "local_discr": 80, "min_rx_interval": null, "min_tx_interval": null, "remote_discr": 800, "session_name": "atob", "state": "present", "tos_exp": null, "wtr_interval": null } existing: description: k/v pairs of existing configuration returned: always type: dict sample: { "session": { "adminDown": "false", "createType": "SESS_STATIC", "description": null, "detectMulti": "3", "localDiscr": null, "minRxInt": null, "minTxInt": null, "remoteDiscr": null, "sessName": "atob", "tosExp": null, "wtrTimerInt": null } } end_state: description: k/v pairs of configuration after module execution returned: always type: dict sample: { "session": { "adminDown": "false", "createType": "SESS_STATIC", "description": null, "detectMulti": "3", "localDiscr": "80", "minRxInt": null, "minTxInt": null, "remoteDiscr": "800", "sessName": "atob", "tosExp": null, "wtrTimerInt": null } } updates: description: commands sent to the device returned: always type: list sample: [ "bfd atob", "discriminator local 80", "discriminator remote 800" ] changed: description: check to see if a change was made on the device returned: always type: bool sample: true ''' import sys 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_BFD = """ %s """ CE_NC_GET_BFD_GLB = """ """ CE_NC_GET_BFD_SESSION = """ %s """ class BfdView(object): """Manages BFD View""" def __init__(self, argument_spec): self.spec = argument_spec self.module = None self.__init_module__() # module input info self.session_name = self.module.params['session_name'] self.local_discr = self.module.params['local_discr'] self.remote_discr = self.module.params['remote_discr'] self.min_tx_interval = self.module.params['min_tx_interval'] self.min_rx_interval = self.module.params['min_rx_interval'] self.detect_multi = self.module.params['detect_multi'] self.wtr_interval = self.module.params['wtr_interval'] self.tos_exp = self.module.params['tos_exp'] self.admin_down = self.module.params['admin_down'] self.description = self.module.params['description'] self.state = self.module.params['state'] # host info self.host = self.module.params['host'] self.username = self.module.params['username'] self.port = self.module.params['port'] # state self.changed = False self.bfd_dict = dict() self.updates_cmd = list() self.commands = 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 get_bfd_dict(self): """bfd config dict""" bfd_dict = dict() bfd_dict["global"] = dict() bfd_dict["session"] = dict() conf_str = CE_NC_GET_BFD % (CE_NC_GET_BFD_GLB + (CE_NC_GET_BFD_SESSION % self.session_name)) xml_str = get_nc_config(self.module, conf_str) if "" in xml_str: return bfd_dict 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) # get bfd global info glb = root.find("bfd/bfdSchGlobal") if glb: for attr in glb: bfd_dict["global"][attr.tag] = attr.text # get bfd session info sess = root.find("bfd/bfdCfgSessions/bfdCfgSession") if sess: for attr in sess: bfd_dict["session"][attr.tag] = attr.text return bfd_dict def config_session(self): """configures bfd session""" xml_str = "" cmd_list = list() cmd_session = "" if not self.session_name: return xml_str if self.bfd_dict["global"].get("bfdEnable", "false") != "true": self.module.fail_json(msg="Error: Please enable BFD globally first.") if not self.bfd_dict["session"]: self.module.fail_json(msg="Error: BFD session is not exist.") session = self.bfd_dict["session"] xml_str = "%s" % self.session_name cmd_session = "bfd %s" % self.session_name # BFD session view if self.local_discr is not None: if self.state == "present" and str(self.local_discr) != session.get("localDiscr"): xml_str += "%s" % self.local_discr cmd_list.append("discriminator local %s" % self.local_discr) elif self.state == "absent" and str(self.local_discr) == session.get("localDiscr"): xml_str += "" cmd_list.append("undo discriminator local") if self.remote_discr is not None: if self.state == "present" and str(self.remote_discr) != session.get("remoteDiscr"): xml_str += "%s" % self.remote_discr cmd_list.append("discriminator remote %s" % self.remote_discr) elif self.state == "absent" and str(self.remote_discr) == session.get("remoteDiscr"): xml_str += "" cmd_list.append("undo discriminator remote") if self.min_tx_interval is not None: if self.state == "present" and str(self.min_tx_interval) != session.get("minTxInt"): xml_str += "%s" % self.min_tx_interval cmd_list.append("min-tx-interval %s" % self.min_tx_interval) elif self.state == "absent" and str(self.min_tx_interval) == session.get("minTxInt"): xml_str += "" cmd_list.append("undo min-tx-interval") if self.min_rx_interval is not None: if self.state == "present" and str(self.min_rx_interval) != session.get("minRxInt"): xml_str += "%s" % self.min_rx_interval cmd_list.append("min-rx-interval %s" % self.min_rx_interval) elif self.state == "absent" and str(self.min_rx_interval) == session.get("minRxInt"): xml_str += "" cmd_list.append("undo min-rx-interval") if self.detect_multi is not None: if self.state == "present" and str(self.detect_multi) != session.get("detectMulti"): xml_str += " %s" % self.detect_multi cmd_list.append("detect-multiplier %s" % self.detect_multi) elif self.state == "absent" and str(self.detect_multi) == session.get("detectMulti"): xml_str += " " cmd_list.append("undo detect-multiplier") if self.wtr_interval is not None: if self.state == "present" and str(self.wtr_interval) != session.get("wtrTimerInt"): xml_str += " %s" % self.wtr_interval cmd_list.append("wtr %s" % self.wtr_interval) elif self.state == "absent" and str(self.wtr_interval) == session.get("wtrTimerInt"): xml_str += " " cmd_list.append("undo wtr") if self.tos_exp is not None: if self.state == "present" and str(self.tos_exp) != session.get("tosExp"): xml_str += " %s" % self.tos_exp cmd_list.append("tos-exp %s" % self.tos_exp) elif self.state == "absent" and str(self.tos_exp) == session.get("tosExp"): xml_str += " " cmd_list.append("undo tos-exp") if self.admin_down and session.get("adminDown", "false") == "false": xml_str += " true" cmd_list.append("shutdown") elif not self.admin_down and session.get("adminDown", "false") == "true": xml_str += " false" cmd_list.append("undo shutdown") if self.description: if self.state == "present" and self.description != session.get("description"): xml_str += "%s" % self.description cmd_list.append("description %s" % self.description) elif self.state == "absent" and self.description == session.get("description"): xml_str += "" cmd_list.append("undo description") if xml_str.endswith(""): # no config update return "" else: cmd_list.insert(0, cmd_session) self.updates_cmd.extend(cmd_list) return '' + xml_str\ + '' def netconf_load_config(self, xml_str): """load bfd config by netconf""" if not xml_str: return xml_cfg = """ %s """ % xml_str set_nc_config(self.min_rx_interval, xml_cfg) self.changed = True def check_params(self): """Check all input params""" # check session_name if not self.session_name: self.module.fail_json(msg="Error: Missing required arguments: session_name.") if self.session_name: if len(self.session_name) < 1 or len(self.session_name) > 15: self.module.fail_json(msg="Error: Session name is invalid.") # check local_discr if self.local_discr is not None: if self.local_discr < 1 or self.local_discr > 16384: self.module.fail_json(msg="Error: Session local_discr is not ranges from 1 to 16384.") # check remote_discr if self.remote_discr is not None: if self.remote_discr < 1 or self.remote_discr > 4294967295: self.module.fail_json(msg="Error: Session remote_discr is not ranges from 1 to 4294967295.") # check min_tx_interval if self.min_tx_interval is not None: if self.min_tx_interval < 50 or self.min_tx_interval > 1000: self.module.fail_json(msg="Error: Session min_tx_interval is not ranges from 50 to 1000.") # check min_rx_interval if self.min_rx_interval is not None: if self.min_rx_interval < 50 or self.min_rx_interval > 1000: self.module.fail_json(msg="Error: Session min_rx_interval is not ranges from 50 to 1000.") # check detect_multi if self.detect_multi is not None: if self.detect_multi < 3 or self.detect_multi > 50: self.module.fail_json(msg="Error: Session detect_multi is not ranges from 3 to 50.") # check wtr_interval if self.wtr_interval is not None: if self.wtr_interval < 1 or self.wtr_interval > 60: self.module.fail_json(msg="Error: Session wtr_interval is not ranges from 1 to 60.") # check tos_exp if self.tos_exp is not None: if self.tos_exp < 0 or self.tos_exp > 7: self.module.fail_json(msg="Error: Session tos_exp is not ranges from 0 to 7.") # check description if self.description: if len(self.description) < 1 or len(self.description) > 51: self.module.fail_json(msg="Error: Session description is invalid.") def get_proposed(self): """get proposed info""" # base config self.proposed["session_name"] = self.session_name self.proposed["local_discr"] = self.local_discr self.proposed["remote_discr"] = self.remote_discr self.proposed["min_tx_interval"] = self.min_tx_interval self.proposed["min_rx_interval"] = self.min_rx_interval self.proposed["detect_multi"] = self.detect_multi self.proposed["wtr_interval"] = self.wtr_interval self.proposed["tos_exp"] = self.tos_exp self.proposed["admin_down"] = self.admin_down self.proposed["description"] = self.description self.proposed["state"] = self.state def get_existing(self): """get existing info""" if not self.bfd_dict: return self.existing["session"] = self.bfd_dict.get("session") def get_end_state(self): """get end state info""" bfd_dict = self.get_bfd_dict() if not bfd_dict: return self.end_state["session"] = bfd_dict.get("session") if self.end_state == self.existing: self.changed = False def work(self): """worker""" self.check_params() self.bfd_dict = self.get_bfd_dict() self.get_existing() self.get_proposed() # deal present or absent xml_str = '' if self.session_name: xml_str += self.config_session() # update to device if xml_str: self.netconf_load_config(xml_str) self.changed = True 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(): """Module main""" argument_spec = dict( session_name=dict(required=True, type='str'), local_discr=dict(required=False, type='int'), remote_discr=dict(required=False, type='int'), min_tx_interval=dict(required=False, type='int'), min_rx_interval=dict(required=False, type='int'), detect_multi=dict(required=False, type='int'), wtr_interval=dict(required=False, type='int'), tos_exp=dict(required=False, type='int'), admin_down=dict(required=False, type='bool', default=False), description=dict(required=False, type='str'), state=dict(required=False, default='present', choices=['present', 'absent']) ) argument_spec.update(ce_argument_spec) module = BfdView(argument_spec) module.work() if __name__ == '__main__': main()