mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
798 lines
30 KiB
Python
798 lines
30 KiB
Python
|
#!/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/>.
|
||
|
#
|
||
|
|
||
|
from __future__ import (absolute_import, division, print_function)
|
||
|
__metaclass__ = type
|
||
|
|
||
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||
|
'status': ['preview'],
|
||
|
'supported_by': 'community'}
|
||
|
|
||
|
DOCUMENTATION = '''
|
||
|
---
|
||
|
module: ce_interface_ospf
|
||
|
short_description: Manages configuration of an OSPF interface instanceon HUAWEI CloudEngine switches.
|
||
|
description:
|
||
|
- Manages configuration of an OSPF interface instanceon HUAWEI CloudEngine switches.
|
||
|
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:
|
||
|
interface:
|
||
|
description:
|
||
|
- Full name of interface, i.e. 40GE1/0/10.
|
||
|
required: true
|
||
|
process_id:
|
||
|
description:
|
||
|
- Specifies a process ID.
|
||
|
The value is an integer ranging from 1 to 4294967295.
|
||
|
required: true
|
||
|
area:
|
||
|
description:
|
||
|
- Ospf area associated with this ospf process.
|
||
|
Valid values are a string, formatted as an IP address
|
||
|
(i.e. "0.0.0.0") or as an integer between 1 and 4294967295.
|
||
|
required: true
|
||
|
cost:
|
||
|
description:
|
||
|
- The cost associated with this interface.
|
||
|
Valid values are an integer in the range from 1 to 65535.
|
||
|
hello_interval:
|
||
|
description:
|
||
|
- Time between sending successive hello packets.
|
||
|
Valid values are an integer in the range from 1 to 65535.
|
||
|
dead_interval:
|
||
|
description:
|
||
|
- Time interval an ospf neighbor waits for a hello
|
||
|
packet before tearing down adjacencies. Valid values are an
|
||
|
integer in the range from 1 to 235926000.
|
||
|
silent_interface:
|
||
|
description:
|
||
|
- Setting to true will prevent this interface from receiving
|
||
|
HELLO packets. Valid values are 'true' and 'false'.
|
||
|
type: bool
|
||
|
default: 'no'
|
||
|
auth_mode:
|
||
|
description:
|
||
|
- Specifies the authentication type.
|
||
|
choices: ['none', 'null', 'hmac-sha256', 'md5', 'hmac-md5', 'simple']
|
||
|
auth_text_simple:
|
||
|
description:
|
||
|
- Specifies a password for simple authentication.
|
||
|
The value is a string of 1 to 8 characters.
|
||
|
auth_key_id:
|
||
|
description:
|
||
|
- Authentication key id when C(auth_mode) is 'hmac-sha256', 'md5' or 'hmac-md5.
|
||
|
Valid value is an integer is in the range from 1 to 255.
|
||
|
auth_text_md5:
|
||
|
description:
|
||
|
- Specifies a password for MD5, HMAC-MD5, or HMAC-SHA256 authentication.
|
||
|
The value is a string of 1 to 255 case-sensitive characters, spaces not supported.
|
||
|
state:
|
||
|
description:
|
||
|
- Determines whether the config should be present or not
|
||
|
on the device.
|
||
|
default: present
|
||
|
choices: ['present','absent']
|
||
|
'''
|
||
|
|
||
|
EXAMPLES = '''
|
||
|
- name: eth_trunk 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: Enables OSPF and sets the cost on an interface
|
||
|
ce_interface_ospf:
|
||
|
interface: 10GE1/0/30
|
||
|
process_id: 1
|
||
|
area: 100
|
||
|
cost: 100
|
||
|
provider: '{{ cli }}'
|
||
|
|
||
|
- name: Sets the dead interval of the OSPF neighbor
|
||
|
ce_interface_ospf:
|
||
|
interface: 10GE1/0/30
|
||
|
process_id: 1
|
||
|
area: 100
|
||
|
dead_interval: 100
|
||
|
provider: '{{ cli }}'
|
||
|
|
||
|
- name: Sets the interval for sending Hello packets on an interface
|
||
|
ce_interface_ospf:
|
||
|
interface: 10GE1/0/30
|
||
|
process_id: 1
|
||
|
area: 100
|
||
|
hello_interval: 2
|
||
|
provider: '{{ cli }}'
|
||
|
|
||
|
- name: Disables an interface from receiving and sending OSPF packets
|
||
|
ce_interface_ospf:
|
||
|
interface: 10GE1/0/30
|
||
|
process_id: 1
|
||
|
area: 100
|
||
|
silent_interface: true
|
||
|
provider: '{{ cli }}'
|
||
|
'''
|
||
|
|
||
|
RETURN = '''
|
||
|
proposed:
|
||
|
description: k/v pairs of parameters passed into module
|
||
|
returned: verbose mode
|
||
|
type: dict
|
||
|
sample: {"process_id": "1", "area": "0.0.0.100", "interface": "10GE1/0/30", "cost": "100"}
|
||
|
existing:
|
||
|
description: k/v pairs of existing configuration
|
||
|
returned: verbose mode
|
||
|
type: dict
|
||
|
sample: {"process_id": "1", "area": "0.0.0.100"}
|
||
|
end_state:
|
||
|
description: k/v pairs of configuration after module execution
|
||
|
returned: verbose mode
|
||
|
type: dict
|
||
|
sample: {"process_id": "1", "area": "0.0.0.100", "interface": "10GE1/0/30",
|
||
|
"cost": "100", "dead_interval": "40", "hello_interval": "10",
|
||
|
"silent_interface": "false", "auth_mode": "none"}
|
||
|
updates:
|
||
|
description: commands sent to the device
|
||
|
returned: always
|
||
|
type: list
|
||
|
sample: ["interface 10GE1/0/30",
|
||
|
"ospf enable 1 area 0.0.0.100",
|
||
|
"ospf cost 100"]
|
||
|
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_OSPF = """
|
||
|
<filter type="subtree">
|
||
|
<ospfv2 xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
|
||
|
<ospfv2comm>
|
||
|
<ospfSites>
|
||
|
<ospfSite>
|
||
|
<processId>%s</processId>
|
||
|
<routerId></routerId>
|
||
|
<vrfName></vrfName>
|
||
|
<areas>
|
||
|
<area>
|
||
|
<areaId>%s</areaId>
|
||
|
<interfaces>
|
||
|
<interface>
|
||
|
<ifName>%s</ifName>
|
||
|
<networkType></networkType>
|
||
|
<helloInterval></helloInterval>
|
||
|
<deadInterval></deadInterval>
|
||
|
<silentEnable></silentEnable>
|
||
|
<configCost></configCost>
|
||
|
<authenticationMode></authenticationMode>
|
||
|
<authTextSimple></authTextSimple>
|
||
|
<keyId></keyId>
|
||
|
<authTextMd5></authTextMd5>
|
||
|
</interface>
|
||
|
</interfaces>
|
||
|
</area>
|
||
|
</areas>
|
||
|
</ospfSite>
|
||
|
</ospfSites>
|
||
|
</ospfv2comm>
|
||
|
</ospfv2>
|
||
|
</filter>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_BUILD_PROCESS = """
|
||
|
<config>
|
||
|
<ospfv2 xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
|
||
|
<ospfv2comm>
|
||
|
<ospfSites>
|
||
|
<ospfSite>
|
||
|
<processId>%s</processId>
|
||
|
<areas>
|
||
|
<area>
|
||
|
<areaId>%s</areaId>
|
||
|
%s
|
||
|
</area>
|
||
|
</areas>
|
||
|
</ospfSite>
|
||
|
</ospfSites>
|
||
|
</ospfv2comm>
|
||
|
</ospfv2>
|
||
|
</config>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_BUILD_MERGE_INTF = """
|
||
|
<interfaces>
|
||
|
<interface operation="merge">
|
||
|
%s
|
||
|
</interface>
|
||
|
</interfaces>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_BUILD_DELETE_INTF = """
|
||
|
<interfaces>
|
||
|
<interface operation="delete">
|
||
|
%s
|
||
|
</interface>
|
||
|
</interfaces>
|
||
|
"""
|
||
|
CE_NC_XML_SET_IF_NAME = """
|
||
|
<ifName>%s</ifName>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_SET_HELLO = """
|
||
|
<helloInterval>%s</helloInterval>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_SET_DEAD = """
|
||
|
<deadInterval>%s</deadInterval>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_SET_SILENT = """
|
||
|
<silentEnable>%s</silentEnable>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_SET_COST = """
|
||
|
<configCost>%s</configCost>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_SET_AUTH_MODE = """
|
||
|
<authenticationMode>%s</authenticationMode>
|
||
|
"""
|
||
|
|
||
|
|
||
|
CE_NC_XML_SET_AUTH_TEXT_SIMPLE = """
|
||
|
<authTextSimple>%s</authTextSimple>
|
||
|
"""
|
||
|
|
||
|
CE_NC_XML_SET_AUTH_MD5 = """
|
||
|
<keyId>%s</keyId>
|
||
|
<authTextMd5>%s</authTextMd5>
|
||
|
"""
|
||
|
|
||
|
|
||
|
def get_interface_type(interface):
|
||
|
"""Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF..."""
|
||
|
|
||
|
if interface is None:
|
||
|
return None
|
||
|
|
||
|
if interface.upper().startswith('GE'):
|
||
|
return 'ge'
|
||
|
elif interface.upper().startswith('10GE'):
|
||
|
return '10ge'
|
||
|
elif interface.upper().startswith('25GE'):
|
||
|
return '25ge'
|
||
|
elif interface.upper().startswith('4X10GE'):
|
||
|
return '4x10ge'
|
||
|
elif interface.upper().startswith('40GE'):
|
||
|
return '40ge'
|
||
|
elif interface.upper().startswith('100GE'):
|
||
|
return '100ge'
|
||
|
elif interface.upper().startswith('VLANIF'):
|
||
|
return 'vlanif'
|
||
|
elif interface.upper().startswith('LOOPBACK'):
|
||
|
return 'loopback'
|
||
|
elif interface.upper().startswith('METH'):
|
||
|
return 'meth'
|
||
|
elif interface.upper().startswith('ETH-TRUNK'):
|
||
|
return 'eth-trunk'
|
||
|
elif interface.upper().startswith('VBDIF'):
|
||
|
return 'vbdif'
|
||
|
elif interface.upper().startswith('NVE'):
|
||
|
return 'nve'
|
||
|
elif interface.upper().startswith('TUNNEL'):
|
||
|
return 'tunnel'
|
||
|
elif interface.upper().startswith('ETHERNET'):
|
||
|
return 'ethernet'
|
||
|
elif interface.upper().startswith('FCOE-PORT'):
|
||
|
return 'fcoe-port'
|
||
|
elif interface.upper().startswith('FABRIC-PORT'):
|
||
|
return 'fabric-port'
|
||
|
elif interface.upper().startswith('STACK-PORT'):
|
||
|
return 'stack-port'
|
||
|
elif interface.upper().startswith('NULL'):
|
||
|
return 'null'
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
|
||
|
def is_valid_v4addr(addr):
|
||
|
"""check is ipv4 addr is valid"""
|
||
|
|
||
|
if not addr:
|
||
|
return False
|
||
|
|
||
|
if addr.find('.') != -1:
|
||
|
addr_list = addr.split('.')
|
||
|
if len(addr_list) != 4:
|
||
|
return False
|
||
|
for each_num in addr_list:
|
||
|
if not each_num.isdigit():
|
||
|
return False
|
||
|
if int(each_num) > 255:
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
|
||
|
class InterfaceOSPF(object):
|
||
|
"""
|
||
|
Manages configuration of an OSPF interface instance.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, argument_spec):
|
||
|
self.spec = argument_spec
|
||
|
self.module = None
|
||
|
self.init_module()
|
||
|
|
||
|
# module input info
|
||
|
self.interface = self.module.params['interface']
|
||
|
self.process_id = self.module.params['process_id']
|
||
|
self.area = self.module.params['area']
|
||
|
self.cost = self.module.params['cost']
|
||
|
self.hello_interval = self.module.params['hello_interval']
|
||
|
self.dead_interval = self.module.params['dead_interval']
|
||
|
self.silent_interface = self.module.params['silent_interface']
|
||
|
self.auth_mode = self.module.params['auth_mode']
|
||
|
self.auth_text_simple = self.module.params['auth_text_simple']
|
||
|
self.auth_key_id = self.module.params['auth_key_id']
|
||
|
self.auth_text_md5 = self.module.params['auth_text_md5']
|
||
|
self.state = self.module.params['state']
|
||
|
|
||
|
# ospf info
|
||
|
self.ospf_info = dict()
|
||
|
|
||
|
# 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 netconf_set_config(self, xml_str, xml_name):
|
||
|
"""netconf set config"""
|
||
|
|
||
|
rcv_xml = set_nc_config(self.module, xml_str)
|
||
|
if "<ok/>" not in rcv_xml:
|
||
|
self.module.fail_json(msg='Error: %s failed.' % xml_name)
|
||
|
|
||
|
def get_area_ip(self):
|
||
|
"""convert integer to ip address"""
|
||
|
|
||
|
if not self.area.isdigit():
|
||
|
return self.area
|
||
|
|
||
|
addr_int = ['0'] * 4
|
||
|
addr_int[0] = str(((int(self.area) & 0xFF000000) >> 24) & 0xFF)
|
||
|
addr_int[1] = str(((int(self.area) & 0x00FF0000) >> 16) & 0xFF)
|
||
|
addr_int[2] = str(((int(self.area) & 0x0000FF00) >> 8) & 0XFF)
|
||
|
addr_int[3] = str(int(self.area) & 0xFF)
|
||
|
|
||
|
return '.'.join(addr_int)
|
||
|
|
||
|
def get_ospf_dict(self):
|
||
|
""" get one ospf attributes dict."""
|
||
|
|
||
|
ospf_info = dict()
|
||
|
conf_str = CE_NC_GET_OSPF % (
|
||
|
self.process_id, self.get_area_ip(), self.interface)
|
||
|
rcv_xml = get_nc_config(self.module, conf_str)
|
||
|
|
||
|
if "<data/>" in rcv_xml:
|
||
|
return ospf_info
|
||
|
|
||
|
xml_str = rcv_xml.replace('\r', '').replace('\n', '').\
|
||
|
replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
|
||
|
replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
|
||
|
|
||
|
# get process base info
|
||
|
root = ElementTree.fromstring(xml_str)
|
||
|
ospfsite = root.find("ospfv2/ospfv2comm/ospfSites/ospfSite")
|
||
|
if not ospfsite:
|
||
|
self.module.fail_json(msg="Error: ospf process does not exist.")
|
||
|
|
||
|
for site in ospfsite:
|
||
|
if site.tag in ["processId", "routerId", "vrfName"]:
|
||
|
ospf_info[site.tag] = site.text
|
||
|
|
||
|
# get areas info
|
||
|
ospf_info["areaId"] = ""
|
||
|
areas = root.find(
|
||
|
"ospfv2/ospfv2comm/ospfSites/ospfSite/areas/area")
|
||
|
if areas:
|
||
|
for area in areas:
|
||
|
if area.tag == "areaId":
|
||
|
ospf_info["areaId"] = area.text
|
||
|
break
|
||
|
|
||
|
# get interface info
|
||
|
ospf_info["interface"] = dict()
|
||
|
intf = root.find(
|
||
|
"ospfv2/ospfv2comm/ospfSites/ospfSite/areas/area/interfaces/interface")
|
||
|
if intf:
|
||
|
for attr in intf:
|
||
|
if attr.tag in ["ifName", "networkType",
|
||
|
"helloInterval", "deadInterval",
|
||
|
"silentEnable", "configCost",
|
||
|
"authenticationMode", "authTextSimple",
|
||
|
"keyId", "authTextMd5"]:
|
||
|
ospf_info["interface"][attr.tag] = attr.text
|
||
|
|
||
|
return ospf_info
|
||
|
|
||
|
def set_ospf_interface(self):
|
||
|
"""set interface ospf enable, and set its ospf attributes"""
|
||
|
|
||
|
xml_intf = CE_NC_XML_SET_IF_NAME % self.interface
|
||
|
|
||
|
# ospf view
|
||
|
self.updates_cmd.append("ospf %s" % self.process_id)
|
||
|
self.updates_cmd.append("area %s" % self.get_area_ip())
|
||
|
if self.silent_interface:
|
||
|
xml_intf += CE_NC_XML_SET_SILENT % str(self.silent_interface).lower()
|
||
|
if self.silent_interface:
|
||
|
self.updates_cmd.append("silent-interface %s" % self.interface)
|
||
|
else:
|
||
|
self.updates_cmd.append("undo silent-interface %s" % self.interface)
|
||
|
|
||
|
# interface view
|
||
|
self.updates_cmd.append("interface %s" % self.interface)
|
||
|
self.updates_cmd.append("ospf enable %s area %s" % (
|
||
|
self.process_id, self.get_area_ip()))
|
||
|
if self.cost:
|
||
|
xml_intf += CE_NC_XML_SET_COST % self.cost
|
||
|
self.updates_cmd.append("ospf cost %s" % self.cost)
|
||
|
if self.hello_interval:
|
||
|
xml_intf += CE_NC_XML_SET_HELLO % self.hello_interval
|
||
|
self.updates_cmd.append("ospf timer hello %s" %
|
||
|
self.hello_interval)
|
||
|
if self.dead_interval:
|
||
|
xml_intf += CE_NC_XML_SET_DEAD % self.dead_interval
|
||
|
self.updates_cmd.append("ospf timer dead %s" % self.dead_interval)
|
||
|
if self.auth_mode:
|
||
|
xml_intf += CE_NC_XML_SET_AUTH_MODE % self.auth_mode
|
||
|
if self.auth_mode == "none":
|
||
|
self.updates_cmd.append("undo ospf authentication-mode")
|
||
|
else:
|
||
|
self.updates_cmd.append("ospf authentication-mode %s" % self.auth_mode)
|
||
|
if self.auth_mode == "simple" and self.auth_text_simple:
|
||
|
xml_intf += CE_NC_XML_SET_AUTH_TEXT_SIMPLE % self.auth_text_simple
|
||
|
self.updates_cmd.pop()
|
||
|
self.updates_cmd.append("ospf authentication-mode %s %s"
|
||
|
% (self.auth_mode, self.auth_text_simple))
|
||
|
elif self.auth_mode in ["hmac-sha256", "md5", "hmac-md5"] and self.auth_key_id:
|
||
|
xml_intf += CE_NC_XML_SET_AUTH_MD5 % (
|
||
|
self.auth_key_id, self.auth_text_md5)
|
||
|
self.updates_cmd.pop()
|
||
|
self.updates_cmd.append("ospf authentication-mode %s %s %s"
|
||
|
% (self.auth_mode, self.auth_key_id, self.auth_text_md5))
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id,
|
||
|
self.get_area_ip(),
|
||
|
(CE_NC_XML_BUILD_MERGE_INTF % xml_intf))
|
||
|
self.netconf_set_config(xml_str, "SET_INTERFACE_OSPF")
|
||
|
self.changed = True
|
||
|
|
||
|
def merge_ospf_interface(self):
|
||
|
"""merge interface ospf attributes"""
|
||
|
|
||
|
intf_dict = self.ospf_info["interface"]
|
||
|
|
||
|
# ospf view
|
||
|
xml_ospf = ""
|
||
|
if intf_dict.get("silentEnable") != str(self.silent_interface).lower():
|
||
|
xml_ospf += CE_NC_XML_SET_SILENT % str(self.silent_interface).lower()
|
||
|
self.updates_cmd.append("ospf %s" % self.process_id)
|
||
|
self.updates_cmd.append("area %s" % self.get_area_ip())
|
||
|
if self.silent_interface:
|
||
|
self.updates_cmd.append("silent-interface %s" % self.interface)
|
||
|
else:
|
||
|
self.updates_cmd.append("undo silent-interface %s" % self.interface)
|
||
|
|
||
|
# interface view
|
||
|
xml_intf = ""
|
||
|
self.updates_cmd.append("interface %s" % self.interface)
|
||
|
if self.cost and intf_dict.get("configCost") != self.cost:
|
||
|
xml_intf += CE_NC_XML_SET_COST % self.cost
|
||
|
self.updates_cmd.append("ospf cost %s" % self.cost)
|
||
|
if self.hello_interval and intf_dict.get("helloInterval") != self.hello_interval:
|
||
|
xml_intf += CE_NC_XML_SET_HELLO % self.hello_interval
|
||
|
self.updates_cmd.append("ospf timer hello %s" %
|
||
|
self.hello_interval)
|
||
|
if self.dead_interval and intf_dict.get("deadInterval") != self.dead_interval:
|
||
|
xml_intf += CE_NC_XML_SET_DEAD % self.dead_interval
|
||
|
self.updates_cmd.append("ospf timer dead %s" % self.dead_interval)
|
||
|
if self.auth_mode:
|
||
|
# NOTE: for security, authentication config will always be update
|
||
|
xml_intf += CE_NC_XML_SET_AUTH_MODE % self.auth_mode
|
||
|
if self.auth_mode == "none":
|
||
|
self.updates_cmd.append("undo ospf authentication-mode")
|
||
|
else:
|
||
|
self.updates_cmd.append("ospf authentication-mode %s" % self.auth_mode)
|
||
|
if self.auth_mode == "simple" and self.auth_text_simple:
|
||
|
xml_intf += CE_NC_XML_SET_AUTH_TEXT_SIMPLE % self.auth_text_simple
|
||
|
self.updates_cmd.pop()
|
||
|
self.updates_cmd.append("ospf authentication-mode %s %s"
|
||
|
% (self.auth_mode, self.auth_text_simple))
|
||
|
elif self.auth_mode in ["hmac-sha256", "md5", "hmac-md5"] and self.auth_key_id:
|
||
|
xml_intf += CE_NC_XML_SET_AUTH_MD5 % (
|
||
|
self.auth_key_id, self.auth_text_md5)
|
||
|
self.updates_cmd.pop()
|
||
|
self.updates_cmd.append("ospf authentication-mode %s %s %s"
|
||
|
% (self.auth_mode, self.auth_key_id, self.auth_text_md5))
|
||
|
else:
|
||
|
pass
|
||
|
if not xml_intf:
|
||
|
self.updates_cmd.pop() # remove command: interface
|
||
|
|
||
|
if not xml_ospf and not xml_intf:
|
||
|
return
|
||
|
|
||
|
xml_sum = CE_NC_XML_SET_IF_NAME % self.interface
|
||
|
xml_sum += xml_ospf + xml_intf
|
||
|
xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id,
|
||
|
self.get_area_ip(),
|
||
|
(CE_NC_XML_BUILD_MERGE_INTF % xml_sum))
|
||
|
self.netconf_set_config(xml_str, "MERGE_INTERFACE_OSPF")
|
||
|
self.changed = True
|
||
|
|
||
|
def unset_ospf_interface(self):
|
||
|
"""set interface ospf disable, and all its ospf attributes will be removed"""
|
||
|
|
||
|
intf_dict = self.ospf_info["interface"]
|
||
|
xml_sum = ""
|
||
|
xml_intf = CE_NC_XML_SET_IF_NAME % self.interface
|
||
|
if intf_dict.get("silentEnable") == "true":
|
||
|
xml_sum += CE_NC_XML_BUILD_MERGE_INTF % (
|
||
|
xml_intf + (CE_NC_XML_SET_SILENT % "false"))
|
||
|
self.updates_cmd.append("ospf %s" % self.process_id)
|
||
|
self.updates_cmd.append("area %s" % self.get_area_ip())
|
||
|
self.updates_cmd.append(
|
||
|
"undo silent-interface %s" % self.interface)
|
||
|
|
||
|
xml_sum += CE_NC_XML_BUILD_DELETE_INTF % xml_intf
|
||
|
xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id,
|
||
|
self.get_area_ip(),
|
||
|
xml_sum)
|
||
|
self.netconf_set_config(xml_str, "DELETE_INTERFACE_OSPF")
|
||
|
self.updates_cmd.append("undo ospf cost")
|
||
|
self.updates_cmd.append("undo ospf timer hello")
|
||
|
self.updates_cmd.append("undo ospf timer dead")
|
||
|
self.updates_cmd.append("undo ospf authentication-mode")
|
||
|
self.updates_cmd.append("undo ospf enable %s area %s" % (
|
||
|
self.process_id, self.get_area_ip()))
|
||
|
self.changed = True
|
||
|
|
||
|
def check_params(self):
|
||
|
"""Check all input params"""
|
||
|
|
||
|
self.interface = self.interface.replace(" ", "").upper()
|
||
|
|
||
|
# interface check
|
||
|
if not get_interface_type(self.interface):
|
||
|
self.module.fail_json(msg="Error: interface is invalid.")
|
||
|
|
||
|
# process_id check
|
||
|
if not self.process_id.isdigit():
|
||
|
self.module.fail_json(msg="Error: process_id is not digit.")
|
||
|
if int(self.process_id) < 1 or int(self.process_id) > 4294967295:
|
||
|
self.module.fail_json(msg="Error: process_id must be an integer between 1 and 4294967295.")
|
||
|
|
||
|
# area check
|
||
|
if self.area.isdigit():
|
||
|
if int(self.area) < 0 or int(self.area) > 4294967295:
|
||
|
self.module.fail_json(msg="Error: area id (Integer) must be between 0 and 4294967295.")
|
||
|
else:
|
||
|
if not is_valid_v4addr(self.area):
|
||
|
self.module.fail_json(msg="Error: area id is invalid.")
|
||
|
|
||
|
# area authentication check
|
||
|
if self.state == "present":
|
||
|
if self.auth_mode:
|
||
|
if self.auth_mode == "simple":
|
||
|
if self.auth_text_simple and len(self.auth_text_simple) > 8:
|
||
|
self.module.fail_json(
|
||
|
msg="Error: auth_text_simple is not in the range from 1 to 8.")
|
||
|
if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]:
|
||
|
if self.auth_key_id and not self.auth_text_md5:
|
||
|
self.module.fail_json(
|
||
|
msg='Error: auth_key_id and auth_text_md5 should be set at the same time.')
|
||
|
if not self.auth_key_id and self.auth_text_md5:
|
||
|
self.module.fail_json(
|
||
|
msg='Error: auth_key_id and auth_text_md5 should be set at the same time.')
|
||
|
if self.auth_key_id:
|
||
|
if not self.auth_key_id.isdigit():
|
||
|
self.module.fail_json(
|
||
|
msg="Error: auth_key_id is not digit.")
|
||
|
if int(self.auth_key_id) < 1 or int(self.auth_key_id) > 255:
|
||
|
self.module.fail_json(
|
||
|
msg="Error: auth_key_id is not in the range from 1 to 255.")
|
||
|
if self.auth_text_md5 and len(self.auth_text_md5) > 255:
|
||
|
self.module.fail_json(
|
||
|
msg="Error: auth_text_md5 is not in the range from 1 to 255.")
|
||
|
# cost check
|
||
|
if self.cost:
|
||
|
if not self.cost.isdigit():
|
||
|
self.module.fail_json(msg="Error: cost is not digit.")
|
||
|
if int(self.cost) < 1 or int(self.cost) > 65535:
|
||
|
self.module.fail_json(
|
||
|
msg="Error: cost is not in the range from 1 to 65535")
|
||
|
|
||
|
# hello_interval check
|
||
|
if self.hello_interval:
|
||
|
if not self.hello_interval.isdigit():
|
||
|
self.module.fail_json(
|
||
|
msg="Error: hello_interval is not digit.")
|
||
|
if int(self.hello_interval) < 1 or int(self.hello_interval) > 65535:
|
||
|
self.module.fail_json(
|
||
|
msg="Error: hello_interval is not in the range from 1 to 65535")
|
||
|
|
||
|
# dead_interval check
|
||
|
if self.dead_interval:
|
||
|
if not self.dead_interval.isdigit():
|
||
|
self.module.fail_json(msg="Error: dead_interval is not digit.")
|
||
|
if int(self.dead_interval) < 1 or int(self.dead_interval) > 235926000:
|
||
|
self.module.fail_json(
|
||
|
msg="Error: dead_interval is not in the range from 1 to 235926000")
|
||
|
|
||
|
def get_proposed(self):
|
||
|
"""get proposed info"""
|
||
|
|
||
|
self.proposed["interface"] = self.interface
|
||
|
self.proposed["process_id"] = self.process_id
|
||
|
self.proposed["area"] = self.get_area_ip()
|
||
|
self.proposed["cost"] = self.cost
|
||
|
self.proposed["hello_interval"] = self.hello_interval
|
||
|
self.proposed["dead_interval"] = self.dead_interval
|
||
|
self.proposed["silent_interface"] = self.silent_interface
|
||
|
if self.auth_mode:
|
||
|
self.proposed["auth_mode"] = self.auth_mode
|
||
|
if self.auth_mode == "simple":
|
||
|
self.proposed["auth_text_simple"] = self.auth_text_simple
|
||
|
if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]:
|
||
|
self.proposed["auth_key_id"] = self.auth_key_id
|
||
|
self.proposed["auth_text_md5"] = self.auth_text_md5
|
||
|
self.proposed["state"] = self.state
|
||
|
|
||
|
def get_existing(self):
|
||
|
"""get existing info"""
|
||
|
|
||
|
if not self.ospf_info:
|
||
|
return
|
||
|
|
||
|
if self.ospf_info["interface"]:
|
||
|
self.existing["interface"] = self.interface
|
||
|
self.existing["cost"] = self.ospf_info["interface"].get("configCost")
|
||
|
self.existing["hello_interval"] = self.ospf_info["interface"].get("helloInterval")
|
||
|
self.existing["dead_interval"] = self.ospf_info["interface"].get("deadInterval")
|
||
|
self.existing["silent_interface"] = self.ospf_info["interface"].get("silentEnable")
|
||
|
self.existing["auth_mode"] = self.ospf_info["interface"].get("authenticationMode")
|
||
|
self.existing["auth_text_simple"] = self.ospf_info["interface"].get("authTextSimple")
|
||
|
self.existing["auth_key_id"] = self.ospf_info["interface"].get("keyId")
|
||
|
self.existing["auth_text_md5"] = self.ospf_info["interface"].get("authTextMd5")
|
||
|
self.existing["process_id"] = self.ospf_info["processId"]
|
||
|
self.existing["area"] = self.ospf_info["areaId"]
|
||
|
|
||
|
def get_end_state(self):
|
||
|
"""get end state info"""
|
||
|
|
||
|
ospf_info = self.get_ospf_dict()
|
||
|
if not ospf_info:
|
||
|
return
|
||
|
|
||
|
if ospf_info["interface"]:
|
||
|
self.end_state["interface"] = self.interface
|
||
|
self.end_state["cost"] = ospf_info["interface"].get("configCost")
|
||
|
self.end_state["hello_interval"] = ospf_info["interface"].get("helloInterval")
|
||
|
self.end_state["dead_interval"] = ospf_info["interface"].get("deadInterval")
|
||
|
self.end_state["silent_interface"] = ospf_info["interface"].get("silentEnable")
|
||
|
self.end_state["auth_mode"] = ospf_info["interface"].get("authenticationMode")
|
||
|
self.end_state["auth_text_simple"] = ospf_info["interface"].get("authTextSimple")
|
||
|
self.end_state["auth_key_id"] = ospf_info["interface"].get("keyId")
|
||
|
self.end_state["auth_text_md5"] = ospf_info["interface"].get("authTextMd5")
|
||
|
self.end_state["process_id"] = ospf_info["processId"]
|
||
|
self.end_state["area"] = ospf_info["areaId"]
|
||
|
|
||
|
def work(self):
|
||
|
"""worker"""
|
||
|
|
||
|
self.check_params()
|
||
|
self.ospf_info = self.get_ospf_dict()
|
||
|
self.get_existing()
|
||
|
self.get_proposed()
|
||
|
|
||
|
# deal present or absent
|
||
|
if self.state == "present":
|
||
|
if not self.ospf_info or not self.ospf_info["interface"]:
|
||
|
# create ospf area and set interface config
|
||
|
self.set_ospf_interface()
|
||
|
else:
|
||
|
# merge interface ospf area config
|
||
|
self.merge_ospf_interface()
|
||
|
else:
|
||
|
if self.ospf_info and self.ospf_info["interface"]:
|
||
|
# delete interface ospf area config
|
||
|
self.unset_ospf_interface()
|
||
|
|
||
|
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(
|
||
|
interface=dict(required=True, type='str'),
|
||
|
process_id=dict(required=True, type='str'),
|
||
|
area=dict(required=True, type='str'),
|
||
|
cost=dict(required=False, type='str'),
|
||
|
hello_interval=dict(required=False, type='str'),
|
||
|
dead_interval=dict(required=False, type='str'),
|
||
|
silent_interface=dict(required=False, default=False, type='bool'),
|
||
|
auth_mode=dict(required=False,
|
||
|
choices=['none', 'null', 'hmac-sha256', 'md5', 'hmac-md5', 'simple'], type='str'),
|
||
|
auth_text_simple=dict(required=False, type='str', no_log=True),
|
||
|
auth_key_id=dict(required=False, type='str'),
|
||
|
auth_text_md5=dict(required=False, type='str', no_log=True),
|
||
|
state=dict(required=False, default='present',
|
||
|
choices=['present', 'absent'])
|
||
|
)
|
||
|
|
||
|
argument_spec.update(ce_argument_spec)
|
||
|
module = InterfaceOSPF(argument_spec)
|
||
|
module.work()
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|