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